"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var moves_exports = {};
__export(moves_exports, {
  Moves: () => Moves
});
module.exports = __toCommonJS(moves_exports);
const Moves = {
  "10000000voltthunderbolt": {
    num: 719,
    accuracy: true,
    basePower: 195,
    category: "Special",
    isNonstandard: "Past",
    name: "10,000,000 Volt Thunderbolt",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "pikashuniumz",
    critRatio: 3,
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  absorb: {
    num: 71,
    accuracy: 100,
    basePower: 20,
    category: "Special",
    name: "Absorb",
    pp: 25,
    priority: 0,
    flags: { protect: 1, mirror: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Clever"
  },
  accelerock: {
    num: 709,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Accelerock",
    pp: 20,
    priority: 1,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Cool"
  },
  acid: {
    num: 51,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Acid",
    pp: 30,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spd: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Poison",
    contestType: "Clever"
  },
  acidarmor: {
    num: 151,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Acid Armor",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 2
    },
    secondary: null,
    target: "self",
    type: "Poison",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Tough"
  },
  aciddownpour: {
    num: 628,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Acid Downpour",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "poisoniumz",
    secondary: null,
    target: "normal",
    type: "Poison",
    contestType: "Cool"
  },
  acidspray: {
    num: 491,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Acid Spray",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spd: -2
      }
    },
    target: "normal",
    type: "Poison",
    contestType: "Beautiful"
  },
  acrobatics: {
    num: 512,
    accuracy: 100,
    basePower: 55,
    basePowerCallback(pokemon, target, move) {
      if (!pokemon.item) {
        this.debug("BP doubled for no item");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    name: "Acrobatics",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, distance: 1, metronome: 1 },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  acupressure: {
    num: 367,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Acupressure",
    pp: 30,
    priority: 0,
    flags: { metronome: 1 },
    onHit(target) {
      const stats = [];
      let stat;
      for (stat in target.boosts) {
        if (target.boosts[stat] < 6) {
          stats.push(stat);
        }
      }
      if (stats.length) {
        const randomStat = this.sample(stats);
        const boost = {};
        boost[randomStat] = 2;
        this.boost(boost);
      } else {
        return false;
      }
    },
    secondary: null,
    target: "adjacentAllyOrSelf",
    type: "Normal",
    zMove: { effect: "crit2" },
    contestType: "Tough"
  },
  aerialace: {
    num: 332,
    accuracy: true,
    basePower: 60,
    category: "Physical",
    name: "Aerial Ace",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, distance: 1, metronome: 1, slicing: 1 },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  aeroblast: {
    num: 177,
    accuracy: 95,
    basePower: 100,
    category: "Special",
    name: "Aeroblast",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, metronome: 1, wind: 1 },
    critRatio: 2,
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  afteryou: {
    num: 495,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "After You",
    pp: 15,
    priority: 0,
    flags: { bypasssub: 1, allyanim: 1 },
    onHit(target) {
      if (this.activePerHalf === 1)
        return false;
      const action = this.queue.willMove(target);
      if (action) {
        this.queue.prioritizeAction(action);
        this.add("-activate", target, "move: After You");
      } else {
        return false;
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Cute"
  },
  agility: {
    num: 97,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Agility",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      spe: 2
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cool"
  },
  aircutter: {
    num: 314,
    accuracy: 95,
    basePower: 60,
    category: "Special",
    name: "Air Cutter",
    pp: 25,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, slicing: 1, wind: 1 },
    critRatio: 2,
    secondary: null,
    target: "allAdjacentFoes",
    type: "Flying",
    contestType: "Cool"
  },
  airslash: {
    num: 403,
    accuracy: 95,
    basePower: 75,
    category: "Special",
    name: "Air Slash",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, metronome: 1, slicing: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  alloutpummeling: {
    num: 624,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "All-Out Pummeling",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "fightiniumz",
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  alluringvoice: {
    num: 914,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Alluring Voice",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: {
      chance: 100,
      onHit(target, source, move) {
        if (target?.statsRaisedThisTurn) {
          target.addVolatile("confusion", source, move);
        }
      }
    },
    target: "normal",
    type: "Fairy"
  },
  allyswitch: {
    num: 502,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Ally Switch",
    pp: 15,
    priority: 2,
    flags: { metronome: 1 },
    onPrepareHit(pokemon) {
      return pokemon.addVolatile("allyswitch");
    },
    onHit(pokemon) {
      let success = true;
      if (this.format.gameType !== "doubles" && this.format.gameType !== "triples")
        success = false;
      if (pokemon.side.active.length === 3 && pokemon.position === 1)
        success = false;
      const newPosition = pokemon.position === 0 ? pokemon.side.active.length - 1 : 0;
      if (!pokemon.side.active[newPosition])
        success = false;
      if (pokemon.side.active[newPosition].fainted)
        success = false;
      if (!success) {
        this.add("-fail", pokemon, "move: Ally Switch");
        this.attrLastMove("[still]");
        return this.NOT_FAIL;
      }
      this.swapPosition(pokemon, newPosition, "[from] move: Ally Switch");
    },
    condition: {
      duration: 2,
      counterMax: 729,
      onStart() {
        this.effectState.counter = 3;
      },
      onRestart(pokemon) {
        const counter = this.effectState.counter || 1;
        this.debug("Ally Switch success chance: " + Math.round(100 / counter) + "%");
        const success = this.randomChance(1, counter);
        if (!success) {
          delete pokemon.volatiles["allyswitch"];
          return false;
        }
        if (this.effectState.counter < this.effect.counterMax) {
          this.effectState.counter *= 3;
        }
        this.effectState.duration = 2;
      }
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { boost: { spe: 2 } },
    contestType: "Clever"
  },
  amnesia: {
    num: 133,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Amnesia",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      spd: 2
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  anchorshot: {
    num: 677,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    isNonstandard: "Past",
    name: "Anchor Shot",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      onHit(target, source, move) {
        if (source.isActive)
          target.addVolatile("trapped", source, move, "trapper");
      }
    },
    target: "normal",
    type: "Steel",
    contestType: "Tough"
  },
  ancientpower: {
    num: 246,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    name: "Ancient Power",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      self: {
        boosts: {
          atk: 1,
          def: 1,
          spa: 1,
          spd: 1,
          spe: 1
        }
      }
    },
    target: "normal",
    type: "Rock",
    contestType: "Tough"
  },
  appleacid: {
    num: 787,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Apple Acid",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Grass"
  },
  aquacutter: {
    num: 895,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Aqua Cutter",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  aquajet: {
    num: 453,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Aqua Jet",
    pp: 20,
    priority: 1,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  aquaring: {
    num: 392,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Aqua Ring",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    volatileStatus: "aquaring",
    condition: {
      onStart(pokemon) {
        this.add("-start", pokemon, "Aqua Ring");
      },
      onResidualOrder: 6,
      onResidual(pokemon) {
        this.heal(pokemon.baseMaxhp / 16);
      }
    },
    secondary: null,
    target: "self",
    type: "Water",
    zMove: { boost: { def: 1 } },
    contestType: "Beautiful"
  },
  aquastep: {
    num: 872,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Aqua Step",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, dance: 1, metronome: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          spe: 1
        }
      }
    },
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  aquatail: {
    num: 401,
    accuracy: 90,
    basePower: 90,
    category: "Physical",
    name: "Aqua Tail",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  armorcannon: {
    num: 890,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    name: "Armor Cannon",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    self: {
      boosts: {
        def: -1,
        spd: -1
      }
    },
    secondary: null,
    target: "normal",
    type: "Fire"
  },
  armthrust: {
    num: 292,
    accuracy: 100,
    basePower: 15,
    category: "Physical",
    name: "Arm Thrust",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  aromatherapy: {
    num: 312,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Aromatherapy",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, distance: 1, metronome: 1 },
    onHit(target, source, move) {
      this.add("-activate", source, "move: Aromatherapy");
      let success = false;
      const allies = [...target.side.pokemon, ...target.side.allySide?.pokemon || []];
      for (const ally of allies) {
        if (ally !== source && (ally.hasAbility("sapsipper") || ally.volatiles["substitute"] && !move.infiltrates)) {
          continue;
        }
        if (ally.cureStatus())
          success = true;
      }
      return success;
    },
    target: "allyTeam",
    type: "Grass",
    zMove: { effect: "heal" },
    contestType: "Clever"
  },
  aromaticmist: {
    num: 597,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Aromatic Mist",
    pp: 20,
    priority: 0,
    flags: { bypasssub: 1, metronome: 1 },
    boosts: {
      spd: 1
    },
    secondary: null,
    target: "adjacentAlly",
    type: "Fairy",
    zMove: { boost: { spd: 2 } },
    contestType: "Beautiful"
  },
  assist: {
    num: 274,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Assist",
    pp: 20,
    priority: 0,
    flags: { failencore: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    onHit(target) {
      const moves = [];
      for (const pokemon of target.side.pokemon) {
        if (pokemon === target)
          continue;
        for (const moveSlot of pokemon.moveSlots) {
          const moveid = moveSlot.id;
          const move = this.dex.moves.get(moveid);
          if (move.flags["noassist"] || move.isZ || move.isMax) {
            continue;
          }
          moves.push(moveid);
        }
      }
      let randomMove = "";
      if (moves.length)
        randomMove = this.sample(moves);
      if (!randomMove) {
        return false;
      }
      this.actions.useMove(randomMove, target);
    },
    secondary: null,
    target: "self",
    type: "Normal",
    contestType: "Cute"
  },
  assurance: {
    num: 372,
    accuracy: 100,
    basePower: 60,
    basePowerCallback(pokemon, target, move) {
      if (target.hurtThisTurn) {
        this.debug("BP doubled on damaged target");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    name: "Assurance",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  astonish: {
    num: 310,
    accuracy: 100,
    basePower: 30,
    category: "Physical",
    name: "Astonish",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Ghost",
    contestType: "Cute"
  },
  astralbarrage: {
    num: 825,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    name: "Astral Barrage",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Ghost"
  },
  attackorder: {
    num: 454,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Attack Order",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Clever"
  },
  attract: {
    num: 213,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Attract",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    volatileStatus: "attract",
    condition: {
      noCopy: true,
      // doesn't get copied by Baton Pass
      onStart(pokemon, source, effect) {
        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.name === "Cute Charm") {
          this.add("-start", pokemon, "Attract", "[from] ability: Cute Charm", "[of] " + source);
        } else if (effect.name === "Destiny Knot") {
          this.add("-start", pokemon, "Attract", "[from] item: Destiny Knot", "[of] " + source);
        } else {
          this.add("-start", pokemon, "Attract");
        }
      },
      onUpdate(pokemon) {
        if (this.effectState.source && !this.effectState.source.isActive && pokemon.volatiles["attract"]) {
          this.debug("Removing Attract volatile on " + pokemon);
          pokemon.removeVolatile("attract");
        }
      },
      onBeforeMovePriority: 2,
      onBeforeMove(pokemon, target, move) {
        this.add("-activate", pokemon, "move: Attract", "[of] " + this.effectState.source);
        if (this.randomChance(1, 2)) {
          this.add("cant", pokemon, "Attract");
          return false;
        }
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Attract", "[silent]");
      }
    },
    onTryImmunity(target, source) {
      return target.gender === "M" && source.gender === "F" || target.gender === "F" && source.gender === "M";
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  aurasphere: {
    num: 396,
    accuracy: true,
    basePower: 80,
    category: "Special",
    name: "Aura Sphere",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, metronome: 1, bullet: 1, pulse: 1 },
    secondary: null,
    target: "any",
    type: "Fighting",
    contestType: "Beautiful"
  },
  aurawheel: {
    num: 783,
    accuracy: 100,
    basePower: 110,
    category: "Physical",
    name: "Aura Wheel",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          spe: 1
        }
      }
    },
    onTry(source) {
      if (source.species.baseSpecies === "Morpeko") {
        return;
      }
      this.attrLastMove("[still]");
      this.add("-fail", source, "move: Aura Wheel");
      this.hint("Only a Pokemon whose form is Morpeko or Morpeko-Hangry can use this move.");
      return null;
    },
    onModifyType(move, pokemon) {
      if (pokemon.species.name === "Morpeko-Hangry") {
        move.type = "Dark";
      } else {
        move.type = "Electric";
      }
    },
    target: "normal",
    type: "Electric"
  },
  aurorabeam: {
    num: 62,
    accuracy: 100,
    basePower: 65,
    category: "Special",
    name: "Aurora Beam",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        atk: -1
      }
    },
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  auroraveil: {
    num: 694,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Aurora Veil",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    sideCondition: "auroraveil",
    onTry() {
      return this.field.isWeather(["hail", "snow"]);
    },
    condition: {
      duration: 5,
      durationCallback(target, source, effect) {
        if (source?.hasItem("lightclay")) {
          return 8;
        }
        return 5;
      },
      onAnyModifyDamage(damage, source, target, move) {
        if (target !== source && this.effectState.target.hasAlly(target)) {
          if (target.side.getSideCondition("reflect") && this.getCategory(move) === "Physical" || target.side.getSideCondition("lightscreen") && this.getCategory(move) === "Special") {
            return;
          }
          if (!target.getMoveHitData(move).crit && !move.infiltrates) {
            this.debug("Aurora Veil weaken");
            if (this.activePerHalf > 1)
              return this.chainModify([2732, 4096]);
            return this.chainModify(0.5);
          }
        }
      },
      onSideStart(side) {
        this.add("-sidestart", side, "move: Aurora Veil");
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 10,
      onSideEnd(side) {
        this.add("-sideend", side, "move: Aurora Veil");
      }
    },
    secondary: null,
    target: "allySide",
    type: "Ice",
    zMove: { boost: { spe: 1 } },
    contestType: "Beautiful"
  },
  autotomize: {
    num: 475,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Autotomize",
    pp: 15,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onTryHit(pokemon) {
      const hasContrary = pokemon.hasAbility("contrary");
      if (!hasContrary && pokemon.boosts.spe === 6 || hasContrary && pokemon.boosts.spe === -6) {
        return false;
      }
    },
    boosts: {
      spe: 2
    },
    onHit(pokemon) {
      if (pokemon.weighthg > 1) {
        pokemon.weighthg = Math.max(1, pokemon.weighthg - 1e3);
        this.add("-start", pokemon, "Autotomize");
      }
    },
    secondary: null,
    target: "self",
    type: "Steel",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  avalanche: {
    num: 419,
    accuracy: 100,
    basePower: 60,
    basePowerCallback(pokemon, target, move) {
      const damagedByTarget = pokemon.attackedBy.some(
        (p) => p.source === target && p.damage > 0 && p.thisTurn
      );
      if (damagedByTarget) {
        this.debug("BP doubled for getting hit by " + target);
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    name: "Avalanche",
    pp: 10,
    priority: -4,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  axekick: {
    num: 853,
    accuracy: 90,
    basePower: 120,
    category: "Physical",
    name: "Axe Kick",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    hasCrashDamage: true,
    onMoveFail(target, source, move) {
      this.damage(source.baseMaxhp / 2, source, source, this.dex.conditions.get("High Jump Kick"));
    },
    secondary: {
      chance: 30,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Fighting"
  },
  babydolleyes: {
    num: 608,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Baby-Doll Eyes",
    pp: 30,
    priority: 1,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    boosts: {
      atk: -1
    },
    secondary: null,
    target: "normal",
    type: "Fairy",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  baddybad: {
    num: 737,
    accuracy: 95,
    basePower: 80,
    category: "Special",
    isNonstandard: "LGPE",
    name: "Baddy Bad",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    self: {
      sideCondition: "reflect"
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  banefulbunker: {
    num: 661,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Baneful Bunker",
    pp: 10,
    priority: 4,
    flags: { noassist: 1, failcopycat: 1 },
    stallingMove: true,
    volatileStatus: "banefulbunker",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    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 (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          if (move.isZ || move.isMax)
            target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        if (move.smartTarget) {
          move.smartTarget = false;
        } else {
          this.add("-activate", target, "move: Protect");
        }
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        if (this.checkMoveMakesContact(move, source, target)) {
          source.trySetStatus("psn", target);
        }
        return this.NOT_FAIL;
      },
      onHit(target, source, move) {
        if (move.isZOrMaxPowered && this.checkMoveMakesContact(move, source, target)) {
          source.trySetStatus("psn", target);
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Poison",
    zMove: { boost: { def: 1 } },
    contestType: "Tough"
  },
  barbbarrage: {
    num: 839,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Barb Barrage",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, pokemon, target) {
      if (target.status === "psn" || target.status === "tox") {
        return this.chainModify(2);
      }
    },
    secondary: {
      chance: 50,
      status: "psn"
    },
    target: "normal",
    type: "Poison"
  },
  barrage: {
    num: 140,
    accuracy: 85,
    basePower: 15,
    category: "Physical",
    isNonstandard: "Past",
    name: "Barrage",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  barrier: {
    num: 112,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Barrier",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 2
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cool"
  },
  batonpass: {
    num: 226,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Baton Pass",
    pp: 40,
    priority: 0,
    flags: { metronome: 1 },
    onHit(target) {
      if (!this.canSwitch(target.side) || target.volatiles["commanded"]) {
        this.attrLastMove("[still]");
        this.add("-fail", target);
        return this.NOT_FAIL;
      }
    },
    self: {
      onHit(source) {
        source.skipBeforeSwitchOutEventFlag = true;
      }
    },
    selfSwitch: "copyvolatile",
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  beakblast: {
    num: 690,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Beak Blast",
    pp: 15,
    priority: -3,
    flags: { protect: 1, failmefirst: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failinstruct: 1, bullet: 1 },
    priorityChargeCallback(pokemon) {
      pokemon.addVolatile("beakblast");
    },
    condition: {
      duration: 1,
      onStart(pokemon) {
        this.add("-singleturn", pokemon, "move: Beak Blast");
      },
      onHit(target, source, move) {
        if (this.checkMoveMakesContact(move, source, target)) {
          source.trySetStatus("brn", target);
        }
      }
    },
    // FIXME: onMoveAborted(pokemon) {pokemon.removeVolatile('beakblast')},
    onAfterMove(pokemon) {
      pokemon.removeVolatile("beakblast");
    },
    secondary: null,
    target: "normal",
    type: "Flying",
    contestType: "Tough"
  },
  beatup: {
    num: 251,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target, move) {
      const currentSpecies = move.allies.shift().species;
      const bp = 5 + Math.floor(currentSpecies.baseStats.atk / 10);
      this.debug("BP for " + currentSpecies.name + " hit: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Beat Up",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onModifyMove(move, pokemon) {
      move.allies = pokemon.side.pokemon.filter((ally) => ally === pokemon || !ally.fainted && !ally.status);
      move.multihit = move.allies.length;
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  behemothbash: {
    num: 782,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Behemoth Bash",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, failcopycat: 1, failmimic: 1 },
    secondary: null,
    target: "normal",
    type: "Steel"
  },
  behemothblade: {
    num: 781,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Behemoth Blade",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, failcopycat: 1, failmimic: 1, slicing: 1 },
    secondary: null,
    target: "normal",
    type: "Steel"
  },
  belch: {
    num: 562,
    accuracy: 90,
    basePower: 120,
    category: "Special",
    name: "Belch",
    pp: 10,
    priority: 0,
    flags: { protect: 1, failmefirst: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    onDisableMove(pokemon) {
      if (!pokemon.ateBerry)
        pokemon.disableMove("belch");
    },
    secondary: null,
    target: "normal",
    type: "Poison",
    contestType: "Tough"
  },
  bellydrum: {
    num: 187,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Belly Drum",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onHit(target) {
      if (target.hp <= target.maxhp / 2 || target.boosts.atk >= 6 || target.maxhp === 1) {
        return false;
      }
      this.directDamage(target.maxhp / 2);
      this.boost({ atk: 12 }, target);
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "heal" },
    contestType: "Cute"
  },
  bestow: {
    num: 516,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Bestow",
    pp: 15,
    priority: 0,
    flags: { mirror: 1, bypasssub: 1, allyanim: 1, noassist: 1, failcopycat: 1 },
    onHit(target, source, move) {
      if (target.item) {
        return false;
      }
      const myItem = source.takeItem();
      if (!myItem)
        return false;
      if (!this.singleEvent("TakeItem", myItem, source.itemState, target, source, move, myItem) || !target.setItem(myItem)) {
        source.item = myItem.id;
        return false;
      }
      this.add("-item", target, myItem.name, "[from] move: Bestow", "[of] " + source);
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 2 } },
    contestType: "Cute"
  },
  bide: {
    num: 117,
    accuracy: true,
    basePower: 0,
    category: "Physical",
    isNonstandard: "Past",
    name: "Bide",
    pp: 10,
    priority: 1,
    flags: { contact: 1, protect: 1, metronome: 1, nosleeptalk: 1, failinstruct: 1 },
    volatileStatus: "bide",
    ignoreImmunity: true,
    beforeMoveCallback(pokemon) {
      if (pokemon.volatiles["bide"])
        return true;
    },
    condition: {
      duration: 3,
      onLockMove: "bide",
      onStart(pokemon) {
        this.effectState.totalDamage = 0;
        this.add("-start", pokemon, "move: Bide");
      },
      onDamagePriority: -101,
      onDamage(damage, target, source, move) {
        if (!move || move.effectType !== "Move" || !source)
          return;
        this.effectState.totalDamage += damage;
        this.effectState.lastDamageSource = source;
      },
      onBeforeMove(pokemon, target, move) {
        if (this.effectState.duration === 1) {
          this.add("-end", pokemon, "move: Bide");
          target = this.effectState.lastDamageSource;
          if (!target || !this.effectState.totalDamage) {
            this.attrLastMove("[still]");
            this.add("-fail", pokemon);
            return false;
          }
          if (!target.isActive) {
            const possibleTarget = this.getRandomTarget(pokemon, this.dex.moves.get("pound"));
            if (!possibleTarget) {
              this.add("-miss", pokemon);
              return false;
            }
            target = possibleTarget;
          }
          const moveData = {
            id: "bide",
            name: "Bide",
            accuracy: true,
            damage: this.effectState.totalDamage * 2,
            category: "Physical",
            priority: 1,
            flags: { contact: 1, protect: 1 },
            effectType: "Move",
            type: "Normal"
          };
          this.actions.tryMoveHit(target, pokemon, moveData);
          pokemon.removeVolatile("bide");
          return false;
        }
        this.add("-activate", pokemon, "move: Bide");
      },
      onMoveAborted(pokemon) {
        pokemon.removeVolatile("bide");
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "move: Bide", "[silent]");
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    contestType: "Tough"
  },
  bind: {
    num: 20,
    accuracy: 85,
    basePower: 15,
    category: "Physical",
    name: "Bind",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  bite: {
    num: 44,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Bite",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Dark",
    contestType: "Tough"
  },
  bitterblade: {
    num: 891,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Bitter Blade",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, heal: 1, metronome: 1, slicing: 1 },
    drain: [1, 2],
    secondary: null,
    target: "normal",
    type: "Fire"
  },
  bittermalice: {
    num: 841,
    accuracy: 100,
    basePower: 75,
    category: "Special",
    name: "Bitter Malice",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        atk: -1
      }
    },
    target: "normal",
    type: "Ghost"
  },
  blackholeeclipse: {
    num: 654,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Black Hole Eclipse",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "darkiniumz",
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Cool"
  },
  blastburn: {
    num: 307,
    accuracy: 90,
    basePower: 150,
    category: "Special",
    name: "Blast Burn",
    pp: 5,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  blazekick: {
    num: 299,
    accuracy: 90,
    basePower: 85,
    category: "Physical",
    name: "Blaze Kick",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Cool"
  },
  blazingtorque: {
    num: 896,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    isNonstandard: "Unobtainable",
    name: "Blazing Torque",
    pp: 10,
    priority: 0,
    flags: {
      protect: 1,
      failencore: 1,
      failmefirst: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    noSketch: true,
    secondary: {
      chance: 30,
      status: "brn"
    },
    target: "normal",
    type: "Fire"
  },
  bleakwindstorm: {
    num: 846,
    accuracy: 80,
    basePower: 100,
    category: "Special",
    name: "Bleakwind Storm",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    onModifyMove(move, pokemon, target) {
      if (target && ["raindance", "primordialsea"].includes(target.effectiveWeather())) {
        move.accuracy = true;
      }
    },
    secondary: {
      chance: 30,
      boosts: {
        spe: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Flying"
  },
  blizzard: {
    num: 59,
    accuracy: 70,
    basePower: 110,
    category: "Special",
    name: "Blizzard",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    onModifyMove(move) {
      if (this.field.isWeather(["hail", "snow"]))
        move.accuracy = true;
    },
    secondary: {
      chance: 10,
      status: "frz"
    },
    target: "allAdjacentFoes",
    type: "Ice",
    contestType: "Beautiful"
  },
  block: {
    num: 335,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Block",
    pp: 5,
    priority: 0,
    flags: { reflectable: 1, mirror: 1, metronome: 1 },
    onHit(target, source, move) {
      return target.addVolatile("trapped", source, move, "trapper");
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  bloodmoon: {
    num: 901,
    accuracy: 100,
    basePower: 140,
    category: "Special",
    name: "Blood Moon",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, cantusetwice: 1 },
    secondary: null,
    target: "normal",
    type: "Normal"
  },
  bloomdoom: {
    num: 644,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Bloom Doom",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "grassiumz",
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  blueflare: {
    num: 551,
    accuracy: 85,
    basePower: 130,
    category: "Special",
    name: "Blue Flare",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  bodypress: {
    num: 776,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Body Press",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    overrideOffensiveStat: "def",
    secondary: null,
    target: "normal",
    type: "Fighting"
  },
  bodyslam: {
    num: 34,
    accuracy: 100,
    basePower: 85,
    category: "Physical",
    name: "Body Slam",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  boltbeak: {
    num: 754,
    accuracy: 100,
    basePower: 85,
    basePowerCallback(pokemon, target, move) {
      if (target.newlySwitched || this.queue.willMove(target)) {
        this.debug("Bolt Beak damage boost");
        return move.basePower * 2;
      }
      this.debug("Bolt Beak NOT boosted");
      return move.basePower;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Bolt Beak",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Electric"
  },
  boltstrike: {
    num: 550,
    accuracy: 85,
    basePower: 130,
    category: "Physical",
    name: "Bolt Strike",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Beautiful"
  },
  boneclub: {
    num: 125,
    accuracy: 85,
    basePower: 65,
    category: "Physical",
    isNonstandard: "Past",
    name: "Bone Club",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Ground",
    contestType: "Tough"
  },
  bonemerang: {
    num: 155,
    accuracy: 90,
    basePower: 50,
    category: "Physical",
    isNonstandard: "Past",
    name: "Bonemerang",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    multihit: 2,
    secondary: null,
    target: "normal",
    type: "Ground",
    maxMove: { basePower: 130 },
    contestType: "Tough"
  },
  bonerush: {
    num: 198,
    accuracy: 90,
    basePower: 25,
    category: "Physical",
    name: "Bone Rush",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Ground",
    zMove: { basePower: 140 },
    maxMove: { basePower: 130 },
    contestType: "Tough"
  },
  boomburst: {
    num: 586,
    accuracy: 100,
    basePower: 140,
    category: "Special",
    name: "Boomburst",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacent",
    type: "Normal",
    contestType: "Tough"
  },
  bounce: {
    num: 340,
    accuracy: 85,
    basePower: 85,
    category: "Physical",
    name: "Bounce",
    pp: 5,
    priority: 0,
    flags: {
      contact: 1,
      charge: 1,
      protect: 1,
      mirror: 1,
      gravity: 1,
      distance: 1,
      metronome: 1,
      nosleeptalk: 1,
      noassist: 1,
      failinstruct: 1
    },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    condition: {
      duration: 2,
      onInvulnerability(target, source, move) {
        if (["gust", "twister", "skyuppercut", "thunder", "hurricane", "smackdown", "thousandarrows"].includes(move.id)) {
          return;
        }
        return false;
      },
      onSourceBasePower(basePower, target, source, move) {
        if (move.id === "gust" || move.id === "twister") {
          return this.chainModify(2);
        }
      }
    },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "any",
    type: "Flying",
    contestType: "Cute"
  },
  bouncybubble: {
    num: 733,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    isNonstandard: "LGPE",
    name: "Bouncy Bubble",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, heal: 1 },
    drain: [1, 2],
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Clever"
  },
  branchpoke: {
    num: 785,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Branch Poke",
    pp: 40,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Grass"
  },
  bravebird: {
    num: 413,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Brave Bird",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, distance: 1, metronome: 1 },
    recoil: [33, 100],
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  breakingswipe: {
    num: 784,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Breaking Swipe",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      boosts: {
        atk: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Dragon"
  },
  breakneckblitz: {
    num: 622,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Breakneck Blitz",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "normaliumz",
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  brickbreak: {
    num: 280,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Brick Break",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onTryHit(pokemon) {
      pokemon.side.removeSideCondition("reflect");
      pokemon.side.removeSideCondition("lightscreen");
      pokemon.side.removeSideCondition("auroraveil");
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  brine: {
    num: 362,
    accuracy: 100,
    basePower: 65,
    category: "Special",
    name: "Brine",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, pokemon, target) {
      if (target.hp * 2 <= target.maxhp) {
        return this.chainModify(2);
      }
    },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Tough"
  },
  brutalswing: {
    num: 693,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Brutal Swing",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacent",
    type: "Dark",
    contestType: "Tough"
  },
  bubble: {
    num: 145,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    isNonstandard: "Past",
    name: "Bubble",
    pp: 30,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spe: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Water",
    contestType: "Cute"
  },
  bubblebeam: {
    num: 61,
    accuracy: 100,
    basePower: 65,
    category: "Special",
    name: "Bubble Beam",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spe: -1
      }
    },
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  bugbite: {
    num: 450,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Bug Bite",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onHit(target, source) {
      const item = target.getItem();
      if (source.hp && item.isBerry && target.takeItem(source)) {
        this.add("-enditem", target, item.name, "[from] stealeat", "[move] Bug Bite", "[of] " + source);
        if (this.singleEvent("Eat", item, null, source, null, null)) {
          this.runEvent("EatItem", source, null, null, item);
          if (item.id === "leppaberry")
            target.staleness = "external";
        }
        if (item.onEat)
          source.ateBerry = true;
      }
    },
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cute"
  },
  bugbuzz: {
    num: 405,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Bug Buzz",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Bug",
    contestType: "Beautiful"
  },
  bulkup: {
    num: 339,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Bulk Up",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      atk: 1,
      def: 1
    },
    secondary: null,
    target: "self",
    type: "Fighting",
    zMove: { boost: { atk: 1 } },
    contestType: "Cool"
  },
  bulldoze: {
    num: 523,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Bulldoze",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "allAdjacent",
    type: "Ground",
    contestType: "Tough"
  },
  bulletpunch: {
    num: 418,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Bullet Punch",
    pp: 30,
    priority: 1,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Steel",
    contestType: "Tough"
  },
  bulletseed: {
    num: 331,
    accuracy: 100,
    basePower: 25,
    category: "Physical",
    name: "Bullet Seed",
    pp: 30,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { basePower: 140 },
    maxMove: { basePower: 130 },
    contestType: "Cool"
  },
  burningbulwark: {
    num: 908,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Burning Bulwark",
    pp: 10,
    priority: 4,
    flags: { metronome: 1, noassist: 1, failcopycat: 1 },
    stallingMove: true,
    volatileStatus: "burningbulwark",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    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"] || move.category === "Status") {
          if (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          if (move.isZ || move.isMax)
            target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        if (move.smartTarget) {
          move.smartTarget = false;
        } else {
          this.add("-activate", target, "move: Protect");
        }
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        if (this.checkMoveMakesContact(move, source, target)) {
          source.trySetStatus("brn", target);
        }
        return this.NOT_FAIL;
      },
      onHit(target, source, move) {
        if (move.isZOrMaxPowered && this.checkMoveMakesContact(move, source, target)) {
          source.trySetStatus("brn", target);
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Fire"
  },
  burningjealousy: {
    num: 807,
    accuracy: 100,
    basePower: 70,
    category: "Special",
    name: "Burning Jealousy",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      onHit(target, source, move) {
        if (target?.statsRaisedThisTurn) {
          target.trySetStatus("brn", source, move);
        }
      }
    },
    target: "allAdjacentFoes",
    type: "Fire",
    contestType: "Tough"
  },
  burnup: {
    num: 682,
    accuracy: 100,
    basePower: 130,
    category: "Special",
    isNonstandard: "Unobtainable",
    name: "Burn Up",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1, metronome: 1 },
    onTryMove(pokemon, target, move) {
      if (pokemon.hasType("Fire"))
        return;
      this.add("-fail", pokemon, "move: Burn Up");
      this.attrLastMove("[still]");
      return null;
    },
    self: {
      onHit(pokemon) {
        pokemon.setType(pokemon.getTypes(true).map((type) => type === "Fire" ? "???" : type));
        this.add("-start", pokemon, "typechange", pokemon.getTypes().join("/"), "[from] move: Burn Up");
      }
    },
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Clever"
  },
  buzzybuzz: {
    num: 734,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    isNonstandard: "LGPE",
    name: "Buzzy Buzz",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Clever"
  },
  calmmind: {
    num: 347,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Calm Mind",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      spa: 1,
      spd: 1
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  camouflage: {
    num: 293,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Camouflage",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onHit(target) {
      let newType = "Normal";
      if (this.field.isTerrain("electricterrain")) {
        newType = "Electric";
      } else if (this.field.isTerrain("grassyterrain")) {
        newType = "Grass";
      } else if (this.field.isTerrain("mistyterrain")) {
        newType = "Fairy";
      } else if (this.field.isTerrain("psychicterrain")) {
        newType = "Psychic";
      }
      if (target.getTypes().join() === newType || !target.setType(newType))
        return false;
      this.add("-start", target, "typechange", newType);
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { evasion: 1 } },
    contestType: "Clever"
  },
  captivate: {
    num: 445,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Captivate",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    onTryImmunity(pokemon, source) {
      return pokemon.gender === "M" && source.gender === "F" || pokemon.gender === "F" && source.gender === "M";
    },
    boosts: {
      spa: -2
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Normal",
    zMove: { boost: { spd: 2 } },
    contestType: "Cute"
  },
  catastropika: {
    num: 658,
    accuracy: true,
    basePower: 210,
    category: "Physical",
    isNonstandard: "Past",
    name: "Catastropika",
    pp: 1,
    priority: 0,
    flags: { contact: 1 },
    isZ: "pikaniumz",
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  ceaselessedge: {
    num: 845,
    accuracy: 90,
    basePower: 65,
    category: "Physical",
    name: "Ceaseless Edge",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    onAfterHit(target, source, move) {
      if (!move.hasSheerForce && source.hp) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("spikes");
        }
      }
    },
    onAfterSubDamage(damage, target, source, move) {
      if (!move.hasSheerForce && source.hp) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("spikes");
        }
      }
    },
    secondary: {},
    // Sheer Force-boosted
    target: "normal",
    type: "Dark"
  },
  celebrate: {
    num: 606,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Celebrate",
    pp: 40,
    priority: 0,
    flags: { nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    onTryHit(target, source) {
      this.add("-activate", target, "move: Celebrate");
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Cute"
  },
  charge: {
    num: 268,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Charge",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    volatileStatus: "charge",
    condition: {
      onStart(pokemon, source, effect) {
        if (effect && ["Electromorphosis", "Wind Power"].includes(effect.name)) {
          this.add("-start", pokemon, "Charge", this.activeMove.name, "[from] ability: " + effect.name);
        } else {
          this.add("-start", pokemon, "Charge");
        }
      },
      onRestart(pokemon, source, effect) {
        if (effect && ["Electromorphosis", "Wind Power"].includes(effect.name)) {
          this.add("-start", pokemon, "Charge", this.activeMove.name, "[from] ability: " + effect.name);
        } else {
          this.add("-start", pokemon, "Charge");
        }
      },
      onBasePowerPriority: 9,
      onBasePower(basePower, attacker, defender, move) {
        if (move.type === "Electric") {
          this.debug("charge boost");
          return this.chainModify(2);
        }
      },
      onMoveAborted(pokemon, target, move) {
        if (move.type === "Electric" && move.id !== "charge") {
          pokemon.removeVolatile("charge");
        }
      },
      onAfterMove(pokemon, target, move) {
        if (move.type === "Electric" && move.id !== "charge") {
          pokemon.removeVolatile("charge");
        }
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Charge", "[silent]");
      }
    },
    boosts: {
      spd: 1
    },
    secondary: null,
    target: "self",
    type: "Electric",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  chargebeam: {
    num: 451,
    accuracy: 90,
    basePower: 50,
    category: "Special",
    name: "Charge Beam",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 70,
      self: {
        boosts: {
          spa: 1
        }
      }
    },
    target: "normal",
    type: "Electric",
    contestType: "Beautiful"
  },
  charm: {
    num: 204,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Charm",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    boosts: {
      atk: -2
    },
    secondary: null,
    target: "normal",
    type: "Fairy",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  chatter: {
    num: 448,
    accuracy: 100,
    basePower: 65,
    category: "Special",
    isNonstandard: "Past",
    name: "Chatter",
    pp: 20,
    priority: 0,
    flags: {
      protect: 1,
      mirror: 1,
      sound: 1,
      distance: 1,
      bypasssub: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    noSketch: true,
    secondary: {
      chance: 100,
      volatileStatus: "confusion"
    },
    target: "any",
    type: "Flying",
    contestType: "Cute"
  },
  chillingwater: {
    num: 886,
    accuracy: 100,
    basePower: 50,
    category: "Special",
    name: "Chilling Water",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      boosts: {
        atk: -1
      }
    },
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  chillyreception: {
    num: 881,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Chilly Reception",
    pp: 10,
    priority: 0,
    flags: {},
    // TODO show prepare message before the "POKEMON used MOVE!" message
    // This happens even before sleep shows its "POKEMON is fast asleep." message
    weather: "snow",
    selfSwitch: true,
    secondary: null,
    target: "all",
    type: "Ice"
  },
  chipaway: {
    num: 498,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    isNonstandard: "Past",
    name: "Chip Away",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    ignoreDefensive: true,
    ignoreEvasion: true,
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  chloroblast: {
    num: 835,
    accuracy: 95,
    basePower: 150,
    category: "Special",
    name: "Chloroblast",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    // Recoil implemented in battle-actions.ts
    secondary: null,
    target: "normal",
    type: "Grass"
  },
  circlethrow: {
    num: 509,
    accuracy: 90,
    basePower: 60,
    category: "Physical",
    name: "Circle Throw",
    pp: 10,
    priority: -6,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, noassist: 1, failcopycat: 1 },
    forceSwitch: true,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  clamp: {
    num: 128,
    accuracy: 85,
    basePower: 35,
    category: "Physical",
    isNonstandard: "Past",
    name: "Clamp",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Tough"
  },
  clangingscales: {
    num: 691,
    accuracy: 100,
    basePower: 110,
    category: "Special",
    name: "Clanging Scales",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    selfBoost: {
      boosts: {
        def: -1
      }
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Dragon",
    contestType: "Tough"
  },
  clangoroussoul: {
    num: 775,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Clangorous Soul",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, sound: 1, dance: 1 },
    onTry(source) {
      if (source.hp <= source.maxhp * 33 / 100 || source.maxhp === 1)
        return false;
    },
    onTryHit(pokemon, target, move) {
      if (!this.boost(move.boosts))
        return null;
      delete move.boosts;
    },
    onHit(pokemon) {
      this.directDamage(pokemon.maxhp * 33 / 100);
    },
    boosts: {
      atk: 1,
      def: 1,
      spa: 1,
      spd: 1,
      spe: 1
    },
    secondary: null,
    target: "self",
    type: "Dragon"
  },
  clangoroussoulblaze: {
    num: 728,
    accuracy: true,
    basePower: 185,
    category: "Special",
    isNonstandard: "Past",
    name: "Clangorous Soulblaze",
    pp: 1,
    priority: 0,
    flags: { sound: 1, bypasssub: 1 },
    selfBoost: {
      boosts: {
        atk: 1,
        def: 1,
        spa: 1,
        spd: 1,
        spe: 1
      }
    },
    isZ: "kommoniumz",
    secondary: {
      // Sheer Force negates the selfBoost even though it is not secondary
    },
    target: "allAdjacentFoes",
    type: "Dragon",
    contestType: "Cool"
  },
  clearsmog: {
    num: 499,
    accuracy: true,
    basePower: 50,
    category: "Special",
    name: "Clear Smog",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onHit(target) {
      target.clearBoosts();
      this.add("-clearboost", target);
    },
    secondary: null,
    target: "normal",
    type: "Poison",
    contestType: "Beautiful"
  },
  closecombat: {
    num: 370,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Close Combat",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      boosts: {
        def: -1,
        spd: -1
      }
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  coaching: {
    num: 811,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Coaching",
    pp: 10,
    priority: 0,
    flags: { bypasssub: 1, allyanim: 1, metronome: 1 },
    secondary: null,
    boosts: {
      atk: 1,
      def: 1
    },
    target: "adjacentAlly",
    type: "Fighting"
  },
  coil: {
    num: 489,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Coil",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      atk: 1,
      def: 1,
      accuracy: 1
    },
    secondary: null,
    target: "self",
    type: "Poison",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Tough"
  },
  collisioncourse: {
    num: 878,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Collision Course",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    onBasePower(basePower, source, target, move) {
      if (target.runEffectiveness(move) > 0) {
        this.debug(`collision course super effective buff`);
        return this.chainModify([5461, 4096]);
      }
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  combattorque: {
    num: 899,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    isNonstandard: "Unobtainable",
    name: "Combat Torque",
    pp: 10,
    priority: 0,
    flags: {
      protect: 1,
      failencore: 1,
      failmefirst: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    noSketch: true,
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Fighting"
  },
  cometpunch: {
    num: 4,
    accuracy: 85,
    basePower: 18,
    category: "Physical",
    isNonstandard: "Past",
    name: "Comet Punch",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Normal",
    maxMove: { basePower: 100 },
    contestType: "Tough"
  },
  comeuppance: {
    num: 894,
    accuracy: 100,
    basePower: 0,
    damageCallback(pokemon) {
      const lastDamagedBy = pokemon.getLastDamagedBy(true);
      if (lastDamagedBy !== void 0) {
        return lastDamagedBy.damage * 1.5 || 1;
      }
      return 0;
    },
    category: "Physical",
    name: "Comeuppance",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, failmefirst: 1 },
    onTry(source) {
      const lastDamagedBy = source.getLastDamagedBy(true);
      if (lastDamagedBy === void 0 || !lastDamagedBy.thisTurn)
        return false;
    },
    onModifyTarget(targetRelayVar, source, target, move) {
      const lastDamagedBy = source.getLastDamagedBy(true);
      if (lastDamagedBy) {
        targetRelayVar.target = this.getAtSlot(lastDamagedBy.slot);
      }
    },
    secondary: null,
    target: "scripted",
    type: "Dark",
    contestType: "Cool"
  },
  confide: {
    num: 590,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Confide",
    pp: 20,
    priority: 0,
    flags: { reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    boosts: {
      spa: -1
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spd: 1 } },
    contestType: "Cute"
  },
  confuseray: {
    num: 109,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Confuse Ray",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    volatileStatus: "confusion",
    secondary: null,
    target: "normal",
    type: "Ghost",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  confusion: {
    num: 93,
    accuracy: 100,
    basePower: 50,
    category: "Special",
    name: "Confusion",
    pp: 25,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  constrict: {
    num: 132,
    accuracy: 100,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Constrict",
    pp: 35,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spe: -1
      }
    },
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  continentalcrush: {
    num: 632,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Continental Crush",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "rockiumz",
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Cool"
  },
  conversion: {
    num: 160,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Conversion",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onHit(target) {
      const type = this.dex.moves.get(target.moveSlots[0].id).type;
      if (target.hasType(type) || !target.setType(type))
        return false;
      this.add("-start", target, "typechange", type);
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Beautiful"
  },
  conversion2: {
    num: 176,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Conversion 2",
    pp: 30,
    priority: 0,
    flags: { bypasssub: 1, metronome: 1 },
    onHit(target, source) {
      if (!target.lastMoveUsed) {
        return false;
      }
      const possibleTypes = [];
      const attackType = target.lastMoveUsed.type;
      for (const type of this.dex.types.names()) {
        if (source.hasType(type))
          continue;
        const typeCheck = this.dex.types.get(type).damageTaken[attackType];
        if (typeCheck === 2 || typeCheck === 3) {
          possibleTypes.push(type);
        }
      }
      if (!possibleTypes.length) {
        return false;
      }
      const randomType = this.sample(possibleTypes);
      if (!source.setType(randomType))
        return false;
      this.add("-start", source, "typechange", randomType);
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { effect: "heal" },
    contestType: "Beautiful"
  },
  copycat: {
    num: 383,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Copycat",
    pp: 20,
    priority: 0,
    flags: { failencore: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    onHit(pokemon) {
      let move = this.lastMove;
      if (!move)
        return;
      if (move.isMax && move.baseMove)
        move = this.dex.moves.get(move.baseMove);
      if (move.flags["failcopycat"] || move.isZ || move.isMax) {
        return false;
      }
      this.actions.useMove(move.id, pokemon);
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { accuracy: 1 } },
    contestType: "Cute"
  },
  coreenforcer: {
    num: 687,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    isNonstandard: "Past",
    name: "Core Enforcer",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onHit(target) {
      if (target.getAbility().flags["cantsuppress"])
        return;
      if (target.newlySwitched || this.queue.willMove(target))
        return;
      target.addVolatile("gastroacid");
    },
    onAfterSubDamage(damage, target) {
      if (target.getAbility().flags["cantsuppress"])
        return;
      if (target.newlySwitched || this.queue.willMove(target))
        return;
      target.addVolatile("gastroacid");
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Dragon",
    zMove: { basePower: 140 },
    contestType: "Tough"
  },
  corkscrewcrash: {
    num: 638,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Corkscrew Crash",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "steeliumz",
    secondary: null,
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  corrosivegas: {
    num: 810,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Unobtainable",
    name: "Corrosive Gas",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      const item = target.takeItem(source);
      if (item) {
        this.add("-enditem", target, item.name, "[from] move: Corrosive Gas", "[of] " + source);
      } else {
        this.add("-fail", target, "move: Corrosive Gas");
      }
    },
    secondary: null,
    target: "allAdjacent",
    type: "Poison"
  },
  cosmicpower: {
    num: 322,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Cosmic Power",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 1,
      spd: 1
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { boost: { spd: 1 } },
    contestType: "Beautiful"
  },
  cottonguard: {
    num: 538,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Cotton Guard",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 3
    },
    secondary: null,
    target: "self",
    type: "Grass",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  cottonspore: {
    num: 178,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Cotton Spore",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1, powder: 1 },
    boosts: {
      spe: -2
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Grass",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  counter: {
    num: 68,
    accuracy: 100,
    basePower: 0,
    damageCallback(pokemon) {
      if (!pokemon.volatiles["counter"])
        return 0;
      return pokemon.volatiles["counter"].damage || 1;
    },
    category: "Physical",
    name: "Counter",
    pp: 20,
    priority: -5,
    flags: { contact: 1, protect: 1, failmefirst: 1, noassist: 1, failcopycat: 1 },
    beforeTurnCallback(pokemon) {
      pokemon.addVolatile("counter");
    },
    onTry(source) {
      if (!source.volatiles["counter"])
        return false;
      if (source.volatiles["counter"].slot === null)
        return false;
    },
    condition: {
      duration: 1,
      noCopy: true,
      onStart(target, source, move) {
        this.effectState.slot = null;
        this.effectState.damage = 0;
      },
      onRedirectTargetPriority: -1,
      onRedirectTarget(target, source, source2, move) {
        if (move.id !== "counter")
          return;
        if (source !== this.effectState.target || !this.effectState.slot)
          return;
        return this.getAtSlot(this.effectState.slot);
      },
      onDamagingHit(damage, target, source, move) {
        if (!source.isAlly(target) && this.getCategory(move) === "Physical") {
          this.effectState.slot = source.getSlot();
          this.effectState.damage = 2 * damage;
        }
      }
    },
    secondary: null,
    target: "scripted",
    type: "Fighting",
    maxMove: { basePower: 75 },
    contestType: "Tough"
  },
  courtchange: {
    num: 756,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Court Change",
    pp: 10,
    priority: 0,
    flags: { mirror: 1, metronome: 1 },
    onHitField(target, source) {
      const sideConditions = [
        "mist",
        "lightscreen",
        "reflect",
        "spikes",
        "safeguard",
        "tailwind",
        "toxicspikes",
        "stealthrock",
        "waterpledge",
        "firepledge",
        "grasspledge",
        "stickyweb",
        "auroraveil",
        "gmaxsteelsurge",
        "gmaxcannonade",
        "gmaxvinelash",
        "gmaxwildfire"
      ];
      let success = false;
      if (this.gameType === "freeforall") {
        const offset = this.random(3) + 1;
        const sides = [this.sides[0], this.sides[2], this.sides[1], this.sides[3]];
        const temp = { 0: {}, 1: {}, 2: {}, 3: {} };
        for (const side of sides) {
          for (const id in side.sideConditions) {
            if (!sideConditions.includes(id))
              continue;
            temp[side.n][id] = side.sideConditions[id];
            delete side.sideConditions[id];
            const effectName = this.dex.conditions.get(id).name;
            this.add("-sideend", side, effectName, "[silent]");
            success = true;
          }
        }
        for (let i = 0; i < 4; i++) {
          const sourceSideConditions = temp[sides[i].n];
          const targetSide = sides[(i + offset) % 4];
          for (const id in sourceSideConditions) {
            targetSide.sideConditions[id] = sourceSideConditions[id];
            const effectName = this.dex.conditions.get(id).name;
            let layers = sourceSideConditions[id].layers || 1;
            for (; layers > 0; layers--)
              this.add("-sidestart", targetSide, effectName, "[silent]");
          }
        }
      } else {
        const sourceSideConditions = source.side.sideConditions;
        const targetSideConditions = source.side.foe.sideConditions;
        const sourceTemp = {};
        const targetTemp = {};
        for (const id in sourceSideConditions) {
          if (!sideConditions.includes(id))
            continue;
          sourceTemp[id] = sourceSideConditions[id];
          delete sourceSideConditions[id];
          success = true;
        }
        for (const id in targetSideConditions) {
          if (!sideConditions.includes(id))
            continue;
          targetTemp[id] = targetSideConditions[id];
          delete targetSideConditions[id];
          success = true;
        }
        for (const id in sourceTemp) {
          targetSideConditions[id] = sourceTemp[id];
        }
        for (const id in targetTemp) {
          sourceSideConditions[id] = targetTemp[id];
        }
        this.add("-swapsideconditions");
      }
      if (!success)
        return false;
      this.add("-activate", source, "move: Court Change");
    },
    secondary: null,
    target: "all",
    type: "Normal"
  },
  covet: {
    num: 343,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Covet",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, failmefirst: 1, noassist: 1, failcopycat: 1 },
    onAfterHit(target, source, move) {
      if (source.item || source.volatiles["gem"]) {
        return;
      }
      const yourItem = target.takeItem(source);
      if (!yourItem) {
        return;
      }
      if (!this.singleEvent("TakeItem", yourItem, target.itemState, source, target, move, yourItem) || !source.setItem(yourItem)) {
        target.item = yourItem.id;
        return;
      }
      this.add("-item", source, yourItem, "[from] move: Covet", "[of] " + target);
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  crabhammer: {
    num: 152,
    accuracy: 90,
    basePower: 100,
    category: "Physical",
    name: "Crabhammer",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Tough"
  },
  craftyshield: {
    num: 578,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Crafty Shield",
    pp: 10,
    priority: 3,
    flags: {},
    sideCondition: "craftyshield",
    onTry() {
      return !!this.queue.willAct();
    },
    condition: {
      duration: 1,
      onSideStart(target, source) {
        this.add("-singleturn", source, "Crafty Shield");
      },
      onTryHitPriority: 3,
      onTryHit(target, source, move) {
        if (["self", "all"].includes(move.target) || move.category !== "Status")
          return;
        this.add("-activate", target, "move: Crafty Shield");
        return this.NOT_FAIL;
      }
    },
    secondary: null,
    target: "allySide",
    type: "Fairy",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  crosschop: {
    num: 238,
    accuracy: 80,
    basePower: 100,
    category: "Physical",
    name: "Cross Chop",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  crosspoison: {
    num: 440,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Cross Poison",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    secondary: {
      chance: 10,
      status: "psn"
    },
    critRatio: 2,
    target: "normal",
    type: "Poison",
    contestType: "Cool"
  },
  crunch: {
    num: 242,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Crunch",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    secondary: {
      chance: 20,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Dark",
    contestType: "Tough"
  },
  crushclaw: {
    num: 306,
    accuracy: 95,
    basePower: 75,
    category: "Physical",
    name: "Crush Claw",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 50,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  crushgrip: {
    num: 462,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      const hp = target.hp;
      const maxHP = target.maxhp;
      const bp = Math.floor(Math.floor((120 * (100 * Math.floor(hp * 4096 / maxHP)) + 2048 - 1) / 4096) / 100) || 1;
      this.debug("BP for " + hp + "/" + maxHP + " HP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Crush Grip",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 190 },
    maxMove: { basePower: 140 },
    contestType: "Tough"
  },
  curse: {
    num: 174,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Curse",
    pp: 10,
    priority: 0,
    flags: { bypasssub: 1, metronome: 1 },
    volatileStatus: "curse",
    onModifyMove(move, source, target) {
      if (!source.hasType("Ghost")) {
        move.target = move.nonGhostTarget;
      } else if (source.isAlly(target)) {
        move.target = "randomNormal";
      }
    },
    onTryHit(target, source, move) {
      if (!source.hasType("Ghost")) {
        delete move.volatileStatus;
        delete move.onHit;
        move.self = { boosts: { spe: -1, atk: 1, def: 1 } };
      } else if (move.volatileStatus && target.volatiles["curse"]) {
        return false;
      }
    },
    onHit(target, source) {
      this.directDamage(source.maxhp / 2, source, source);
    },
    condition: {
      onStart(pokemon, source) {
        this.add("-start", pokemon, "Curse", "[of] " + source);
      },
      onResidualOrder: 12,
      onResidual(pokemon) {
        this.damage(pokemon.baseMaxhp / 4);
      }
    },
    secondary: null,
    target: "normal",
    nonGhostTarget: "self",
    type: "Ghost",
    zMove: { effect: "curse" },
    contestType: "Tough"
  },
  cut: {
    num: 15,
    accuracy: 95,
    basePower: 50,
    category: "Physical",
    isNonstandard: "Unobtainable",
    name: "Cut",
    pp: 30,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  darkestlariat: {
    num: 663,
    accuracy: 100,
    basePower: 85,
    category: "Physical",
    name: "Darkest Lariat",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    ignoreEvasion: true,
    ignoreDefensive: true,
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Cool"
  },
  darkpulse: {
    num: 399,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Dark Pulse",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, metronome: 1, pulse: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "flinch"
    },
    target: "any",
    type: "Dark",
    contestType: "Cool"
  },
  darkvoid: {
    num: 464,
    accuracy: 50,
    basePower: 0,
    category: "Status",
    name: "Dark Void",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    status: "slp",
    onTry(source, target, move) {
      if (source.species.name === "Darkrai" || move.hasBounced) {
        return;
      }
      this.add("-fail", source, "move: Dark Void");
      this.hint("Only a Pokemon whose form is Darkrai can use this move.");
      return null;
    },
    noSketch: true,
    secondary: null,
    target: "allAdjacentFoes",
    type: "Dark",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  dazzlinggleam: {
    num: 605,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Dazzling Gleam",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Fairy",
    contestType: "Beautiful"
  },
  decorate: {
    num: 777,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Decorate",
    pp: 15,
    priority: 0,
    flags: { allyanim: 1 },
    secondary: null,
    boosts: {
      atk: 2,
      spa: 2
    },
    target: "normal",
    type: "Fairy"
  },
  defendorder: {
    num: 455,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Defend Order",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 1,
      spd: 1
    },
    secondary: null,
    target: "self",
    type: "Bug",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  defensecurl: {
    num: 111,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Defense Curl",
    pp: 40,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 1
    },
    volatileStatus: "defensecurl",
    condition: {
      noCopy: true,
      onRestart: () => null
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { accuracy: 1 } },
    contestType: "Cute"
  },
  defog: {
    num: 432,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Defog",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    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",
        "stickyweb",
        "gmaxsteelsurge"
      ];
      const removeAll = [
        "spikes",
        "toxicspikes",
        "stealthrock",
        "stickyweb",
        "gmaxsteelsurge"
      ];
      for (const targetCondition of removeTarget) {
        if (target.side.removeSideCondition(targetCondition)) {
          if (!removeAll.includes(targetCondition))
            continue;
          this.add("-sideend", target.side, this.dex.conditions.get(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.conditions.get(sideCondition).name, "[from] move: Defog", "[of] " + source);
          success = true;
        }
      }
      this.field.clearTerrain();
      return success;
    },
    secondary: null,
    target: "normal",
    type: "Flying",
    zMove: { boost: { accuracy: 1 } },
    contestType: "Cool"
  },
  destinybond: {
    num: 194,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Destiny Bond",
    pp: 5,
    priority: 0,
    flags: { bypasssub: 1, noassist: 1, failcopycat: 1 },
    volatileStatus: "destinybond",
    onPrepareHit(pokemon) {
      return !pokemon.removeVolatile("destinybond");
    },
    condition: {
      onStart(pokemon) {
        this.add("-singlemove", pokemon, "Destiny Bond");
      },
      onFaint(target, source, effect) {
        if (!source || !effect || target.isAlly(source))
          return;
        if (effect.effectType === "Move" && !effect.flags["futuremove"]) {
          if (source.volatiles["dynamax"]) {
            this.add("-hint", "Dynamaxed Pok\xE9mon are immune to Destiny Bond.");
            return;
          }
          this.add("-activate", target, "move: Destiny Bond");
          source.faint();
        }
      },
      onBeforeMovePriority: -1,
      onBeforeMove(pokemon, target, move) {
        if (move.id === "destinybond")
          return;
        this.debug("removing Destiny Bond before attack");
        pokemon.removeVolatile("destinybond");
      },
      onMoveAborted(pokemon, target, move) {
        pokemon.removeVolatile("destinybond");
      }
    },
    secondary: null,
    target: "self",
    type: "Ghost",
    zMove: { effect: "redirect" },
    contestType: "Clever"
  },
  detect: {
    num: 197,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Detect",
    pp: 5,
    priority: 4,
    flags: { noassist: 1, failcopycat: 1 },
    stallingMove: true,
    volatileStatus: "protect",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    onHit(pokemon) {
      pokemon.addVolatile("stall");
    },
    secondary: null,
    target: "self",
    type: "Fighting",
    zMove: { boost: { evasion: 1 } },
    contestType: "Cool"
  },
  devastatingdrake: {
    num: 652,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Devastating Drake",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "dragoniumz",
    secondary: null,
    target: "normal",
    type: "Dragon",
    contestType: "Cool"
  },
  diamondstorm: {
    num: 591,
    accuracy: 95,
    basePower: 100,
    category: "Physical",
    name: "Diamond Storm",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    self: {
      chance: 50,
      boosts: {
        def: 2
      }
    },
    secondary: {
      // Sheer Force negates the self even though it is not secondary
    },
    target: "allAdjacentFoes",
    type: "Rock",
    contestType: "Beautiful"
  },
  dig: {
    num: 91,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Dig",
    pp: 10,
    priority: 0,
    flags: {
      contact: 1,
      charge: 1,
      protect: 1,
      mirror: 1,
      nonsky: 1,
      metronome: 1,
      nosleeptalk: 1,
      noassist: 1,
      failinstruct: 1
    },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    condition: {
      duration: 2,
      onImmunity(type, pokemon) {
        if (type === "sandstorm" || type === "hail")
          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);
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Ground",
    contestType: "Tough"
  },
  disable: {
    num: 50,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Disable",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    volatileStatus: "disable",
    onTryHit(target) {
      if (!target.lastMove || target.lastMove.isZ || target.lastMove.isMax || target.lastMove.id === "struggle") {
        return false;
      }
    },
    condition: {
      duration: 5,
      noCopy: true,
      // doesn't get copied by Baton Pass
      onStart(pokemon, source, effect) {
        if (this.queue.willMove(pokemon) || pokemon === this.activePokemon && this.activeMove && !this.activeMove.isExternal) {
          this.effectState.duration--;
        }
        if (!pokemon.lastMove) {
          this.debug(`Pokemon hasn't moved yet`);
          return false;
        }
        for (const moveSlot of pokemon.moveSlots) {
          if (moveSlot.id === pokemon.lastMove.id) {
            if (!moveSlot.pp) {
              this.debug("Move out of PP");
              return false;
            }
          }
        }
        if (effect.effectType === "Ability") {
          this.add("-start", pokemon, "Disable", pokemon.lastMove.name, "[from] ability: Cursed Body", "[of] " + source);
        } else {
          this.add("-start", pokemon, "Disable", pokemon.lastMove.name);
        }
        this.effectState.move = pokemon.lastMove.id;
      },
      onResidualOrder: 17,
      onEnd(pokemon) {
        this.add("-end", pokemon, "Disable");
      },
      onBeforeMovePriority: 7,
      onBeforeMove(attacker, defender, move) {
        if (!move.isZ && move.id === this.effectState.move) {
          this.add("cant", attacker, "Disable", move);
          return false;
        }
      },
      onDisableMove(pokemon) {
        for (const moveSlot of pokemon.moveSlots) {
          if (moveSlot.id === this.effectState.move) {
            pokemon.disableMove(moveSlot.id);
          }
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  disarmingvoice: {
    num: 574,
    accuracy: true,
    basePower: 40,
    category: "Special",
    name: "Disarming Voice",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Fairy",
    contestType: "Cute"
  },
  discharge: {
    num: 435,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Discharge",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "allAdjacent",
    type: "Electric",
    contestType: "Beautiful"
  },
  direclaw: {
    num: 827,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Dire Claw",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 50,
      onHit(target, source) {
        const result = this.random(3);
        if (result === 0) {
          target.trySetStatus("psn", source);
        } else if (result === 1) {
          target.trySetStatus("par", source);
        } else {
          target.trySetStatus("slp", source);
        }
      }
    },
    target: "normal",
    type: "Poison"
  },
  dive: {
    num: 291,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Dive",
    pp: 10,
    priority: 0,
    flags: {
      contact: 1,
      charge: 1,
      protect: 1,
      mirror: 1,
      nonsky: 1,
      allyanim: 1,
      metronome: 1,
      nosleeptalk: 1,
      noassist: 1,
      failinstruct: 1
    },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      if (attacker.hasAbility("gulpmissile") && attacker.species.name === "Cramorant" && !attacker.transformed) {
        const forme = attacker.hp <= attacker.maxhp / 2 ? "cramorantgorging" : "cramorantgulping";
        attacker.formeChange(forme, move);
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    condition: {
      duration: 2,
      onImmunity(type, pokemon) {
        if (type === "sandstorm" || type === "hail")
          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);
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  dizzypunch: {
    num: 146,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    isNonstandard: "Past",
    name: "Dizzy Punch",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  doodle: {
    num: 867,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Doodle",
    pp: 10,
    priority: 0,
    flags: {},
    onHit(target, source, move) {
      let success = false;
      if (!target.getAbility().flags["failroleplay"]) {
        for (const pokemon of source.alliesAndSelf()) {
          if (pokemon.ability === target.ability || pokemon.getAbility().flags["cantsuppress"])
            continue;
          const oldAbility = pokemon.setAbility(target.ability);
          if (oldAbility) {
            this.add("-ability", pokemon, target.getAbility().name, "[from] move: Doodle");
            success = true;
          } else if (!success && oldAbility === null) {
            success = null;
          }
        }
      }
      if (!success) {
        if (success === false) {
          this.add("-fail", source);
        }
        this.attrLastMove("[still]");
        return this.NOT_FAIL;
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Normal"
  },
  doomdesire: {
    num: 353,
    accuracy: 100,
    basePower: 140,
    category: "Special",
    name: "Doom Desire",
    pp: 5,
    priority: 0,
    flags: { metronome: 1, futuremove: 1 },
    onTry(source, target) {
      if (!target.side.addSlotCondition(target, "futuremove"))
        return false;
      Object.assign(target.side.slotConditions[target.position]["futuremove"], {
        move: "doomdesire",
        source,
        moveData: {
          id: "doomdesire",
          name: "Doom Desire",
          accuracy: 100,
          basePower: 140,
          category: "Special",
          priority: 0,
          flags: { metronome: 1, futuremove: 1 },
          effectType: "Move",
          type: "Steel"
        }
      });
      this.add("-start", source, "Doom Desire");
      return this.NOT_FAIL;
    },
    secondary: null,
    target: "normal",
    type: "Steel",
    contestType: "Beautiful"
  },
  doubleedge: {
    num: 38,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Double-Edge",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [33, 100],
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  doublehit: {
    num: 458,
    accuracy: 90,
    basePower: 35,
    category: "Physical",
    name: "Double Hit",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: 2,
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 140 },
    maxMove: { basePower: 120 },
    contestType: "Cool"
  },
  doubleironbash: {
    num: 742,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    isNonstandard: "Past",
    name: "Double Iron Bash",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1 },
    multihit: 2,
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Steel",
    zMove: { basePower: 180 },
    maxMove: { basePower: 140 },
    contestType: "Clever"
  },
  doublekick: {
    num: 24,
    accuracy: 100,
    basePower: 30,
    category: "Physical",
    name: "Double Kick",
    pp: 30,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: 2,
    secondary: null,
    target: "normal",
    type: "Fighting",
    maxMove: { basePower: 80 },
    contestType: "Cool"
  },
  doubleshock: {
    num: 892,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Double Shock",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    onTryMove(pokemon, target, move) {
      if (pokemon.hasType("Electric"))
        return;
      this.add("-fail", pokemon, "move: Double Shock");
      this.attrLastMove("[still]");
      return null;
    },
    self: {
      onHit(pokemon) {
        pokemon.setType(pokemon.getTypes(true).map((type) => type === "Electric" ? "???" : type));
        this.add("-start", pokemon, "typechange", pokemon.getTypes().join("/"), "[from] move: Double Shock");
      }
    },
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Clever"
  },
  doubleslap: {
    num: 3,
    accuracy: 85,
    basePower: 15,
    category: "Physical",
    isNonstandard: "Past",
    name: "Double Slap",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  doubleteam: {
    num: 104,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Double Team",
    pp: 15,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      evasion: 1
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cool"
  },
  dracometeor: {
    num: 434,
    accuracy: 90,
    basePower: 130,
    category: "Special",
    name: "Draco Meteor",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    self: {
      boosts: {
        spa: -2
      }
    },
    secondary: null,
    target: "normal",
    type: "Dragon",
    contestType: "Beautiful"
  },
  dragonascent: {
    num: 620,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Dragon Ascent",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, distance: 1 },
    self: {
      boosts: {
        def: -1,
        spd: -1
      }
    },
    target: "any",
    type: "Flying",
    contestType: "Beautiful"
  },
  dragonbreath: {
    num: 225,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    name: "Dragon Breath",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Dragon",
    contestType: "Cool"
  },
  dragoncheer: {
    num: 913,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Dragon Cheer",
    pp: 15,
    priority: 0,
    flags: { bypasssub: 1, allyanim: 1, metronome: 1 },
    volatileStatus: "dragoncheer",
    condition: {
      onStart(target, source, effect) {
        if (target.volatiles["focusenergy"])
          return false;
        if (effect && ["costar", "imposter", "psychup", "transform"].includes(effect.id)) {
          this.add("-start", target, "move: Dragon Cheer", "[silent]");
        } else {
          this.add("-start", target, "move: Dragon Cheer");
        }
        this.effectState.hasDragonType = target.hasType("Dragon");
      },
      onModifyCritRatio(critRatio, source) {
        return critRatio + (this.effectState.hasDragonType ? 2 : 1);
      }
    },
    secondary: null,
    target: "adjacentAlly",
    type: "Dragon"
  },
  dragonclaw: {
    num: 337,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Dragon Claw",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Dragon",
    contestType: "Cool"
  },
  dragondance: {
    num: 349,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Dragon Dance",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, dance: 1, metronome: 1 },
    boosts: {
      atk: 1,
      spe: 1
    },
    secondary: null,
    target: "self",
    type: "Dragon",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cool"
  },
  dragondarts: {
    num: 751,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Dragon Darts",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, noparentalbond: 1 },
    multihit: 2,
    smartTarget: true,
    secondary: null,
    target: "normal",
    type: "Dragon",
    maxMove: { basePower: 130 }
  },
  dragonenergy: {
    num: 820,
    accuracy: 100,
    basePower: 150,
    basePowerCallback(pokemon, target, move) {
      const bp = move.basePower * pokemon.hp / pokemon.maxhp;
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Special",
    name: "Dragon Energy",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Dragon"
  },
  dragonhammer: {
    num: 692,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Dragon Hammer",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Dragon",
    contestType: "Tough"
  },
  dragonpulse: {
    num: 406,
    accuracy: 100,
    basePower: 85,
    category: "Special",
    name: "Dragon Pulse",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, metronome: 1, pulse: 1 },
    secondary: null,
    target: "any",
    type: "Dragon",
    contestType: "Beautiful"
  },
  dragonrage: {
    num: 82,
    accuracy: 100,
    basePower: 0,
    damage: 40,
    category: "Special",
    isNonstandard: "Past",
    name: "Dragon Rage",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Dragon",
    contestType: "Cool"
  },
  dragonrush: {
    num: 407,
    accuracy: 75,
    basePower: 100,
    category: "Physical",
    name: "Dragon Rush",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Dragon",
    contestType: "Tough"
  },
  dragontail: {
    num: 525,
    accuracy: 90,
    basePower: 60,
    category: "Physical",
    name: "Dragon Tail",
    pp: 10,
    priority: -6,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, noassist: 1, failcopycat: 1 },
    forceSwitch: true,
    target: "normal",
    type: "Dragon",
    contestType: "Tough"
  },
  drainingkiss: {
    num: 577,
    accuracy: 100,
    basePower: 50,
    category: "Special",
    name: "Draining Kiss",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, heal: 1, metronome: 1 },
    drain: [3, 4],
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Cute"
  },
  drainpunch: {
    num: 409,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Drain Punch",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  dreameater: {
    num: 138,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Dream Eater",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    onTryImmunity(target) {
      return target.status === "slp" || target.hasAbility("comatose");
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  drillpeck: {
    num: 65,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Drill Peck",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, distance: 1, metronome: 1 },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  drillrun: {
    num: 529,
    accuracy: 95,
    basePower: 80,
    category: "Physical",
    name: "Drill Run",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Ground",
    contestType: "Tough"
  },
  drumbeating: {
    num: 778,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Drum Beating",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "normal",
    type: "Grass"
  },
  dualchop: {
    num: 530,
    accuracy: 90,
    basePower: 40,
    category: "Physical",
    isNonstandard: "Past",
    name: "Dual Chop",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: 2,
    secondary: null,
    target: "normal",
    type: "Dragon",
    maxMove: { basePower: 130 },
    contestType: "Tough"
  },
  dualwingbeat: {
    num: 814,
    accuracy: 90,
    basePower: 40,
    category: "Physical",
    name: "Dual Wingbeat",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: 2,
    secondary: null,
    target: "normal",
    type: "Flying",
    maxMove: { basePower: 130 }
  },
  dynamaxcannon: {
    num: 744,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Dynamax Cannon",
    pp: 5,
    priority: 0,
    flags: { protect: 1, failencore: 1, nosleeptalk: 1, failcopycat: 1, failmimic: 1, failinstruct: 1, noparentalbond: 1 },
    secondary: null,
    target: "normal",
    type: "Dragon"
  },
  dynamicpunch: {
    num: 223,
    accuracy: 50,
    basePower: 100,
    category: "Physical",
    name: "Dynamic Punch",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: {
      chance: 100,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  earthpower: {
    num: 414,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Earth Power",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Ground",
    contestType: "Beautiful"
  },
  earthquake: {
    num: 89,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Earthquake",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacent",
    type: "Ground",
    contestType: "Tough"
  },
  echoedvoice: {
    num: 497,
    accuracy: 100,
    basePower: 40,
    basePowerCallback(pokemon, target, move) {
      let bp = move.basePower;
      if (this.field.pseudoWeather.echoedvoice) {
        bp = move.basePower * this.field.pseudoWeather.echoedvoice.multiplier;
      }
      this.debug("BP: " + move.basePower);
      return bp;
    },
    category: "Special",
    name: "Echoed Voice",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    onTry() {
      this.field.addPseudoWeather("echoedvoice");
    },
    condition: {
      duration: 2,
      onFieldStart() {
        this.effectState.multiplier = 1;
      },
      onFieldRestart() {
        if (this.effectState.duration !== 2) {
          this.effectState.duration = 2;
          if (this.effectState.multiplier < 5) {
            this.effectState.multiplier++;
          }
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Beautiful"
  },
  eerieimpulse: {
    num: 598,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Eerie Impulse",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      spa: -2
    },
    secondary: null,
    target: "normal",
    type: "Electric",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  eeriespell: {
    num: 826,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Eerie Spell",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: {
      chance: 100,
      onHit(target) {
        if (!target.hp)
          return;
        let move = target.lastMove;
        if (!move || move.isZ)
          return;
        if (move.isMax && move.baseMove)
          move = this.dex.moves.get(move.baseMove);
        const ppDeducted = target.deductPP(move.id, 3);
        if (!ppDeducted)
          return;
        this.add("-activate", target, "move: Eerie Spell", move.name, ppDeducted);
      }
    },
    target: "normal",
    type: "Psychic"
  },
  eggbomb: {
    num: 121,
    accuracy: 75,
    basePower: 100,
    category: "Physical",
    isNonstandard: "Past",
    name: "Egg Bomb",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  electricterrain: {
    num: 604,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Electric Terrain",
    pp: 10,
    priority: 0,
    flags: { nonsky: 1, metronome: 1 },
    terrain: "electricterrain",
    condition: {
      duration: 5,
      durationCallback(source, effect) {
        if (source?.hasItem("terrainextender")) {
          return 8;
        }
        return 5;
      },
      onSetStatus(status, target, source, effect) {
        if (status.id === "slp" && target.isGrounded() && !target.isSemiInvulnerable()) {
          if (effect.id === "yawn" || effect.effectType === "Move" && !effect.secondaries) {
            this.add("-activate", target, "move: Electric Terrain");
          }
          return false;
        }
      },
      onTryAddVolatile(status, target) {
        if (!target.isGrounded() || target.isSemiInvulnerable())
          return;
        if (status.id === "yawn") {
          this.add("-activate", target, "move: Electric Terrain");
          return null;
        }
      },
      onBasePowerPriority: 6,
      onBasePower(basePower, attacker, defender, move) {
        if (move.type === "Electric" && attacker.isGrounded() && !attacker.isSemiInvulnerable()) {
          this.debug("electric terrain boost");
          return this.chainModify([5325, 4096]);
        }
      },
      onFieldStart(field, source, effect) {
        if (effect?.effectType === "Ability") {
          this.add("-fieldstart", "move: Electric Terrain", "[from] ability: " + effect.name, "[of] " + source);
        } else {
          this.add("-fieldstart", "move: Electric Terrain");
        }
      },
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 7,
      onFieldEnd() {
        this.add("-fieldend", "move: Electric Terrain");
      }
    },
    secondary: null,
    target: "all",
    type: "Electric",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  electrify: {
    num: 582,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Electrify",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, allyanim: 1, metronome: 1 },
    volatileStatus: "electrify",
    onTryHit(target) {
      if (!this.queue.willMove(target) && target.activeTurns)
        return false;
    },
    condition: {
      duration: 1,
      onStart(target) {
        this.add("-singleturn", target, "move: Electrify");
      },
      onModifyTypePriority: -2,
      onModifyType(move) {
        if (move.id !== "struggle") {
          this.debug("Electrify making move type electric");
          move.type = "Electric";
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Electric",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  electroball: {
    num: 486,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      let ratio = Math.floor(pokemon.getStat("spe") / target.getStat("spe"));
      if (!isFinite(ratio))
        ratio = 0;
      const bp = [40, 60, 80, 120, 150][Math.min(ratio, 4)];
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Special",
    name: "Electro Ball",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: null,
    target: "normal",
    type: "Electric",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Cool"
  },
  electrodrift: {
    num: 879,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Electro Drift",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    onBasePower(basePower, source, target, move) {
      if (target.runEffectiveness(move) > 0) {
        this.debug(`electro drift super effective buff`);
        return this.chainModify([5461, 4096]);
      }
    },
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  electroshot: {
    num: 905,
    accuracy: 100,
    basePower: 130,
    category: "Special",
    name: "Electro Shot",
    pp: 10,
    priority: 0,
    flags: { charge: 1, protect: 1, mirror: 1, metronome: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      this.boost({ spa: 1 }, attacker, attacker, move);
      if (["raindance", "primordialsea"].includes(attacker.effectiveWeather())) {
        this.attrLastMove("[still]");
        this.addMove("-anim", attacker, move.name, defender);
        return;
      }
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    secondary: null,
    hasSheerForce: true,
    target: "normal",
    type: "Electric"
  },
  electroweb: {
    num: 527,
    accuracy: 95,
    basePower: 55,
    category: "Special",
    name: "Electroweb",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Electric",
    contestType: "Beautiful"
  },
  embargo: {
    num: 373,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Embargo",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    volatileStatus: "embargo",
    condition: {
      duration: 5,
      onStart(pokemon) {
        this.add("-start", pokemon, "Embargo");
        this.singleEvent("End", pokemon.getItem(), pokemon.itemState, pokemon);
      },
      // Item suppression implemented in Pokemon.ignoringItem() within sim/pokemon.js
      onResidualOrder: 21,
      onEnd(pokemon) {
        this.add("-end", pokemon, "Embargo");
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  ember: {
    num: 52,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Ember",
    pp: 25,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Cute"
  },
  encore: {
    num: 227,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Encore",
    pp: 5,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1, failencore: 1 },
    volatileStatus: "encore",
    condition: {
      duration: 3,
      noCopy: true,
      // doesn't get copied by Z-Baton Pass
      onStart(target) {
        let move = target.lastMove;
        if (!move || target.volatiles["dynamax"])
          return false;
        if (move.isMax && move.baseMove)
          move = this.dex.moves.get(move.baseMove);
        const moveIndex = target.moves.indexOf(move.id);
        if (move.isZ || move.flags["failencore"] || !target.moveSlots[moveIndex] || target.moveSlots[moveIndex].pp <= 0) {
          return false;
        }
        this.effectState.move = move.id;
        this.add("-start", target, "Encore");
        if (!this.queue.willMove(target)) {
          this.effectState.duration++;
        }
      },
      onOverrideAction(pokemon, target, move) {
        if (move.id !== this.effectState.move)
          return this.effectState.move;
      },
      onResidualOrder: 16,
      onResidual(target) {
        if (!target.moves.includes(this.effectState.move) || target.moveSlots[target.moves.indexOf(this.effectState.move)].pp <= 0) {
          target.removeVolatile("encore");
        }
      },
      onEnd(target) {
        this.add("-end", target, "Encore");
      },
      onDisableMove(pokemon) {
        if (!this.effectState.move || !pokemon.hasMove(this.effectState.move)) {
          return;
        }
        for (const moveSlot of pokemon.moveSlots) {
          if (moveSlot.id !== this.effectState.move) {
            pokemon.disableMove(moveSlot.id);
          }
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Cute"
  },
  endeavor: {
    num: 283,
    accuracy: 100,
    basePower: 0,
    damageCallback(pokemon, target) {
      return target.getUndynamaxedHP() - pokemon.hp;
    },
    category: "Physical",
    name: "Endeavor",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, noparentalbond: 1 },
    onTryImmunity(target, pokemon) {
      return pokemon.hp < target.hp;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Tough"
  },
  endure: {
    num: 203,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Endure",
    pp: 10,
    priority: 4,
    flags: { noassist: 1, failcopycat: 1 },
    stallingMove: true,
    volatileStatus: "endure",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    onHit(pokemon) {
      pokemon.addVolatile("stall");
    },
    condition: {
      duration: 1,
      onStart(target) {
        this.add("-singleturn", target, "move: Endure");
      },
      onDamagePriority: -10,
      onDamage(damage, target, source, effect) {
        if (effect?.effectType === "Move" && damage >= target.hp) {
          this.add("-activate", target, "move: Endure");
          return target.hp - 1;
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Tough"
  },
  energyball: {
    num: 412,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Energy Ball",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Grass",
    contestType: "Beautiful"
  },
  entrainment: {
    num: 494,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Entrainment",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onTryHit(target, source) {
      if (target === source || target.volatiles["dynamax"])
        return false;
      if (target.ability === source.ability || target.getAbility().flags["cantsuppress"] || target.ability === "truant" || source.getAbility().flags["noentrain"]) {
        return false;
      }
    },
    onHit(target, source) {
      const oldAbility = target.setAbility(source.ability);
      if (oldAbility) {
        this.add("-ability", target, target.getAbility().name, "[from] move: Entrainment");
        if (!target.isAlly(source))
          target.volatileStaleness = "external";
        return;
      }
      return oldAbility;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spd: 1 } },
    contestType: "Cute"
  },
  eruption: {
    num: 284,
    accuracy: 100,
    basePower: 150,
    basePowerCallback(pokemon, target, move) {
      const bp = move.basePower * pokemon.hp / pokemon.maxhp;
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Special",
    name: "Eruption",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Fire",
    contestType: "Beautiful"
  },
  esperwing: {
    num: 840,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Esper Wing",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: {
      chance: 100,
      self: {
        boosts: {
          spe: 1
        }
      }
    },
    target: "normal",
    type: "Psychic"
  },
  eternabeam: {
    num: 795,
    accuracy: 90,
    basePower: 160,
    category: "Special",
    isNonstandard: "Past",
    name: "Eternabeam",
    pp: 5,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Dragon"
  },
  expandingforce: {
    num: 797,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Expanding Force",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, source) {
      if (this.field.isTerrain("psychicterrain") && source.isGrounded()) {
        this.debug("terrain buff");
        return this.chainModify(1.5);
      }
    },
    onModifyMove(move, source, target) {
      if (this.field.isTerrain("psychicterrain") && source.isGrounded()) {
        move.target = "allAdjacentFoes";
      }
    },
    secondary: null,
    target: "normal",
    type: "Psychic"
  },
  explosion: {
    num: 153,
    accuracy: 100,
    basePower: 250,
    category: "Physical",
    name: "Explosion",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, noparentalbond: 1 },
    selfdestruct: "always",
    secondary: null,
    target: "allAdjacent",
    type: "Normal",
    contestType: "Beautiful"
  },
  extrasensory: {
    num: 326,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Extrasensory",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  extremeevoboost: {
    num: 702,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Extreme Evoboost",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "eeviumz",
    boosts: {
      atk: 2,
      def: 2,
      spa: 2,
      spd: 2,
      spe: 2
    },
    secondary: null,
    target: "self",
    type: "Normal",
    contestType: "Beautiful"
  },
  extremespeed: {
    num: 245,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Extreme Speed",
    pp: 5,
    priority: 2,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  facade: {
    num: 263,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Facade",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, pokemon) {
      if (pokemon.status && pokemon.status !== "slp") {
        return this.chainModify(2);
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  fairylock: {
    num: 587,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Fairy Lock",
    pp: 10,
    priority: 0,
    flags: { mirror: 1, bypasssub: 1, metronome: 1 },
    pseudoWeather: "fairylock",
    condition: {
      duration: 2,
      onFieldStart(target) {
        this.add("-fieldactivate", "move: Fairy Lock");
      },
      onTrapPokemon(pokemon) {
        pokemon.tryTrap();
      }
    },
    secondary: null,
    target: "all",
    type: "Fairy",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  fairywind: {
    num: 584,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Fairy Wind",
    pp: 30,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Beautiful"
  },
  fakeout: {
    num: 252,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Fake Out",
    pp: 10,
    priority: 3,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onTry(source) {
      if (source.activeMoveActions > 1) {
        this.hint("Fake Out only works on your first turn out.");
        return false;
      }
    },
    secondary: {
      chance: 100,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  faketears: {
    num: 313,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Fake Tears",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    boosts: {
      spd: -2
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { boost: { spa: 1 } },
    contestType: "Cute"
  },
  falsesurrender: {
    num: 793,
    accuracy: true,
    basePower: 80,
    category: "Physical",
    name: "False Surrender",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Dark"
  },
  falseswipe: {
    num: 206,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "False Swipe",
    pp: 40,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onDamagePriority: -20,
    onDamage(damage, target, source, effect) {
      if (damage >= target.hp)
        return target.hp - 1;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  featherdance: {
    num: 297,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Feather Dance",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, dance: 1, allyanim: 1, metronome: 1 },
    boosts: {
      atk: -2
    },
    secondary: null,
    target: "normal",
    type: "Flying",
    zMove: { boost: { def: 1 } },
    contestType: "Beautiful"
  },
  feint: {
    num: 364,
    accuracy: 100,
    basePower: 30,
    category: "Physical",
    name: "Feint",
    pp: 10,
    priority: 2,
    flags: { mirror: 1, noassist: 1, failcopycat: 1 },
    breaksProtect: true,
    // Breaking protection implemented in scripts.js
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Clever"
  },
  feintattack: {
    num: 185,
    accuracy: true,
    basePower: 60,
    category: "Physical",
    isNonstandard: "Past",
    name: "Feint Attack",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  fellstinger: {
    num: 565,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Fell Stinger",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onAfterMoveSecondarySelf(pokemon, target, move) {
      if (!target || target.fainted || target.hp <= 0)
        this.boost({ atk: 3 }, pokemon, pokemon, move);
    },
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cool"
  },
  ficklebeam: {
    num: 907,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Fickle Beam",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, pokemon) {
      if (this.randomChance(3, 10)) {
        this.attrLastMove("[anim] Fickle Beam All Out");
        this.add("-activate", pokemon, "move: Fickle Beam");
        return this.chainModify(2);
      }
    },
    secondary: null,
    target: "normal",
    type: "Dragon"
  },
  fierydance: {
    num: 552,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Fiery Dance",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, dance: 1, metronome: 1 },
    secondary: {
      chance: 50,
      self: {
        boosts: {
          spa: 1
        }
      }
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  fierywrath: {
    num: 822,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Fiery Wrath",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "flinch"
    },
    target: "allAdjacentFoes",
    type: "Dark"
  },
  filletaway: {
    num: 868,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Fillet Away",
    pp: 10,
    priority: 0,
    flags: { snatch: 1 },
    onTry(source) {
      if (source.hp <= source.maxhp / 2 || source.maxhp === 1)
        return false;
    },
    onTryHit(pokemon, target, move) {
      if (!this.boost(move.boosts))
        return null;
      delete move.boosts;
    },
    onHit(pokemon) {
      this.directDamage(pokemon.maxhp / 2);
    },
    boosts: {
      atk: 2,
      spa: 2,
      spe: 2
    },
    secondary: null,
    target: "self",
    type: "Normal"
  },
  finalgambit: {
    num: 515,
    accuracy: 100,
    basePower: 0,
    damageCallback(pokemon) {
      const damage = pokemon.hp;
      pokemon.faint();
      return damage;
    },
    selfdestruct: "ifHit",
    category: "Special",
    name: "Final Gambit",
    pp: 5,
    priority: 0,
    flags: { protect: 1, metronome: 1, noparentalbond: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    zMove: { basePower: 180 },
    contestType: "Tough"
  },
  fireblast: {
    num: 126,
    accuracy: 85,
    basePower: 110,
    category: "Special",
    name: "Fire Blast",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  firefang: {
    num: 424,
    accuracy: 95,
    basePower: 65,
    category: "Physical",
    name: "Fire Fang",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    secondaries: [
      {
        chance: 10,
        status: "brn"
      },
      {
        chance: 10,
        volatileStatus: "flinch"
      }
    ],
    target: "normal",
    type: "Fire",
    contestType: "Cool"
  },
  firelash: {
    num: 680,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Fire Lash",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Fire",
    contestType: "Cute"
  },
  firepledge: {
    num: 519,
    accuracy: 100,
    basePower: 80,
    basePowerCallback(target, source, move) {
      if (["grasspledge", "waterpledge"].includes(move.sourceEffect)) {
        this.add("-combine");
        return 150;
      }
      return move.basePower;
    },
    category: "Special",
    name: "Fire Pledge",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1, pledgecombo: 1 },
    onPrepareHit(target, source, move) {
      for (const action of this.queue.list) {
        if (!action.move || !action.pokemon?.isActive || action.pokemon.fainted || action.maxMove || action.zmove) {
          continue;
        }
        if (action.pokemon.isAlly(source) && ["grasspledge", "waterpledge"].includes(action.move.id)) {
          this.queue.prioritizeAction(action, move);
          this.add("-waiting", source, action.pokemon);
          return null;
        }
      }
    },
    onModifyMove(move) {
      if (move.sourceEffect === "waterpledge") {
        move.type = "Water";
        move.forceSTAB = true;
        move.self = { sideCondition: "waterpledge" };
      }
      if (move.sourceEffect === "grasspledge") {
        move.type = "Fire";
        move.forceSTAB = true;
        move.sideCondition = "firepledge";
      }
    },
    condition: {
      duration: 4,
      onSideStart(targetSide) {
        this.add("-sidestart", targetSide, "Fire Pledge");
      },
      onResidualOrder: 5,
      onResidualSubOrder: 1,
      onResidual(pokemon) {
        if (!pokemon.hasType("Fire"))
          this.damage(pokemon.baseMaxhp / 8, pokemon);
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 8,
      onSideEnd(targetSide) {
        this.add("-sideend", targetSide, "Fire Pledge");
      }
    },
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  firepunch: {
    num: 7,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Fire Punch",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Tough"
  },
  firespin: {
    num: 83,
    accuracy: 85,
    basePower: 35,
    category: "Special",
    name: "Fire Spin",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  firstimpression: {
    num: 660,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "First Impression",
    pp: 10,
    priority: 2,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onTry(source) {
      if (source.activeMoveActions > 1) {
        this.hint("First Impression only works on your first turn out.");
        return false;
      }
    },
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cute"
  },
  fishiousrend: {
    num: 755,
    accuracy: 100,
    basePower: 85,
    basePowerCallback(pokemon, target, move) {
      if (target.newlySwitched || this.queue.willMove(target)) {
        this.debug("Fishious Rend damage boost");
        return move.basePower * 2;
      }
      this.debug("Fishious Rend NOT boosted");
      return move.basePower;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Fishious Rend",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    secondary: null,
    target: "normal",
    type: "Water"
  },
  fissure: {
    num: 90,
    accuracy: 30,
    basePower: 0,
    category: "Physical",
    name: "Fissure",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    ohko: true,
    secondary: null,
    target: "normal",
    type: "Ground",
    zMove: { basePower: 180 },
    maxMove: { basePower: 130 },
    contestType: "Tough"
  },
  flail: {
    num: 175,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      const ratio = Math.max(Math.floor(pokemon.hp * 48 / pokemon.maxhp), 1);
      let bp;
      if (ratio < 2) {
        bp = 200;
      } else if (ratio < 5) {
        bp = 150;
      } else if (ratio < 10) {
        bp = 100;
      } else if (ratio < 17) {
        bp = 80;
      } else if (ratio < 33) {
        bp = 40;
      } else {
        bp = 20;
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Flail",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Cute"
  },
  flameburst: {
    num: 481,
    accuracy: 100,
    basePower: 70,
    category: "Special",
    isNonstandard: "Past",
    name: "Flame Burst",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onHit(target, source, move) {
      for (const ally of target.adjacentAllies()) {
        this.damage(ally.baseMaxhp / 16, ally, source, this.dex.conditions.get("Flame Burst"));
      }
    },
    onAfterSubDamage(damage, target, source, move) {
      for (const ally of target.adjacentAllies()) {
        this.damage(ally.baseMaxhp / 16, ally, source, this.dex.conditions.get("Flame Burst"));
      }
    },
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  flamecharge: {
    num: 488,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Flame Charge",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          spe: 1
        }
      }
    },
    target: "normal",
    type: "Fire",
    contestType: "Cool"
  },
  flamewheel: {
    num: 172,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Flame Wheel",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, defrost: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  flamethrower: {
    num: 53,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Flamethrower",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  flareblitz: {
    num: 394,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Flare Blitz",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, defrost: 1, metronome: 1 },
    recoil: [33, 100],
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Cool"
  },
  flash: {
    num: 148,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Flash",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      accuracy: -1
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { evasion: 1 } },
    contestType: "Beautiful"
  },
  flashcannon: {
    num: 430,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Flash Cannon",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Steel",
    contestType: "Beautiful"
  },
  flatter: {
    num: 260,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Flatter",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    volatileStatus: "confusion",
    boosts: {
      spa: 1
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  fleurcannon: {
    num: 705,
    accuracy: 90,
    basePower: 130,
    category: "Special",
    name: "Fleur Cannon",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    self: {
      boosts: {
        spa: -2
      }
    },
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Beautiful"
  },
  fling: {
    num: 374,
    accuracy: 100,
    basePower: 0,
    category: "Physical",
    name: "Fling",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, allyanim: 1, metronome: 1, noparentalbond: 1 },
    onPrepareHit(target, source, move) {
      if (source.ignoringItem())
        return false;
      const item = source.getItem();
      if (!this.singleEvent("TakeItem", item, source.itemState, source, source, move, item))
        return false;
      if (!item.fling)
        return false;
      move.basePower = item.fling.basePower;
      this.debug("BP: " + move.basePower);
      if (item.isBerry) {
        move.onHit = function(foe) {
          if (this.singleEvent("Eat", item, null, foe, null, null)) {
            this.runEvent("EatItem", foe, null, null, item);
            if (item.id === "leppaberry")
              foe.staleness = "external";
          }
          if (item.onEat)
            foe.ateBerry = true;
        };
      } else if (item.fling.effect) {
        move.onHit = item.fling.effect;
      } else {
        if (!move.secondaries)
          move.secondaries = [];
        if (item.fling.status) {
          move.secondaries.push({ status: item.fling.status });
        } else if (item.fling.volatileStatus) {
          move.secondaries.push({ volatileStatus: item.fling.volatileStatus });
        }
      }
      source.addVolatile("fling");
    },
    condition: {
      onUpdate(pokemon) {
        const item = pokemon.getItem();
        pokemon.setItem("");
        pokemon.lastItem = item.id;
        pokemon.usedItemThisTurn = true;
        this.add("-enditem", pokemon, item.name, "[from] move: Fling");
        this.runEvent("AfterUseItem", pokemon, null, null, item);
        pokemon.removeVolatile("fling");
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Cute"
  },
  flipturn: {
    num: 812,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Flip Turn",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    selfSwitch: true,
    secondary: null,
    target: "normal",
    type: "Water"
  },
  floatyfall: {
    num: 731,
    accuracy: 95,
    basePower: 90,
    category: "Physical",
    isNonstandard: "LGPE",
    name: "Floaty Fall",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, gravity: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Flying",
    contestType: "Cool"
  },
  floralhealing: {
    num: 666,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Floral Healing",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, heal: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      let success = false;
      if (this.field.isTerrain("grassyterrain")) {
        success = !!this.heal(this.modify(target.baseMaxhp, 0.667));
      } else {
        success = !!this.heal(Math.ceil(target.baseMaxhp * 0.5));
      }
      if (success && !target.isAlly(source)) {
        target.staleness = "external";
      }
      if (!success) {
        this.add("-fail", target, "heal");
        return this.NOT_FAIL;
      }
      return success;
    },
    secondary: null,
    target: "normal",
    type: "Fairy",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  flowershield: {
    num: 579,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Flower Shield",
    pp: 10,
    priority: 0,
    flags: { distance: 1, metronome: 1 },
    onHitField(t, source, move) {
      const targets = [];
      for (const pokemon of this.getAllActive()) {
        if (pokemon.hasType("Grass") && (!pokemon.volatiles["maxguard"] || this.runEvent("TryHit", pokemon, source, move))) {
          targets.push(pokemon);
        }
      }
      let success = false;
      for (const target of targets) {
        success = this.boost({ def: 1 }, target, source, move) || success;
      }
      return success;
    },
    secondary: null,
    target: "all",
    type: "Fairy",
    zMove: { boost: { def: 1 } },
    contestType: "Beautiful"
  },
  flowertrick: {
    num: 870,
    accuracy: true,
    basePower: 70,
    category: "Physical",
    name: "Flower Trick",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    willCrit: true,
    secondary: null,
    target: "normal",
    type: "Grass"
  },
  fly: {
    num: 19,
    accuracy: 95,
    basePower: 90,
    category: "Physical",
    name: "Fly",
    pp: 15,
    priority: 0,
    flags: {
      contact: 1,
      charge: 1,
      protect: 1,
      mirror: 1,
      gravity: 1,
      distance: 1,
      metronome: 1,
      nosleeptalk: 1,
      noassist: 1,
      failinstruct: 1
    },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    condition: {
      duration: 2,
      onInvulnerability(target, source, move) {
        if (["gust", "twister", "skyuppercut", "thunder", "hurricane", "smackdown", "thousandarrows"].includes(move.id)) {
          return;
        }
        return false;
      },
      onSourceModifyDamage(damage, source, target, move) {
        if (move.id === "gust" || move.id === "twister") {
          return this.chainModify(2);
        }
      }
    },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Clever"
  },
  flyingpress: {
    num: 560,
    accuracy: 95,
    basePower: 100,
    category: "Physical",
    name: "Flying Press",
    pp: 10,
    flags: { contact: 1, protect: 1, mirror: 1, gravity: 1, distance: 1, nonsky: 1, metronome: 1 },
    onEffectiveness(typeMod, target, type, move) {
      return typeMod + this.dex.getEffectiveness("Flying", type);
    },
    priority: 0,
    secondary: null,
    target: "any",
    type: "Fighting",
    zMove: { basePower: 170 },
    contestType: "Tough"
  },
  focusblast: {
    num: 411,
    accuracy: 70,
    basePower: 120,
    category: "Special",
    name: "Focus Blast",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  focusenergy: {
    num: 116,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Focus Energy",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    volatileStatus: "focusenergy",
    condition: {
      onStart(target, source, effect) {
        if (target.volatiles["dragoncheer"])
          return false;
        if (effect?.id === "zpower") {
          this.add("-start", target, "move: Focus Energy", "[zeffect]");
        } else if (effect && ["costar", "imposter", "psychup", "transform"].includes(effect.id)) {
          this.add("-start", target, "move: Focus Energy", "[silent]");
        } else {
          this.add("-start", target, "move: Focus Energy");
        }
      },
      onModifyCritRatio(critRatio) {
        return critRatio + 2;
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { accuracy: 1 } },
    contestType: "Cool"
  },
  focuspunch: {
    num: 264,
    accuracy: 100,
    basePower: 150,
    category: "Physical",
    name: "Focus Punch",
    pp: 20,
    priority: -3,
    flags: { contact: 1, protect: 1, punch: 1, failmefirst: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failinstruct: 1 },
    priorityChargeCallback(pokemon) {
      pokemon.addVolatile("focuspunch");
    },
    beforeMoveCallback(pokemon) {
      if (pokemon.volatiles["focuspunch"]?.lostFocus) {
        this.add("cant", pokemon, "Focus Punch", "Focus Punch");
        return true;
      }
    },
    condition: {
      duration: 1,
      onStart(pokemon) {
        this.add("-singleturn", pokemon, "move: Focus Punch");
      },
      onHit(pokemon, source, move) {
        if (move.category !== "Status") {
          this.effectState.lostFocus = true;
        }
      },
      onTryAddVolatile(status, pokemon) {
        if (status.id === "flinch")
          return null;
      }
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  followme: {
    num: 266,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Follow Me",
    pp: 20,
    priority: 2,
    flags: { noassist: 1, failcopycat: 1 },
    volatileStatus: "followme",
    onTry(source) {
      return this.activePerHalf > 1;
    },
    condition: {
      duration: 1,
      onStart(target, source, effect) {
        if (effect?.id === "zpower") {
          this.add("-singleturn", target, "move: Follow Me", "[zeffect]");
        } else {
          this.add("-singleturn", target, "move: Follow Me");
        }
      },
      onFoeRedirectTargetPriority: 1,
      onFoeRedirectTarget(target, source, source2, move) {
        if (!this.effectState.target.isSkyDropped() && this.validTarget(this.effectState.target, source, move.target)) {
          if (move.smartTarget)
            move.smartTarget = false;
          this.debug("Follow Me redirected target of move");
          return this.effectState.target;
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  forcepalm: {
    num: 395,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Force Palm",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  foresight: {
    num: 193,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Foresight",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    volatileStatus: "foresight",
    onTryHit(target) {
      if (target.volatiles["miracleeye"])
        return false;
    },
    condition: {
      noCopy: true,
      onStart(pokemon) {
        this.add("-start", pokemon, "Foresight");
      },
      onNegateImmunity(pokemon, type) {
        if (pokemon.hasType("Ghost") && ["Normal", "Fighting"].includes(type))
          return false;
      },
      onModifyBoost(boosts) {
        if (boosts.evasion && boosts.evasion > 0) {
          boosts.evasion = 0;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { effect: "crit2" },
    contestType: "Clever"
  },
  forestscurse: {
    num: 571,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Forest's Curse",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onHit(target) {
      if (target.hasType("Grass"))
        return false;
      if (!target.addType("Grass"))
        return false;
      this.add("-start", target, "typeadd", "Grass", "[from] move: Forest's Curse");
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Clever"
  },
  foulplay: {
    num: 492,
    accuracy: 100,
    basePower: 95,
    category: "Physical",
    name: "Foul Play",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    overrideOffensivePokemon: "target",
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  freezedry: {
    num: 573,
    accuracy: 100,
    basePower: 70,
    category: "Special",
    name: "Freeze-Dry",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onEffectiveness(typeMod, target, type) {
      if (type === "Water")
        return 1;
    },
    secondary: {
      chance: 10,
      status: "frz"
    },
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  freezeshock: {
    num: 553,
    accuracy: 90,
    basePower: 140,
    category: "Physical",
    name: "Freeze Shock",
    pp: 5,
    priority: 0,
    flags: { charge: 1, protect: 1, mirror: 1, nosleeptalk: 1, failinstruct: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  freezingglare: {
    num: 821,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Freezing Glare",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 10,
      status: "frz"
    },
    target: "normal",
    type: "Psychic"
  },
  freezyfrost: {
    num: 739,
    accuracy: 90,
    basePower: 100,
    category: "Special",
    isNonstandard: "LGPE",
    name: "Freezy Frost",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    onHit() {
      this.add("-clearallboost");
      for (const pokemon of this.getAllActive()) {
        pokemon.clearBoosts();
      }
    },
    secondary: null,
    target: "normal",
    type: "Ice",
    contestType: "Clever"
  },
  frenzyplant: {
    num: 338,
    accuracy: 90,
    basePower: 150,
    category: "Special",
    name: "Frenzy Plant",
    pp: 5,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  frostbreath: {
    num: 524,
    accuracy: 90,
    basePower: 60,
    category: "Special",
    name: "Frost Breath",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    willCrit: true,
    secondary: null,
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  frustration: {
    num: 218,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon) {
      return Math.floor((255 - pokemon.happiness) * 10 / 25) || 1;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Frustration",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Cute"
  },
  furyattack: {
    num: 31,
    accuracy: 85,
    basePower: 15,
    category: "Physical",
    name: "Fury Attack",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  furycutter: {
    num: 210,
    accuracy: 95,
    basePower: 40,
    basePowerCallback(pokemon, target, move) {
      if (!pokemon.volatiles["furycutter"] || move.hit === 1) {
        pokemon.addVolatile("furycutter");
      }
      const bp = this.clampIntRange(move.basePower * pokemon.volatiles["furycutter"].multiplier, 1, 160);
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Fury Cutter",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    condition: {
      duration: 2,
      onStart() {
        this.effectState.multiplier = 1;
      },
      onRestart() {
        if (this.effectState.multiplier < 4) {
          this.effectState.multiplier <<= 1;
        }
        this.effectState.duration = 2;
      }
    },
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cool"
  },
  furyswipes: {
    num: 154,
    accuracy: 80,
    basePower: 18,
    category: "Physical",
    name: "Fury Swipes",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Normal",
    maxMove: { basePower: 100 },
    contestType: "Tough"
  },
  fusionbolt: {
    num: 559,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Fusion Bolt",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, pokemon) {
      if (this.lastSuccessfulMoveThisTurn === "fusionflare") {
        this.debug("double power");
        return this.chainModify(2);
      }
    },
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  fusionflare: {
    num: 558,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Fusion Flare",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1, metronome: 1 },
    onBasePower(basePower, pokemon) {
      if (this.lastSuccessfulMoveThisTurn === "fusionbolt") {
        this.debug("double power");
        return this.chainModify(2);
      }
    },
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  futuresight: {
    num: 248,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    name: "Future Sight",
    pp: 10,
    priority: 0,
    flags: { allyanim: 1, metronome: 1, futuremove: 1 },
    ignoreImmunity: true,
    onTry(source, target) {
      if (!target.side.addSlotCondition(target, "futuremove"))
        return false;
      Object.assign(target.side.slotConditions[target.position]["futuremove"], {
        duration: 3,
        move: "futuresight",
        source,
        moveData: {
          id: "futuresight",
          name: "Future Sight",
          accuracy: 100,
          basePower: 120,
          category: "Special",
          priority: 0,
          flags: { allyanim: 1, metronome: 1, futuremove: 1 },
          ignoreImmunity: false,
          effectType: "Move",
          type: "Psychic"
        }
      });
      this.add("-start", source, "move: Future Sight");
      return this.NOT_FAIL;
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  gastroacid: {
    num: 380,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Gastro Acid",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    volatileStatus: "gastroacid",
    onTryHit(target) {
      if (target.getAbility().flags["cantsuppress"]) {
        return false;
      }
      if (target.hasItem("Ability Shield")) {
        this.add("-block", target, "item: Ability Shield");
        return null;
      }
    },
    condition: {
      // Ability suppression implemented in Pokemon.ignoringAbility() within sim/pokemon.ts
      onStart(pokemon) {
        if (pokemon.hasItem("Ability Shield"))
          return false;
        this.add("-endability", pokemon);
        this.singleEvent("End", pokemon.getAbility(), pokemon.abilityState, pokemon, pokemon, "gastroacid");
      },
      onCopy(pokemon) {
        if (pokemon.getAbility().flags["cantsuppress"])
          pokemon.removeVolatile("gastroacid");
      }
    },
    secondary: null,
    target: "normal",
    type: "Poison",
    zMove: { boost: { spe: 1 } },
    contestType: "Tough"
  },
  geargrind: {
    num: 544,
    accuracy: 85,
    basePower: 50,
    category: "Physical",
    isNonstandard: "Past",
    name: "Gear Grind",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: 2,
    secondary: null,
    target: "normal",
    type: "Steel",
    zMove: { basePower: 180 },
    maxMove: { basePower: 130 },
    contestType: "Clever"
  },
  gearup: {
    num: 674,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Gear Up",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, bypasssub: 1, metronome: 1 },
    onHitSide(side, source, move) {
      const targets = side.allies().filter((target) => target.hasAbility(["plus", "minus"]) && (!target.volatiles["maxguard"] || this.runEvent("TryHit", target, source, move)));
      if (!targets.length)
        return false;
      let didSomething = false;
      for (const target of targets) {
        didSomething = this.boost({ atk: 1, spa: 1 }, target, source, move, false, true) || didSomething;
      }
      return didSomething;
    },
    secondary: null,
    target: "allySide",
    type: "Steel",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  genesissupernova: {
    num: 703,
    accuracy: true,
    basePower: 185,
    category: "Special",
    isNonstandard: "Past",
    name: "Genesis Supernova",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "mewniumz",
    secondary: {
      chance: 100,
      self: {
        onHit() {
          this.field.setTerrain("psychicterrain");
        }
      }
    },
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  geomancy: {
    num: 601,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Geomancy",
    pp: 10,
    priority: 0,
    flags: { charge: 1, nonsky: 1, metronome: 1, nosleeptalk: 1, failinstruct: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    boosts: {
      spa: 2,
      spd: 2,
      spe: 2
    },
    secondary: null,
    target: "self",
    type: "Fairy",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Beautiful"
  },
  gigadrain: {
    num: 202,
    accuracy: 100,
    basePower: 75,
    category: "Special",
    name: "Giga Drain",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Clever"
  },
  gigaimpact: {
    num: 416,
    accuracy: 90,
    basePower: 150,
    category: "Physical",
    name: "Giga Impact",
    pp: 5,
    priority: 0,
    flags: { contact: 1, recharge: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  gigatonhammer: {
    num: 893,
    accuracy: 100,
    basePower: 160,
    category: "Physical",
    name: "Gigaton Hammer",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, cantusetwice: 1 },
    secondary: null,
    target: "normal",
    type: "Steel"
  },
  gigavolthavoc: {
    num: 646,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Gigavolt Havoc",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "electriumz",
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  glaciallance: {
    num: 824,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Glacial Lance",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Ice"
  },
  glaciate: {
    num: 549,
    accuracy: 95,
    basePower: 65,
    category: "Special",
    name: "Glaciate",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Ice",
    contestType: "Beautiful"
  },
  glaiverush: {
    num: 862,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Glaive Rush",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      volatileStatus: "glaiverush"
    },
    condition: {
      noCopy: true,
      onStart(pokemon) {
        this.add("-singlemove", pokemon, "Glaive Rush", "[silent]");
      },
      onAccuracy() {
        return true;
      },
      onSourceModifyDamage() {
        return this.chainModify(2);
      },
      onBeforeMovePriority: 100,
      onBeforeMove(pokemon) {
        this.debug("removing Glaive Rush drawback before attack");
        pokemon.removeVolatile("glaiverush");
      }
    },
    secondary: null,
    target: "normal",
    type: "Dragon"
  },
  glare: {
    num: 137,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Glare",
    pp: 30,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    status: "par",
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spd: 1 } },
    contestType: "Tough"
  },
  glitzyglow: {
    num: 736,
    accuracy: 95,
    basePower: 80,
    category: "Special",
    isNonstandard: "LGPE",
    name: "Glitzy Glow",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    self: {
      sideCondition: "lightscreen"
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  gmaxbefuddle: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Befuddle",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Butterfree",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          const result = this.random(3);
          if (result === 0) {
            pokemon.trySetStatus("slp", source);
          } else if (result === 1) {
            pokemon.trySetStatus("par", source);
          } else {
            pokemon.trySetStatus("psn", source);
          }
        }
      }
    },
    target: "adjacentFoe",
    type: "Bug",
    contestType: "Cool"
  },
  gmaxcannonade: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Cannonade",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Blastoise",
    self: {
      onHit(source) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("gmaxcannonade");
        }
      }
    },
    condition: {
      duration: 4,
      onSideStart(targetSide) {
        this.add("-sidestart", targetSide, "G-Max Cannonade");
      },
      onResidualOrder: 5,
      onResidualSubOrder: 1,
      onResidual(target) {
        if (!target.hasType("Water"))
          this.damage(target.baseMaxhp / 6, target);
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 11,
      onSideEnd(targetSide) {
        this.add("-sideend", targetSide, "G-Max Cannonade");
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Water",
    contestType: "Cool"
  },
  gmaxcentiferno: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Centiferno",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Centiskorch",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          pokemon.addVolatile("partiallytrapped", source, this.dex.getActiveMove("G-Max Centiferno"));
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Fire",
    contestType: "Cool"
  },
  gmaxchistrike: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Chi Strike",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Machamp",
    self: {
      onHit(source) {
        for (const pokemon of source.alliesAndSelf()) {
          pokemon.addVolatile("gmaxchistrike");
        }
      }
    },
    condition: {
      noCopy: true,
      onStart(target, source, effect) {
        this.effectState.layers = 1;
        if (!["costar", "imposter", "psychup", "transform"].includes(effect?.id)) {
          this.add("-start", target, "move: G-Max Chi Strike");
        }
      },
      onRestart(target, source, effect) {
        if (this.effectState.layers >= 3)
          return false;
        this.effectState.layers++;
        if (!["costar", "imposter", "psychup", "transform"].includes(effect?.id)) {
          this.add("-start", target, "move: G-Max Chi Strike");
        }
      },
      onModifyCritRatio(critRatio) {
        return critRatio + this.effectState.layers;
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Fighting",
    contestType: "Cool"
  },
  gmaxcuddle: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Cuddle",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Eevee",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          pokemon.addVolatile("attract");
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Normal",
    contestType: "Cool"
  },
  gmaxdepletion: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Depletion",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Duraludon",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          let move = pokemon.lastMove;
          if (!move || move.isZ)
            continue;
          if (move.isMax && move.baseMove)
            move = this.dex.moves.get(move.baseMove);
          const ppDeducted = pokemon.deductPP(move.id, 2);
          if (ppDeducted) {
            this.add("-activate", pokemon, "move: G-Max Depletion", move.name, ppDeducted);
          }
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Dragon",
    contestType: "Cool"
  },
  gmaxdrumsolo: {
    num: 1e3,
    accuracy: true,
    basePower: 160,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Drum Solo",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Rillaboom",
    ignoreAbility: true,
    secondary: null,
    target: "adjacentFoe",
    type: "Grass",
    contestType: "Cool"
  },
  gmaxfinale: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Finale",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Alcremie",
    self: {
      onHit(target, source, move) {
        for (const pokemon of source.alliesAndSelf()) {
          this.heal(pokemon.maxhp / 6, pokemon, source, move);
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Fairy",
    contestType: "Cool"
  },
  gmaxfireball: {
    num: 1e3,
    accuracy: true,
    basePower: 160,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Fireball",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Cinderace",
    ignoreAbility: true,
    secondary: null,
    target: "adjacentFoe",
    type: "Fire",
    contestType: "Cool"
  },
  gmaxfoamburst: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Foam Burst",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Kingler",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          this.boost({ spe: -2 }, pokemon);
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Water",
    contestType: "Cool"
  },
  gmaxgoldrush: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Gold Rush",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Meowth",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          pokemon.addVolatile("confusion");
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Normal",
    contestType: "Cool"
  },
  gmaxgravitas: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Gravitas",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Orbeetle",
    self: {
      pseudoWeather: "gravity"
    },
    target: "adjacentFoe",
    type: "Psychic",
    contestType: "Cool"
  },
  gmaxhydrosnipe: {
    num: 1e3,
    accuracy: true,
    basePower: 160,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Hydrosnipe",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Inteleon",
    ignoreAbility: true,
    secondary: null,
    target: "adjacentFoe",
    type: "Water",
    contestType: "Cool"
  },
  gmaxmalodor: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Malodor",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Garbodor",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          pokemon.trySetStatus("psn", source);
        }
      }
    },
    target: "adjacentFoe",
    type: "Poison",
    contestType: "Cool"
  },
  gmaxmeltdown: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Meltdown",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Melmetal",
    self: {
      onHit(source, target, effect) {
        for (const pokemon of source.foes()) {
          if (!pokemon.volatiles["dynamax"])
            pokemon.addVolatile("torment", source, effect);
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Steel",
    contestType: "Cool"
  },
  gmaxoneblow: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max One Blow",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Urshifu",
    secondary: null,
    target: "adjacentFoe",
    type: "Dark",
    contestType: "Cool"
  },
  gmaxrapidflow: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Rapid Flow",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Urshifu-Rapid-Strike",
    secondary: null,
    target: "adjacentFoe",
    type: "Water",
    contestType: "Cool"
  },
  gmaxreplenish: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Replenish",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Snorlax",
    self: {
      onHit(source) {
        if (this.random(2) === 0)
          return;
        for (const pokemon of source.alliesAndSelf()) {
          if (pokemon.item)
            continue;
          if (pokemon.lastItem && this.dex.items.get(pokemon.lastItem).isBerry) {
            const item = pokemon.lastItem;
            pokemon.lastItem = "";
            this.add("-item", pokemon, this.dex.items.get(item), "[from] move: G-Max Replenish");
            pokemon.setItem(item);
          }
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Normal",
    contestType: "Cool"
  },
  gmaxresonance: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Resonance",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Lapras",
    self: {
      sideCondition: "auroraveil"
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Ice",
    contestType: "Cool"
  },
  gmaxsandblast: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Sandblast",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Sandaconda",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          pokemon.addVolatile("partiallytrapped", source, this.dex.getActiveMove("G-Max Sandblast"));
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Ground",
    contestType: "Cool"
  },
  gmaxsmite: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Smite",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Hatterene",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          pokemon.addVolatile("confusion", source);
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Fairy",
    contestType: "Cool"
  },
  gmaxsnooze: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Snooze",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Grimmsnarl",
    onHit(target) {
      if (target.status || !target.runStatusImmunity("slp"))
        return;
      if (this.random(2) === 0)
        return;
      target.addVolatile("yawn");
    },
    onAfterSubDamage(damage, target) {
      if (target.status || !target.runStatusImmunity("slp"))
        return;
      if (this.random(2) === 0)
        return;
      target.addVolatile("yawn");
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Dark",
    contestType: "Cool"
  },
  gmaxsteelsurge: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Steelsurge",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Copperajah",
    self: {
      onHit(source) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("gmaxsteelsurge");
        }
      }
    },
    condition: {
      onSideStart(side) {
        this.add("-sidestart", side, "move: G-Max Steelsurge");
      },
      onEntryHazard(pokemon) {
        if (pokemon.hasItem("heavydutyboots"))
          return;
        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);
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Steel",
    contestType: "Cool"
  },
  gmaxstonesurge: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Stonesurge",
    pp: 5,
    priority: 0,
    flags: {},
    isMax: "Drednaw",
    self: {
      onHit(source) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("stealthrock");
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Water",
    contestType: "Cool"
  },
  gmaxstunshock: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Stun Shock",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Toxtricity",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          const result = this.random(2);
          if (result === 0) {
            pokemon.trySetStatus("par", source);
          } else {
            pokemon.trySetStatus("psn", source);
          }
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Electric",
    contestType: "Cool"
  },
  gmaxsweetness: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Sweetness",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Appletun",
    self: {
      onHit(source) {
        for (const ally of source.side.pokemon) {
          ally.cureStatus();
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Grass",
    contestType: "Cool"
  },
  gmaxtartness: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Tartness",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Flapple",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          this.boost({ evasion: -1 }, pokemon);
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Grass",
    contestType: "Cool"
  },
  gmaxterror: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Terror",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Gengar",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          pokemon.addVolatile("trapped", source, null, "trapper");
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Ghost",
    contestType: "Cool"
  },
  gmaxvinelash: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Vine Lash",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Venusaur",
    self: {
      onHit(source) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("gmaxvinelash");
        }
      }
    },
    condition: {
      duration: 4,
      onSideStart(targetSide) {
        this.add("-sidestart", targetSide, "G-Max Vine Lash");
      },
      onResidualOrder: 5,
      onResidualSubOrder: 1,
      onResidual(target) {
        if (!target.hasType("Grass"))
          this.damage(target.baseMaxhp / 6, target);
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 11,
      onSideEnd(targetSide) {
        this.add("-sideend", targetSide, "G-Max Vine Lash");
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Grass",
    contestType: "Cool"
  },
  gmaxvolcalith: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Volcalith",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Coalossal",
    self: {
      onHit(source) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("gmaxvolcalith");
        }
      }
    },
    condition: {
      duration: 4,
      onSideStart(targetSide) {
        this.add("-sidestart", targetSide, "G-Max Volcalith");
      },
      onResidualOrder: 5,
      onResidualSubOrder: 1,
      onResidual(target) {
        if (!target.hasType("Rock"))
          this.damage(target.baseMaxhp / 6, target);
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 11,
      onSideEnd(targetSide) {
        this.add("-sideend", targetSide, "G-Max Volcalith");
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Rock",
    contestType: "Cool"
  },
  gmaxvoltcrash: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Volt Crash",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Pikachu",
    self: {
      onHit(source) {
        for (const pokemon of source.foes()) {
          pokemon.trySetStatus("par", source);
        }
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Electric",
    contestType: "Cool"
  },
  gmaxwildfire: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Wildfire",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Charizard",
    self: {
      onHit(source) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("gmaxwildfire");
        }
      }
    },
    condition: {
      duration: 4,
      onSideStart(targetSide) {
        this.add("-sidestart", targetSide, "G-Max Wildfire");
      },
      onResidualOrder: 5,
      onResidualSubOrder: 1,
      onResidual(target) {
        if (!target.hasType("Fire"))
          this.damage(target.baseMaxhp / 6, target);
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 11,
      onSideEnd(targetSide) {
        this.add("-sideend", targetSide, "G-Max Wildfire");
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Fire",
    contestType: "Cool"
  },
  gmaxwindrage: {
    num: 1e3,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Gigantamax",
    name: "G-Max Wind Rage",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: "Corviknight",
    self: {
      onHit(source) {
        let success = false;
        const removeTarget = [
          "reflect",
          "lightscreen",
          "auroraveil",
          "safeguard",
          "mist",
          "spikes",
          "toxicspikes",
          "stealthrock",
          "stickyweb"
        ];
        const removeAll = ["spikes", "toxicspikes", "stealthrock", "stickyweb", "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.conditions.get(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.conditions.get(sideCondition).name, "[from] move: G-Max Wind Rage", "[of] " + source);
            success = true;
          }
        }
        this.field.clearTerrain();
        return success;
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Flying",
    contestType: "Cool"
  },
  grassknot: {
    num: 447,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      const targetWeight = target.getWeight();
      let bp;
      if (targetWeight >= 2e3) {
        bp = 120;
      } else if (targetWeight >= 1e3) {
        bp = 100;
      } else if (targetWeight >= 500) {
        bp = 80;
      } else if (targetWeight >= 250) {
        bp = 60;
      } else if (targetWeight >= 100) {
        bp = 40;
      } else {
        bp = 20;
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Special",
    name: "Grass Knot",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    onTryHit(target, source, move) {
      if (target.volatiles["dynamax"]) {
        this.add("-fail", source, "move: Grass Knot", "[from] Dynamax");
        this.attrLastMove("[still]");
        return null;
      }
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Cute"
  },
  grasspledge: {
    num: 520,
    accuracy: 100,
    basePower: 80,
    basePowerCallback(target, source, move) {
      if (["waterpledge", "firepledge"].includes(move.sourceEffect)) {
        this.add("-combine");
        return 150;
      }
      return move.basePower;
    },
    category: "Special",
    name: "Grass Pledge",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1, pledgecombo: 1 },
    onPrepareHit(target, source, move) {
      for (const action of this.queue.list) {
        if (!action.move || !action.pokemon?.isActive || action.pokemon.fainted || action.maxMove || action.zmove) {
          continue;
        }
        if (action.pokemon.isAlly(source) && ["waterpledge", "firepledge"].includes(action.move.id)) {
          this.queue.prioritizeAction(action, move);
          this.add("-waiting", source, action.pokemon);
          return null;
        }
      }
    },
    onModifyMove(move) {
      if (move.sourceEffect === "waterpledge") {
        move.type = "Grass";
        move.forceSTAB = true;
        move.sideCondition = "grasspledge";
      }
      if (move.sourceEffect === "firepledge") {
        move.type = "Fire";
        move.forceSTAB = true;
        move.sideCondition = "firepledge";
      }
    },
    condition: {
      duration: 4,
      onSideStart(targetSide) {
        this.add("-sidestart", targetSide, "Grass Pledge");
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 9,
      onSideEnd(targetSide) {
        this.add("-sideend", targetSide, "Grass Pledge");
      },
      onModifySpe(spe, pokemon) {
        return this.chainModify(0.25);
      }
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Beautiful"
  },
  grasswhistle: {
    num: 320,
    accuracy: 55,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Grass Whistle",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    status: "slp",
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  grassyglide: {
    num: 803,
    accuracy: 100,
    basePower: 55,
    category: "Physical",
    name: "Grassy Glide",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onModifyPriority(priority, source, target, move) {
      if (this.field.isTerrain("grassyterrain") && source.isGrounded()) {
        return priority + 1;
      }
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  grassyterrain: {
    num: 580,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Grassy Terrain",
    pp: 10,
    priority: 0,
    flags: { nonsky: 1, metronome: 1 },
    terrain: "grassyterrain",
    condition: {
      duration: 5,
      durationCallback(source, effect) {
        if (source?.hasItem("terrainextender")) {
          return 8;
        }
        return 5;
      },
      onBasePowerPriority: 6,
      onBasePower(basePower, attacker, defender, move) {
        const weakenedMoves = ["earthquake", "bulldoze", "magnitude"];
        if (weakenedMoves.includes(move.id) && defender.isGrounded() && !defender.isSemiInvulnerable()) {
          this.debug("move weakened by grassy terrain");
          return this.chainModify(0.5);
        }
        if (move.type === "Grass" && attacker.isGrounded()) {
          this.debug("grassy terrain boost");
          return this.chainModify([5325, 4096]);
        }
      },
      onFieldStart(field, source, effect) {
        if (effect?.effectType === "Ability") {
          this.add("-fieldstart", "move: Grassy Terrain", "[from] ability: " + effect.name, "[of] " + source);
        } else {
          this.add("-fieldstart", "move: Grassy Terrain");
        }
      },
      onResidualOrder: 5,
      onResidualSubOrder: 2,
      onResidual(pokemon) {
        if (pokemon.isGrounded() && !pokemon.isSemiInvulnerable()) {
          this.heal(pokemon.baseMaxhp / 16, pokemon, pokemon);
        } else {
          this.debug(`Pokemon semi-invuln or not grounded; Grassy Terrain skipped`);
        }
      },
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 7,
      onFieldEnd() {
        this.add("-fieldend", "move: Grassy Terrain");
      }
    },
    secondary: null,
    target: "all",
    type: "Grass",
    zMove: { boost: { def: 1 } },
    contestType: "Beautiful"
  },
  gravapple: {
    num: 788,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Grav Apple",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    onBasePower(basePower) {
      if (this.field.getPseudoWeather("gravity")) {
        return this.chainModify(1.5);
      }
    },
    secondary: {
      chance: 100,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Grass"
  },
  gravity: {
    num: 356,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Gravity",
    pp: 5,
    priority: 0,
    flags: { nonsky: 1, metronome: 1 },
    pseudoWeather: "gravity",
    condition: {
      duration: 5,
      durationCallback(source, effect) {
        if (source?.hasAbility("persistent")) {
          this.add("-activate", source, "ability: Persistent", "[move] Gravity");
          return 7;
        }
        return 5;
      },
      onFieldStart(target, source) {
        if (source?.hasAbility("persistent")) {
          this.add("-fieldstart", "move: Gravity", "[persistent]");
        } else {
          this.add("-fieldstart", "move: Gravity");
        }
        for (const pokemon of this.getAllActive()) {
          let applies = false;
          if (pokemon.removeVolatile("bounce") || pokemon.removeVolatile("fly")) {
            applies = true;
            this.queue.cancelMove(pokemon);
            pokemon.removeVolatile("twoturnmove");
          }
          if (pokemon.volatiles["skydrop"]) {
            applies = true;
            this.queue.cancelMove(pokemon);
            if (pokemon.volatiles["skydrop"].source) {
              this.add("-end", pokemon.volatiles["twoturnmove"].source, "Sky Drop", "[interrupt]");
            }
            pokemon.removeVolatile("skydrop");
            pokemon.removeVolatile("twoturnmove");
          }
          if (pokemon.volatiles["magnetrise"]) {
            applies = true;
            delete pokemon.volatiles["magnetrise"];
          }
          if (pokemon.volatiles["telekinesis"]) {
            applies = true;
            delete pokemon.volatiles["telekinesis"];
          }
          if (applies)
            this.add("-activate", pokemon, "move: Gravity");
        }
      },
      onModifyAccuracy(accuracy) {
        if (typeof accuracy !== "number")
          return;
        return this.chainModify([6840, 4096]);
      },
      onDisableMove(pokemon) {
        for (const moveSlot of pokemon.moveSlots) {
          if (this.dex.moves.get(moveSlot.id).flags["gravity"]) {
            pokemon.disableMove(moveSlot.id);
          }
        }
      },
      // groundedness implemented in battle.engine.js:BattlePokemon#isGrounded
      onBeforeMovePriority: 6,
      onBeforeMove(pokemon, target, move) {
        if (move.flags["gravity"] && !move.isZ) {
          this.add("cant", pokemon, "move: Gravity", move);
          return false;
        }
      },
      onModifyMove(move, pokemon, target) {
        if (move.flags["gravity"] && !move.isZ) {
          this.add("cant", pokemon, "move: Gravity", move);
          return false;
        }
      },
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 2,
      onFieldEnd() {
        this.add("-fieldend", "move: Gravity");
      }
    },
    secondary: null,
    target: "all",
    type: "Psychic",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  growl: {
    num: 45,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Growl",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    boosts: {
      atk: -1
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  growth: {
    num: 74,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Growth",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onModifyMove(move, pokemon) {
      if (["sunnyday", "desolateland"].includes(pokemon.effectiveWeather()))
        move.boosts = { atk: 2, spa: 2 };
    },
    boosts: {
      atk: 1,
      spa: 1
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { spa: 1 } },
    contestType: "Beautiful"
  },
  grudge: {
    num: 288,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Grudge",
    pp: 5,
    priority: 0,
    flags: { bypasssub: 1, metronome: 1 },
    volatileStatus: "grudge",
    condition: {
      onStart(pokemon) {
        this.add("-singlemove", pokemon, "Grudge");
      },
      onFaint(target, source, effect) {
        if (!source || source.fainted || !effect)
          return;
        if (effect.effectType === "Move" && !effect.flags["futuremove"] && source.lastMove) {
          let move = source.lastMove;
          if (move.isMax && move.baseMove)
            move = this.dex.moves.get(move.baseMove);
          for (const moveSlot of source.moveSlots) {
            if (moveSlot.id === move.id) {
              moveSlot.pp = 0;
              this.add("-activate", source, "move: Grudge", move.name);
            }
          }
        }
      },
      onBeforeMovePriority: 100,
      onBeforeMove(pokemon) {
        this.debug("removing Grudge before attack");
        pokemon.removeVolatile("grudge");
      }
    },
    secondary: null,
    target: "self",
    type: "Ghost",
    zMove: { effect: "redirect" },
    contestType: "Tough"
  },
  guardianofalola: {
    num: 698,
    accuracy: true,
    basePower: 0,
    damageCallback(pokemon, target) {
      const hp75 = Math.floor(target.getUndynamaxedHP() * 3 / 4);
      if (target.volatiles["protect"] || target.volatiles["banefulbunker"] || target.volatiles["kingsshield"] || target.volatiles["spikyshield"] || target.side.getSideCondition("matblock")) {
        this.add("-zbroken", target);
        return this.clampIntRange(Math.ceil(hp75 / 4 - 0.5), 1);
      }
      return this.clampIntRange(hp75, 1);
    },
    category: "Special",
    isNonstandard: "Past",
    name: "Guardian of Alola",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "tapuniumz",
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Tough"
  },
  guardsplit: {
    num: 470,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Guard Split",
    pp: 10,
    priority: 0,
    flags: { protect: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      const newdef = Math.floor((target.storedStats.def + source.storedStats.def) / 2);
      target.storedStats.def = newdef;
      source.storedStats.def = newdef;
      const newspd = Math.floor((target.storedStats.spd + source.storedStats.spd) / 2);
      target.storedStats.spd = newspd;
      source.storedStats.spd = newspd;
      this.add("-activate", source, "move: Guard Split", "[of] " + target);
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  guardswap: {
    num: 385,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Guard Swap",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      const targetBoosts = {};
      const sourceBoosts = {};
      const defSpd = ["def", "spd"];
      for (const stat of defSpd) {
        targetBoosts[stat] = target.boosts[stat];
        sourceBoosts[stat] = source.boosts[stat];
      }
      source.setBoost(targetBoosts);
      target.setBoost(sourceBoosts);
      this.add("-swapboost", source, target, "def, spd", "[from] move: Guard Swap");
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  guillotine: {
    num: 12,
    accuracy: 30,
    basePower: 0,
    category: "Physical",
    name: "Guillotine",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    ohko: true,
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 180 },
    maxMove: { basePower: 130 },
    contestType: "Cool"
  },
  gunkshot: {
    num: 441,
    accuracy: 80,
    basePower: 120,
    category: "Physical",
    name: "Gunk Shot",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "psn"
    },
    target: "normal",
    type: "Poison",
    contestType: "Tough"
  },
  gust: {
    num: 16,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Gust",
    pp: 35,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, metronome: 1, wind: 1 },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Clever"
  },
  gyroball: {
    num: 360,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      let power = Math.floor(25 * target.getStat("spe") / pokemon.getStat("spe")) + 1;
      if (!isFinite(power))
        power = 1;
      if (power > 150)
        power = 150;
      this.debug("BP: " + power);
      return power;
    },
    category: "Physical",
    name: "Gyro Ball",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: null,
    target: "normal",
    type: "Steel",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Cool"
  },
  hail: {
    num: 258,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Hail",
    pp: 10,
    priority: 0,
    flags: { metronome: 1 },
    weather: "hail",
    secondary: null,
    target: "all",
    type: "Ice",
    zMove: { boost: { spe: 1 } },
    contestType: "Beautiful"
  },
  hammerarm: {
    num: 359,
    accuracy: 90,
    basePower: 100,
    category: "Physical",
    name: "Hammer Arm",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    self: {
      boosts: {
        spe: -1
      }
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  happyhour: {
    num: 603,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Happy Hour",
    pp: 30,
    priority: 0,
    flags: { metronome: 1 },
    onTryHit(target, source) {
      this.add("-activate", target, "move: Happy Hour");
    },
    secondary: null,
    target: "allySide",
    type: "Normal",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Cute"
  },
  harden: {
    num: 106,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Harden",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 1
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Tough"
  },
  hardpress: {
    num: 912,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      const hp = target.hp;
      const maxHP = target.maxhp;
      const bp = Math.floor(Math.floor((100 * (100 * Math.floor(hp * 4096 / maxHP)) + 2048 - 1) / 4096) / 100) || 1;
      this.debug("BP for " + hp + "/" + maxHP + " HP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Hard Press",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Steel"
  },
  haze: {
    num: 114,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Haze",
    pp: 30,
    priority: 0,
    flags: { bypasssub: 1, metronome: 1 },
    onHitField() {
      this.add("-clearallboost");
      for (const pokemon of this.getAllActive()) {
        pokemon.clearBoosts();
      }
    },
    secondary: null,
    target: "all",
    type: "Ice",
    zMove: { effect: "heal" },
    contestType: "Beautiful"
  },
  headbutt: {
    num: 29,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Headbutt",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  headcharge: {
    num: 543,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    isNonstandard: "Past",
    name: "Head Charge",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [1, 4],
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  headlongrush: {
    num: 838,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Headlong Rush",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    self: {
      boosts: {
        def: -1,
        spd: -1
      }
    },
    secondary: null,
    target: "normal",
    type: "Ground"
  },
  headsmash: {
    num: 457,
    accuracy: 80,
    basePower: 150,
    category: "Physical",
    name: "Head Smash",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [1, 2],
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Tough"
  },
  healbell: {
    num: 215,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Heal Bell",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, sound: 1, distance: 1, bypasssub: 1, metronome: 1 },
    onHit(target, source) {
      this.add("-activate", source, "move: Heal Bell");
      let success = false;
      const allies = [...target.side.pokemon, ...target.side.allySide?.pokemon || []];
      for (const ally of allies) {
        if (ally !== source && ally.hasAbility("soundproof"))
          continue;
        if (ally.cureStatus())
          success = true;
      }
      return success;
    },
    target: "allyTeam",
    type: "Normal",
    zMove: { effect: "heal" },
    contestType: "Beautiful"
  },
  healblock: {
    num: 377,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Heal Block",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    volatileStatus: "healblock",
    condition: {
      duration: 5,
      durationCallback(target, source, effect) {
        if (effect?.name === "Psychic Noise") {
          return 2;
        }
        if (source?.hasAbility("persistent")) {
          this.add("-activate", source, "ability: Persistent", "[move] Heal Block");
          return 7;
        }
        return 5;
      },
      onStart(pokemon, source) {
        this.add("-start", pokemon, "move: Heal Block");
        source.moveThisTurnResult = true;
      },
      onDisableMove(pokemon) {
        for (const moveSlot of pokemon.moveSlots) {
          if (this.dex.moves.get(moveSlot.id).flags["heal"]) {
            pokemon.disableMove(moveSlot.id);
          }
        }
      },
      onBeforeMovePriority: 6,
      onBeforeMove(pokemon, target, move) {
        if (move.flags["heal"] && !move.isZ && !move.isMax) {
          this.add("cant", pokemon, "move: Heal Block", move);
          return false;
        }
      },
      onModifyMove(move, pokemon, target) {
        if (move.flags["heal"] && !move.isZ && !move.isMax) {
          this.add("cant", pokemon, "move: Heal Block", move);
          return false;
        }
      },
      onResidualOrder: 20,
      onEnd(pokemon) {
        this.add("-end", pokemon, "move: Heal Block");
      },
      onTryHeal(damage, target, source, effect) {
        if (effect?.id === "zpower" || this.effectState.isZ)
          return damage;
        return false;
      },
      onRestart(target, source) {
        this.add("-fail", target, "move: Heal Block");
        if (!source.moveThisTurnResult) {
          source.moveThisTurnResult = false;
        }
      }
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Psychic",
    zMove: { boost: { spa: 2 } },
    contestType: "Clever"
  },
  healingwish: {
    num: 361,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Healing Wish",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    onTryHit(source) {
      if (!this.canSwitch(source.side)) {
        this.attrLastMove("[still]");
        this.add("-fail", source);
        return this.NOT_FAIL;
      }
    },
    selfdestruct: "ifHit",
    slotCondition: "healingwish",
    condition: {
      onSwap(target) {
        if (!target.fainted && (target.hp < target.maxhp || target.status)) {
          target.heal(target.maxhp);
          target.clearStatus();
          this.add("-heal", target, target.getHealth, "[from] move: Healing Wish");
          target.side.removeSlotCondition(target, "healingwish");
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    contestType: "Beautiful"
  },
  healorder: {
    num: 456,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Heal Order",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    heal: [1, 2],
    secondary: null,
    target: "self",
    type: "Bug",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  healpulse: {
    num: 505,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Heal Pulse",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, distance: 1, heal: 1, allyanim: 1, metronome: 1, pulse: 1 },
    onHit(target, source) {
      let success = false;
      if (source.hasAbility("megalauncher")) {
        success = !!this.heal(this.modify(target.baseMaxhp, 0.75));
      } else {
        success = !!this.heal(Math.ceil(target.baseMaxhp * 0.5));
      }
      if (success && !target.isAlly(source)) {
        target.staleness = "external";
      }
      if (!success) {
        this.add("-fail", target, "heal");
        return this.NOT_FAIL;
      }
      return success;
    },
    secondary: null,
    target: "any",
    type: "Psychic",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  heartstamp: {
    num: 531,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    isNonstandard: "Past",
    name: "Heart Stamp",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Psychic",
    contestType: "Cute"
  },
  heartswap: {
    num: 391,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Heart Swap",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      const targetBoosts = {};
      const sourceBoosts = {};
      let i;
      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: Heart Swap");
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { effect: "crit2" },
    contestType: "Clever"
  },
  heatcrash: {
    num: 535,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      const targetWeight = target.getWeight();
      const pokemonWeight = pokemon.getWeight();
      let bp;
      if (pokemonWeight >= targetWeight * 5) {
        bp = 120;
      } else if (pokemonWeight >= targetWeight * 4) {
        bp = 100;
      } else if (pokemonWeight >= targetWeight * 3) {
        bp = 80;
      } else if (pokemonWeight >= targetWeight * 2) {
        bp = 60;
      } else {
        bp = 40;
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Heat Crash",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    onTryHit(target, pokemon, move) {
      if (target.volatiles["dynamax"]) {
        this.add("-fail", pokemon, "Dynamax");
        this.attrLastMove("[still]");
        return null;
      }
    },
    secondary: null,
    target: "normal",
    type: "Fire",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Tough"
  },
  heatwave: {
    num: 257,
    accuracy: 90,
    basePower: 95,
    category: "Special",
    name: "Heat Wave",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "allAdjacentFoes",
    type: "Fire",
    contestType: "Beautiful"
  },
  heavyslam: {
    num: 484,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      const targetWeight = target.getWeight();
      const pokemonWeight = pokemon.getWeight();
      let bp;
      if (pokemonWeight >= targetWeight * 5) {
        bp = 120;
      } else if (pokemonWeight >= targetWeight * 4) {
        bp = 100;
      } else if (pokemonWeight >= targetWeight * 3) {
        bp = 80;
      } else if (pokemonWeight >= targetWeight * 2) {
        bp = 60;
      } else {
        bp = 40;
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Heavy Slam",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    onTryHit(target, pokemon, move) {
      if (target.volatiles["dynamax"]) {
        this.add("-fail", pokemon, "Dynamax");
        this.attrLastMove("[still]");
        return null;
      }
    },
    secondary: null,
    target: "normal",
    type: "Steel",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Tough"
  },
  helpinghand: {
    num: 270,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Helping Hand",
    pp: 20,
    priority: 5,
    flags: { bypasssub: 1, noassist: 1, failcopycat: 1 },
    volatileStatus: "helpinghand",
    onTryHit(target) {
      if (!target.newlySwitched && !this.queue.willMove(target))
        return false;
    },
    condition: {
      duration: 1,
      onStart(target, source) {
        this.effectState.multiplier = 1.5;
        this.add("-singleturn", target, "Helping Hand", "[of] " + source);
      },
      onRestart(target, source) {
        this.effectState.multiplier *= 1.5;
        this.add("-singleturn", target, "Helping Hand", "[of] " + source);
      },
      onBasePowerPriority: 10,
      onBasePower(basePower) {
        this.debug("Boosting from Helping Hand: " + this.effectState.multiplier);
        return this.chainModify(this.effectState.multiplier);
      }
    },
    secondary: null,
    target: "adjacentAlly",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  hex: {
    num: 506,
    accuracy: 100,
    basePower: 65,
    basePowerCallback(pokemon, target, move) {
      if (target.status || target.hasAbility("comatose")) {
        this.debug("BP doubled from status condition");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Special",
    name: "Hex",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ghost",
    zMove: { basePower: 160 },
    contestType: "Clever"
  },
  hiddenpower: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    isNonstandard: "Past",
    name: "Hidden Power",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onModifyType(move, pokemon) {
      move.type = pokemon.hpType || "Dark";
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Clever"
  },
  hiddenpowerbug: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Bug",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Clever"
  },
  hiddenpowerdark: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Dark",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  hiddenpowerdragon: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Dragon",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Dragon",
    contestType: "Clever"
  },
  hiddenpowerelectric: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Electric",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Clever"
  },
  hiddenpowerfighting: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Fighting",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Clever"
  },
  hiddenpowerfire: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Fire",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Clever"
  },
  hiddenpowerflying: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Flying",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Flying",
    contestType: "Clever"
  },
  hiddenpowerghost: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Ghost",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Clever"
  },
  hiddenpowergrass: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Grass",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Clever"
  },
  hiddenpowerground: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Ground",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Ground",
    contestType: "Clever"
  },
  hiddenpowerice: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Ice",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Ice",
    contestType: "Clever"
  },
  hiddenpowerpoison: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Poison",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Poison",
    contestType: "Clever"
  },
  hiddenpowerpsychic: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Psychic",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  hiddenpowerrock: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Rock",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Clever"
  },
  hiddenpowersteel: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Steel",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Steel",
    contestType: "Clever"
  },
  hiddenpowerwater: {
    num: 237,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    realMove: "Hidden Power",
    isNonstandard: "Past",
    name: "Hidden Power Water",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Clever"
  },
  highhorsepower: {
    num: 667,
    accuracy: 95,
    basePower: 95,
    category: "Physical",
    name: "High Horsepower",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ground",
    contestType: "Tough"
  },
  highjumpkick: {
    num: 136,
    accuracy: 90,
    basePower: 130,
    category: "Physical",
    name: "High Jump Kick",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, gravity: 1, metronome: 1 },
    hasCrashDamage: true,
    onMoveFail(target, source, move) {
      this.damage(source.baseMaxhp / 2, source, source, this.dex.conditions.get("High Jump Kick"));
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  holdback: {
    num: 610,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    isNonstandard: "Unobtainable",
    name: "Hold Back",
    pp: 40,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onDamagePriority: -20,
    onDamage(damage, target, source, effect) {
      if (damage >= target.hp)
        return target.hp - 1;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  holdhands: {
    num: 607,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Unobtainable",
    name: "Hold Hands",
    pp: 40,
    priority: 0,
    flags: { bypasssub: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    secondary: null,
    target: "adjacentAlly",
    type: "Normal",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Cute"
  },
  honeclaws: {
    num: 468,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Hone Claws",
    pp: 15,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      atk: 1,
      accuracy: 1
    },
    secondary: null,
    target: "self",
    type: "Dark",
    zMove: { boost: { atk: 1 } },
    contestType: "Cute"
  },
  hornattack: {
    num: 30,
    accuracy: 100,
    basePower: 65,
    category: "Physical",
    name: "Horn Attack",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  horndrill: {
    num: 32,
    accuracy: 30,
    basePower: 0,
    category: "Physical",
    name: "Horn Drill",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    ohko: true,
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 180 },
    maxMove: { basePower: 130 },
    contestType: "Cool"
  },
  hornleech: {
    num: 532,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Horn Leech",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Tough"
  },
  howl: {
    num: 336,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Howl",
    pp: 40,
    priority: 0,
    flags: { snatch: 1, sound: 1, metronome: 1 },
    boosts: {
      atk: 1
    },
    secondary: null,
    target: "allies",
    type: "Normal",
    zMove: { boost: { atk: 1 } },
    contestType: "Cool"
  },
  hurricane: {
    num: 542,
    accuracy: 70,
    basePower: 110,
    category: "Special",
    name: "Hurricane",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, metronome: 1, wind: 1 },
    onModifyMove(move, pokemon, target) {
      switch (target?.effectiveWeather()) {
        case "raindance":
        case "primordialsea":
          move.accuracy = true;
          break;
        case "sunnyday":
        case "desolateland":
          move.accuracy = 50;
          break;
      }
    },
    secondary: {
      chance: 30,
      volatileStatus: "confusion"
    },
    target: "any",
    type: "Flying",
    contestType: "Tough"
  },
  hydrocannon: {
    num: 308,
    accuracy: 90,
    basePower: 150,
    category: "Special",
    name: "Hydro Cannon",
    pp: 5,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  hydropump: {
    num: 56,
    accuracy: 80,
    basePower: 110,
    category: "Special",
    name: "Hydro Pump",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  hydrosteam: {
    num: 876,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Hydro Steam",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1, metronome: 1 },
    // Damage boost in Sun applied in conditions.ts
    thawsTarget: true,
    secondary: null,
    target: "normal",
    type: "Water"
  },
  hydrovortex: {
    num: 642,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Hydro Vortex",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "wateriumz",
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  hyperbeam: {
    num: 63,
    accuracy: 90,
    basePower: 150,
    category: "Special",
    name: "Hyper Beam",
    pp: 5,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  hyperdrill: {
    num: 887,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Hyper Drill",
    pp: 5,
    priority: 0,
    flags: { contact: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Clever"
  },
  hyperfang: {
    num: 158,
    accuracy: 90,
    basePower: 80,
    category: "Physical",
    isNonstandard: "Past",
    name: "Hyper Fang",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    secondary: {
      chance: 10,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  hyperspacefury: {
    num: 621,
    accuracy: true,
    basePower: 100,
    category: "Physical",
    name: "Hyperspace Fury",
    pp: 5,
    priority: 0,
    flags: { mirror: 1, bypasssub: 1 },
    breaksProtect: true,
    onTry(source) {
      if (source.species.name === "Hoopa-Unbound") {
        return;
      }
      this.hint("Only a Pokemon whose form is Hoopa Unbound can use this move.");
      if (source.species.name === "Hoopa") {
        this.attrLastMove("[still]");
        this.add("-fail", source, "move: Hyperspace Fury", "[forme]");
        return null;
      }
      this.attrLastMove("[still]");
      this.add("-fail", source, "move: Hyperspace Fury");
      return null;
    },
    self: {
      boosts: {
        def: -1
      }
    },
    noSketch: true,
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Tough"
  },
  hyperspacehole: {
    num: 593,
    accuracy: true,
    basePower: 80,
    category: "Special",
    name: "Hyperspace Hole",
    pp: 5,
    priority: 0,
    flags: { mirror: 1, bypasssub: 1 },
    breaksProtect: true,
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  hypervoice: {
    num: 304,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Hyper Voice",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Normal",
    contestType: "Cool"
  },
  hypnosis: {
    num: 95,
    accuracy: 60,
    basePower: 0,
    category: "Status",
    name: "Hypnosis",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    status: "slp",
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  iceball: {
    num: 301,
    accuracy: 90,
    basePower: 30,
    basePowerCallback(pokemon, target, move) {
      let bp = move.basePower;
      const iceballData = pokemon.volatiles["iceball"];
      if (iceballData?.hitCount) {
        bp *= Math.pow(2, iceballData.contactHitCount);
      }
      if (iceballData && pokemon.status !== "slp") {
        iceballData.hitCount++;
        iceballData.contactHitCount++;
        if (iceballData.hitCount < 5) {
          iceballData.duration = 2;
        }
      }
      if (pokemon.volatiles["defensecurl"]) {
        bp *= 2;
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Ice Ball",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, failinstruct: 1, bullet: 1, noparentalbond: 1 },
    onModifyMove(move, pokemon, target) {
      if (pokemon.volatiles["iceball"] || pokemon.status === "slp" || !target)
        return;
      pokemon.addVolatile("iceball");
      pokemon.volatiles["iceball"].targetSlot = move.sourceEffect ? pokemon.lastMoveTargetLoc : pokemon.getLocOf(target);
    },
    onAfterMove(source, target, move) {
      const iceballData = source.volatiles["iceball"];
      if (iceballData && iceballData.hitCount === 5 && iceballData.contactHitCount < 5) {
        source.addVolatile("rolloutstorage");
        source.volatiles["rolloutstorage"].contactHitCount = iceballData.contactHitCount;
      }
    },
    condition: {
      duration: 1,
      onLockMove: "iceball",
      onStart() {
        this.effectState.hitCount = 0;
        this.effectState.contactHitCount = 0;
      },
      onResidual(target) {
        if (target.lastMove && target.lastMove.id === "struggle") {
          delete target.volatiles["iceball"];
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  icebeam: {
    num: 58,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Ice Beam",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "frz"
    },
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  iceburn: {
    num: 554,
    accuracy: 90,
    basePower: 140,
    category: "Special",
    name: "Ice Burn",
    pp: 5,
    priority: 0,
    flags: { charge: 1, protect: 1, mirror: 1, nosleeptalk: 1, failinstruct: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    secondary: {
      chance: 30,
      status: "brn"
    },
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  icefang: {
    num: 423,
    accuracy: 95,
    basePower: 65,
    category: "Physical",
    name: "Ice Fang",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    secondaries: [
      {
        chance: 10,
        status: "frz"
      },
      {
        chance: 10,
        volatileStatus: "flinch"
      }
    ],
    target: "normal",
    type: "Ice",
    contestType: "Cool"
  },
  icehammer: {
    num: 665,
    accuracy: 90,
    basePower: 100,
    category: "Physical",
    name: "Ice Hammer",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    self: {
      boosts: {
        spe: -1
      }
    },
    secondary: null,
    target: "normal",
    type: "Ice",
    contestType: "Tough"
  },
  icepunch: {
    num: 8,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Ice Punch",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "frz"
    },
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  iceshard: {
    num: 420,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Ice Shard",
    pp: 30,
    priority: 1,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  icespinner: {
    num: 861,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Ice Spinner",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onAfterHit(target, source) {
      if (source.hp) {
        this.field.clearTerrain();
      }
    },
    onAfterSubDamage(damage, target, source) {
      if (source.hp) {
        this.field.clearTerrain();
      }
    },
    secondary: null,
    target: "normal",
    type: "Ice"
  },
  iciclecrash: {
    num: 556,
    accuracy: 90,
    basePower: 85,
    category: "Physical",
    name: "Icicle Crash",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Ice",
    contestType: "Beautiful"
  },
  iciclespear: {
    num: 333,
    accuracy: 100,
    basePower: 25,
    category: "Physical",
    name: "Icicle Spear",
    pp: 30,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Ice",
    zMove: { basePower: 140 },
    maxMove: { basePower: 130 },
    contestType: "Beautiful"
  },
  icywind: {
    num: 196,
    accuracy: 95,
    basePower: 55,
    category: "Special",
    name: "Icy Wind",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Ice",
    contestType: "Beautiful"
  },
  imprison: {
    num: 286,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Imprison",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, bypasssub: 1, metronome: 1, mustpressure: 1 },
    volatileStatus: "imprison",
    condition: {
      noCopy: true,
      onStart(target) {
        this.add("-start", target, "move: Imprison");
      },
      onFoeDisableMove(pokemon) {
        for (const moveSlot of this.effectState.source.moveSlots) {
          if (moveSlot.id === "struggle")
            continue;
          pokemon.disableMove(moveSlot.id, "hidden");
        }
        pokemon.maybeDisabled = true;
      },
      onFoeBeforeMovePriority: 4,
      onFoeBeforeMove(attacker, defender, move) {
        if (move.id !== "struggle" && this.effectState.source.hasMove(move.id) && !move.isZ && !move.isMax) {
          this.add("cant", attacker, "move: Imprison", move);
          return false;
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { boost: { spd: 2 } },
    contestType: "Clever"
  },
  incinerate: {
    num: 510,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    name: "Incinerate",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onHit(pokemon, source) {
      const item = pokemon.getItem();
      if ((item.isBerry || item.isGem) && pokemon.takeItem(source)) {
        this.add("-enditem", pokemon, item.name, "[from] move: Incinerate");
      }
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Fire",
    contestType: "Tough"
  },
  infernalparade: {
    num: 844,
    accuracy: 100,
    basePower: 60,
    basePowerCallback(pokemon, target, move) {
      if (target.status || target.hasAbility("comatose"))
        return move.basePower * 2;
      return move.basePower;
    },
    category: "Special",
    name: "Infernal Parade",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "brn"
    },
    target: "normal",
    type: "Ghost"
  },
  inferno: {
    num: 517,
    accuracy: 50,
    basePower: 100,
    category: "Special",
    name: "Inferno",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  infernooverdrive: {
    num: 640,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Inferno Overdrive",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "firiumz",
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Cool"
  },
  infestation: {
    num: 611,
    accuracy: 100,
    basePower: 20,
    category: "Special",
    name: "Infestation",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cute"
  },
  ingrain: {
    num: 275,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Ingrain",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, nonsky: 1, metronome: 1 },
    volatileStatus: "ingrain",
    condition: {
      onStart(pokemon) {
        this.add("-start", pokemon, "move: Ingrain");
      },
      onResidualOrder: 7,
      onResidual(pokemon) {
        this.heal(pokemon.baseMaxhp / 16);
      },
      onTrapPokemon(pokemon) {
        pokemon.tryTrap();
      },
      // groundedness implemented in battle.engine.js:BattlePokemon#isGrounded
      onDragOut(pokemon) {
        this.add("-activate", pokemon, "move: Ingrain");
        return null;
      }
    },
    secondary: null,
    target: "self",
    type: "Grass",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  instruct: {
    num: 689,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Instruct",
    pp: 15,
    priority: 0,
    flags: { protect: 1, bypasssub: 1, allyanim: 1, failinstruct: 1 },
    onHit(target, source) {
      if (!target.lastMove || target.volatiles["dynamax"])
        return false;
      const lastMove = target.lastMove;
      const moveIndex = target.moves.indexOf(lastMove.id);
      if (lastMove.flags["failinstruct"] || lastMove.isZ || lastMove.isMax || lastMove.flags["charge"] || lastMove.flags["recharge"] || target.volatiles["beakblast"] || target.volatiles["focuspunch"] || target.volatiles["shelltrap"] || target.moveSlots[moveIndex] && target.moveSlots[moveIndex].pp <= 0) {
        return false;
      }
      this.add("-singleturn", target, "move: Instruct", "[of] " + source);
      this.queue.prioritizeAction(this.queue.resolveAction({
        choice: "move",
        pokemon: target,
        moveid: target.lastMove.id,
        targetLoc: target.lastMoveTargetLoc
      })[0]);
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  iondeluge: {
    num: 569,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Ion Deluge",
    pp: 25,
    priority: 1,
    flags: { metronome: 1 },
    pseudoWeather: "iondeluge",
    condition: {
      duration: 1,
      onFieldStart(target, source, sourceEffect) {
        this.add("-fieldactivate", "move: Ion Deluge");
        this.hint(`Normal-type moves become Electric-type after using ${sourceEffect}.`);
      },
      onModifyTypePriority: -2,
      onModifyType(move) {
        if (move.type === "Normal") {
          move.type = "Electric";
          this.debug(move.name + "'s type changed to Electric");
        }
      }
    },
    secondary: null,
    target: "all",
    type: "Electric",
    zMove: { boost: { spa: 1 } },
    contestType: "Beautiful"
  },
  irondefense: {
    num: 334,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Iron Defense",
    pp: 15,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 2
    },
    secondary: null,
    target: "self",
    type: "Steel",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Tough"
  },
  ironhead: {
    num: 442,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Iron Head",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Steel",
    contestType: "Tough"
  },
  irontail: {
    num: 231,
    accuracy: 75,
    basePower: 100,
    category: "Physical",
    name: "Iron Tail",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  ivycudgel: {
    num: 904,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Ivy Cudgel",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    onPrepareHit(target, source, move) {
      if (move.type !== "Grass") {
        this.attrLastMove("[anim] Ivy Cudgel " + move.type);
      }
    },
    onModifyType(move, pokemon) {
      switch (pokemon.species.name) {
        case "Ogerpon-Wellspring":
        case "Ogerpon-Wellspring-Tera":
          move.type = "Water";
          break;
        case "Ogerpon-Hearthflame":
        case "Ogerpon-Hearthflame-Tera":
          move.type = "Fire";
          break;
        case "Ogerpon-Cornerstone":
        case "Ogerpon-Cornerstone-Tera":
          move.type = "Rock";
          break;
      }
    },
    secondary: null,
    target: "normal",
    type: "Grass"
  },
  jawlock: {
    num: 746,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Jaw Lock",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    onHit(target, source, move) {
      source.addVolatile("trapped", target, move, "trapper");
      target.addVolatile("trapped", source, move, "trapper");
    },
    secondary: null,
    target: "normal",
    type: "Dark"
  },
  jetpunch: {
    num: 857,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Jet Punch",
    pp: 15,
    priority: 1,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1 },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  judgment: {
    num: 449,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Judgment",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onModifyType(move, pokemon) {
      if (pokemon.ignoringItem())
        return;
      const item = pokemon.getItem();
      if (item.id && item.onPlate && !item.zMove) {
        move.type = item.onPlate;
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Beautiful"
  },
  jumpkick: {
    num: 26,
    accuracy: 95,
    basePower: 100,
    category: "Physical",
    isNonstandard: "Past",
    name: "Jump Kick",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, gravity: 1, metronome: 1 },
    hasCrashDamage: true,
    onMoveFail(target, source, move) {
      this.damage(source.baseMaxhp / 2, source, source, this.dex.conditions.get("Jump Kick"));
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  junglehealing: {
    num: 816,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Jungle Healing",
    pp: 10,
    priority: 0,
    flags: { heal: 1, bypasssub: 1, allyanim: 1 },
    onHit(pokemon) {
      const success = !!this.heal(this.modify(pokemon.maxhp, 0.25));
      return pokemon.cureStatus() || success;
    },
    secondary: null,
    target: "allies",
    type: "Grass"
  },
  karatechop: {
    num: 2,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    isNonstandard: "Past",
    name: "Karate Chop",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  kinesis: {
    num: 134,
    accuracy: 80,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Kinesis",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      accuracy: -1
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { evasion: 1 } },
    contestType: "Clever"
  },
  kingsshield: {
    num: 588,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "King's Shield",
    pp: 10,
    priority: 4,
    flags: { noassist: 1, failcopycat: 1, failinstruct: 1 },
    stallingMove: true,
    volatileStatus: "kingsshield",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    onHit(pokemon) {
      pokemon.addVolatile("stall");
    },
    condition: {
      duration: 1,
      onStart(target) {
        this.add("-singleturn", target, "Protect");
      },
      onTryHitPriority: 3,
      onTryHit(target, source, move) {
        if (!move.flags["protect"] || move.category === "Status") {
          if (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          if (move.isZ || move.isMax)
            target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        if (move.smartTarget) {
          move.smartTarget = false;
        } else {
          this.add("-activate", target, "move: Protect");
        }
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        if (this.checkMoveMakesContact(move, source, target)) {
          this.boost({ atk: -1 }, source, target, this.dex.getActiveMove("King's Shield"));
        }
        return this.NOT_FAIL;
      },
      onHit(target, source, move) {
        if (move.isZOrMaxPowered && this.checkMoveMakesContact(move, source, target)) {
          this.boost({ atk: -1 }, source, target, this.dex.getActiveMove("King's Shield"));
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Steel",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cool"
  },
  knockoff: {
    num: 282,
    accuracy: 100,
    basePower: 65,
    category: "Physical",
    name: "Knock Off",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, source, target, move) {
      const item = target.getItem();
      if (!this.singleEvent("TakeItem", item, target.itemState, target, target, move, item))
        return;
      if (item.id) {
        return this.chainModify(1.5);
      }
    },
    onAfterHit(target, source) {
      if (source.hp) {
        const item = target.takeItem();
        if (item) {
          this.add("-enditem", target, item.name, "[from] move: Knock Off", "[of] " + source);
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  kowtowcleave: {
    num: 869,
    accuracy: true,
    basePower: 85,
    category: "Physical",
    name: "Kowtow Cleave",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    secondary: null,
    target: "normal",
    type: "Dark"
  },
  landswrath: {
    num: 616,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    isNonstandard: "Past",
    name: "Land's Wrath",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Ground",
    zMove: { basePower: 185 },
    contestType: "Beautiful"
  },
  laserfocus: {
    num: 673,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Laser Focus",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    volatileStatus: "laserfocus",
    condition: {
      duration: 2,
      onStart(pokemon, source, effect) {
        if (effect && ["costar", "imposter", "psychup", "transform"].includes(effect.id)) {
          this.add("-start", pokemon, "move: Laser Focus", "[silent]");
        } else {
          this.add("-start", pokemon, "move: Laser Focus");
        }
      },
      onRestart(pokemon) {
        this.effectState.duration = 2;
        this.add("-start", pokemon, "move: Laser Focus");
      },
      onModifyCritRatio(critRatio) {
        return 5;
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "move: Laser Focus", "[silent]");
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { atk: 1 } },
    contestType: "Cool"
  },
  lashout: {
    num: 808,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Lash Out",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, source) {
      if (source.statsLoweredThisTurn) {
        this.debug("lashout buff");
        return this.chainModify(2);
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark"
  },
  lastresort: {
    num: 387,
    accuracy: 100,
    basePower: 140,
    category: "Physical",
    name: "Last Resort",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onTry(source) {
      if (source.moveSlots.length < 2)
        return false;
      let hasLastResort = false;
      for (const moveSlot of source.moveSlots) {
        if (moveSlot.id === "lastresort") {
          hasLastResort = true;
          continue;
        }
        if (!moveSlot.used)
          return false;
      }
      return hasLastResort;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  lastrespects: {
    num: 854,
    accuracy: 100,
    basePower: 50,
    basePowerCallback(pokemon, target, move) {
      return 50 + 50 * pokemon.side.totalFainted;
    },
    category: "Physical",
    name: "Last Respects",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ghost"
  },
  lavaplume: {
    num: 436,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Lava Plume",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "brn"
    },
    target: "allAdjacent",
    type: "Fire",
    contestType: "Tough"
  },
  leafage: {
    num: 670,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Leafage",
    pp: 40,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Tough"
  },
  leafblade: {
    num: 348,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Leaf Blade",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  leafstorm: {
    num: 437,
    accuracy: 90,
    basePower: 130,
    category: "Special",
    name: "Leaf Storm",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    self: {
      boosts: {
        spa: -2
      }
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Beautiful"
  },
  leaftornado: {
    num: 536,
    accuracy: 90,
    basePower: 65,
    category: "Special",
    isNonstandard: "Past",
    name: "Leaf Tornado",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 50,
      boosts: {
        accuracy: -1
      }
    },
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  leechlife: {
    num: 141,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Leech Life",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Clever"
  },
  leechseed: {
    num: 73,
    accuracy: 90,
    basePower: 0,
    category: "Status",
    name: "Leech Seed",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    volatileStatus: "leechseed",
    condition: {
      onStart(target) {
        this.add("-start", target, "move: Leech Seed");
      },
      onResidualOrder: 8,
      onResidual(pokemon) {
        const target = this.getAtSlot(pokemon.volatiles["leechseed"].sourceSlot);
        if (!target || target.fainted || target.hp <= 0) {
          this.debug("Nothing to leech into");
          return;
        }
        const damage = this.damage(pokemon.baseMaxhp / 8, pokemon, target);
        if (damage) {
          this.heal(damage, target, pokemon);
        }
      }
    },
    onTryImmunity(target) {
      return !target.hasType("Grass");
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  leer: {
    num: 43,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Leer",
    pp: 30,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      def: -1
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Normal",
    zMove: { boost: { atk: 1 } },
    contestType: "Cool"
  },
  letssnuggleforever: {
    num: 726,
    accuracy: true,
    basePower: 190,
    category: "Physical",
    isNonstandard: "Past",
    name: "Let's Snuggle Forever",
    pp: 1,
    priority: 0,
    flags: { contact: 1 },
    isZ: "mimikiumz",
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Cool"
  },
  lick: {
    num: 122,
    accuracy: 100,
    basePower: 30,
    category: "Physical",
    name: "Lick",
    pp: 30,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Ghost",
    contestType: "Cute"
  },
  lifedew: {
    num: 791,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Life Dew",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, heal: 1, bypasssub: 1 },
    heal: [1, 4],
    secondary: null,
    target: "allies",
    type: "Water"
  },
  lightofruin: {
    num: 617,
    accuracy: 90,
    basePower: 140,
    category: "Special",
    isNonstandard: "Past",
    name: "Light of Ruin",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    recoil: [1, 2],
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Beautiful"
  },
  lightscreen: {
    num: 113,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Light Screen",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    sideCondition: "lightscreen",
    condition: {
      duration: 5,
      durationCallback(target, source, effect) {
        if (source?.hasItem("lightclay")) {
          return 8;
        }
        return 5;
      },
      onAnyModifyDamage(damage, source, target, move) {
        if (target !== source && this.effectState.target.hasAlly(target) && this.getCategory(move) === "Special") {
          if (!target.getMoveHitData(move).crit && !move.infiltrates) {
            this.debug("Light Screen weaken");
            if (this.activePerHalf > 1)
              return this.chainModify([2732, 4096]);
            return this.chainModify(0.5);
          }
        }
      },
      onSideStart(side) {
        this.add("-sidestart", side, "move: Light Screen");
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 2,
      onSideEnd(side) {
        this.add("-sideend", side, "move: Light Screen");
      }
    },
    secondary: null,
    target: "allySide",
    type: "Psychic",
    zMove: { boost: { spd: 1 } },
    contestType: "Beautiful"
  },
  lightthatburnsthesky: {
    num: 723,
    accuracy: true,
    basePower: 200,
    category: "Special",
    isNonstandard: "Past",
    name: "Light That Burns the Sky",
    pp: 1,
    priority: 0,
    flags: {},
    onModifyMove(move, pokemon) {
      if (pokemon.getStat("atk", false, true) > pokemon.getStat("spa", false, true))
        move.category = "Physical";
    },
    ignoreAbility: true,
    isZ: "ultranecroziumz",
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  liquidation: {
    num: 710,
    accuracy: 100,
    basePower: 85,
    category: "Physical",
    name: "Liquidation",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  lockon: {
    num: 199,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Lock-On",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onTryHit(target, source) {
      if (source.volatiles["lockon"])
        return false;
    },
    onHit(target, source) {
      source.addVolatile("lockon", target);
      this.add("-activate", source, "move: Lock-On", "[of] " + target);
    },
    condition: {
      noCopy: true,
      // doesn't get copied by Baton Pass
      duration: 2,
      onSourceInvulnerabilityPriority: 1,
      onSourceInvulnerability(target, source, move) {
        if (move && source === this.effectState.target && target === this.effectState.source)
          return 0;
      },
      onSourceAccuracy(accuracy, target, source, move) {
        if (move && source === this.effectState.target && target === this.effectState.source)
          return true;
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  lovelykiss: {
    num: 142,
    accuracy: 75,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Lovely Kiss",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    status: "slp",
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Beautiful"
  },
  lowkick: {
    num: 67,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      const targetWeight = target.getWeight();
      let bp;
      if (targetWeight >= 2e3) {
        bp = 120;
      } else if (targetWeight >= 1e3) {
        bp = 100;
      } else if (targetWeight >= 500) {
        bp = 80;
      } else if (targetWeight >= 250) {
        bp = 60;
      } else if (targetWeight >= 100) {
        bp = 40;
      } else {
        bp = 20;
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Low Kick",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onTryHit(target, pokemon, move) {
      if (target.volatiles["dynamax"]) {
        this.add("-fail", pokemon, "Dynamax");
        this.attrLastMove("[still]");
        return null;
      }
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    zMove: { basePower: 160 },
    contestType: "Tough"
  },
  lowsweep: {
    num: 490,
    accuracy: 100,
    basePower: 65,
    category: "Physical",
    name: "Low Sweep",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "normal",
    type: "Fighting",
    contestType: "Clever"
  },
  luckychant: {
    num: 381,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Lucky Chant",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    sideCondition: "luckychant",
    condition: {
      duration: 5,
      onSideStart(side) {
        this.add("-sidestart", side, "move: Lucky Chant");
      },
      onCriticalHit: false,
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 6,
      onSideEnd(side) {
        this.add("-sideend", side, "move: Lucky Chant");
      }
    },
    secondary: null,
    target: "allySide",
    type: "Normal",
    zMove: { boost: { evasion: 1 } },
    contestType: "Cute"
  },
  luminacrash: {
    num: 855,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Lumina Crash",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spd: -2
      }
    },
    target: "normal",
    type: "Psychic"
  },
  lunarblessing: {
    num: 849,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Lunar Blessing",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    onHit(pokemon) {
      const success = !!this.heal(this.modify(pokemon.maxhp, 0.25));
      return pokemon.cureStatus() || success;
    },
    secondary: null,
    target: "allies",
    type: "Psychic"
  },
  lunardance: {
    num: 461,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Lunar Dance",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, dance: 1, heal: 1, metronome: 1 },
    onTryHit(source) {
      if (!this.canSwitch(source.side)) {
        this.attrLastMove("[still]");
        this.add("-fail", source);
        return this.NOT_FAIL;
      }
    },
    selfdestruct: "ifHit",
    slotCondition: "lunardance",
    condition: {
      onSwap(target) {
        if (!target.fainted && (target.hp < target.maxhp || target.status || target.moveSlots.some((moveSlot) => moveSlot.pp < moveSlot.maxpp))) {
          target.heal(target.maxhp);
          target.clearStatus();
          for (const moveSlot of target.moveSlots) {
            moveSlot.pp = moveSlot.maxpp;
          }
          this.add("-heal", target, target.getHealth, "[from] move: Lunar Dance");
          target.side.removeSlotCondition(target, "lunardance");
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    contestType: "Beautiful"
  },
  lunge: {
    num: 679,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Lunge",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        atk: -1
      }
    },
    target: "normal",
    type: "Bug",
    contestType: "Cute"
  },
  lusterpurge: {
    num: 295,
    accuracy: 100,
    basePower: 95,
    category: "Special",
    name: "Luster Purge",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 50,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  machpunch: {
    num: 183,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Mach Punch",
    pp: 30,
    priority: 1,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  magicalleaf: {
    num: 345,
    accuracy: true,
    basePower: 60,
    category: "Special",
    name: "Magical Leaf",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Beautiful"
  },
  magicaltorque: {
    num: 900,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    isNonstandard: "Unobtainable",
    name: "Magical Torque",
    pp: 10,
    priority: 0,
    flags: {
      protect: 1,
      failencore: 1,
      failmefirst: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    noSketch: true,
    secondary: {
      chance: 30,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Fairy"
  },
  magiccoat: {
    num: 277,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Magic Coat",
    pp: 15,
    priority: 4,
    flags: { metronome: 1 },
    volatileStatus: "magiccoat",
    condition: {
      duration: 1,
      onStart(target, source, effect) {
        this.add("-singleturn", target, "move: Magic Coat");
        if (effect?.effectType === "Move") {
          this.effectState.pranksterBoosted = effect.pranksterBoosted;
        }
      },
      onTryHitPriority: 2,
      onTryHit(target, source, move) {
        if (target === source || move.hasBounced || !move.flags["reflectable"]) {
          return;
        }
        const newMove = this.dex.getActiveMove(move.id);
        newMove.hasBounced = true;
        newMove.pranksterBoosted = this.effectState.pranksterBoosted;
        this.actions.useMove(newMove, target, source);
        return null;
      },
      onAllyTryHitSide(target, source, move) {
        if (target.isAlly(source) || move.hasBounced || !move.flags["reflectable"]) {
          return;
        }
        const newMove = this.dex.getActiveMove(move.id);
        newMove.hasBounced = true;
        newMove.pranksterBoosted = false;
        this.actions.useMove(newMove, this.effectState.target, source);
        return null;
      }
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { boost: { spd: 2 } },
    contestType: "Beautiful"
  },
  magicpowder: {
    num: 750,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Magic Powder",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1, powder: 1 },
    onHit(target) {
      if (target.getTypes().join() === "Psychic" || !target.setType("Psychic"))
        return false;
      this.add("-start", target, "typechange", "Psychic");
    },
    secondary: null,
    target: "normal",
    type: "Psychic"
  },
  magicroom: {
    num: 478,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Magic Room",
    pp: 10,
    priority: 0,
    flags: { mirror: 1, metronome: 1 },
    pseudoWeather: "magicroom",
    condition: {
      duration: 5,
      durationCallback(source, effect) {
        if (source?.hasAbility("persistent")) {
          this.add("-activate", source, "ability: Persistent", "[move] Magic Room");
          return 7;
        }
        return 5;
      },
      onFieldStart(target, source) {
        if (source?.hasAbility("persistent")) {
          this.add("-fieldstart", "move: Magic Room", "[of] " + source, "[persistent]");
        } else {
          this.add("-fieldstart", "move: Magic Room", "[of] " + source);
        }
        for (const mon of this.getAllActive()) {
          this.singleEvent("End", mon.getItem(), mon.itemState, mon);
        }
      },
      onFieldRestart(target, source) {
        this.field.removePseudoWeather("magicroom");
      },
      // Item suppression implemented in Pokemon.ignoringItem() within sim/pokemon.js
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 6,
      onFieldEnd() {
        this.add("-fieldend", "move: Magic Room", "[of] " + this.effectState.source);
      }
    },
    secondary: null,
    target: "all",
    type: "Psychic",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  magmastorm: {
    num: 463,
    accuracy: 75,
    basePower: 100,
    category: "Special",
    name: "Magma Storm",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Tough"
  },
  magnetbomb: {
    num: 443,
    accuracy: true,
    basePower: 60,
    category: "Physical",
    isNonstandard: "Past",
    name: "Magnet Bomb",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: null,
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  magneticflux: {
    num: 602,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Magnetic Flux",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, distance: 1, bypasssub: 1, metronome: 1 },
    onHitSide(side, source, move) {
      const targets = side.allies().filter((ally) => ally.hasAbility(["plus", "minus"]) && (!ally.volatiles["maxguard"] || this.runEvent("TryHit", ally, source, move)));
      if (!targets.length)
        return false;
      let didSomething = false;
      for (const target of targets) {
        didSomething = this.boost({ def: 1, spd: 1 }, target, source, move, false, true) || didSomething;
      }
      return didSomething;
    },
    secondary: null,
    target: "allySide",
    type: "Electric",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  magnetrise: {
    num: 393,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Magnet Rise",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, gravity: 1, metronome: 1 },
    volatileStatus: "magnetrise",
    onTry(source, target, move) {
      if (target.volatiles["smackdown"] || target.volatiles["ingrain"])
        return false;
      if (this.field.getPseudoWeather("Gravity")) {
        this.add("cant", source, "move: Gravity", move);
        return null;
      }
    },
    condition: {
      duration: 5,
      onStart(target) {
        this.add("-start", target, "Magnet Rise");
      },
      onImmunity(type) {
        if (type === "Ground")
          return false;
      },
      onResidualOrder: 18,
      onEnd(target) {
        this.add("-end", target, "Magnet Rise");
      }
    },
    secondary: null,
    target: "self",
    type: "Electric",
    zMove: { boost: { evasion: 1 } },
    contestType: "Clever"
  },
  magnitude: {
    num: 222,
    accuracy: 100,
    basePower: 0,
    category: "Physical",
    isNonstandard: "Past",
    name: "Magnitude",
    pp: 30,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    onModifyMove(move, pokemon) {
      const i = this.random(100);
      if (i < 5) {
        move.magnitude = 4;
        move.basePower = 10;
      } else if (i < 15) {
        move.magnitude = 5;
        move.basePower = 30;
      } else if (i < 35) {
        move.magnitude = 6;
        move.basePower = 50;
      } else if (i < 65) {
        move.magnitude = 7;
        move.basePower = 70;
      } else if (i < 85) {
        move.magnitude = 8;
        move.basePower = 90;
      } else if (i < 95) {
        move.magnitude = 9;
        move.basePower = 110;
      } else {
        move.magnitude = 10;
        move.basePower = 150;
      }
    },
    onUseMoveMessage(pokemon, target, move) {
      this.add("-activate", pokemon, "move: Magnitude", move.magnitude);
    },
    secondary: null,
    target: "allAdjacent",
    type: "Ground",
    zMove: { basePower: 140 },
    maxMove: { basePower: 140 },
    contestType: "Tough"
  },
  makeitrain: {
    num: 874,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    name: "Make It Rain",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    self: {
      boosts: {
        spa: -1
      }
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Steel",
    contestType: "Beautiful"
  },
  maliciousmoonsault: {
    num: 696,
    accuracy: true,
    basePower: 180,
    category: "Physical",
    isNonstandard: "Past",
    name: "Malicious Moonsault",
    pp: 1,
    priority: 0,
    flags: { contact: 1 },
    isZ: "inciniumz",
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Cool"
  },
  malignantchain: {
    num: 919,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Malignant Chain",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 50,
      status: "tox"
    },
    target: "normal",
    type: "Poison"
  },
  matblock: {
    num: 561,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Mat Block",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, nonsky: 1, noassist: 1, failcopycat: 1 },
    stallingMove: true,
    sideCondition: "matblock",
    onTry(source) {
      if (source.activeMoveActions > 1) {
        this.hint("Mat Block only works on your first turn out.");
        return false;
      }
      return !!this.queue.willAct();
    },
    condition: {
      duration: 1,
      onSideStart(target, source) {
        this.add("-singleturn", source, "Mat Block");
      },
      onTryHitPriority: 3,
      onTryHit(target, source, move) {
        if (!move.flags["protect"]) {
          if (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          if (move.isZ || move.isMax)
            target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        if (move && (move.target === "self" || move.category === "Status"))
          return;
        this.add("-activate", target, "move: Mat Block", move.name);
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        return this.NOT_FAIL;
      }
    },
    secondary: null,
    target: "allySide",
    type: "Fighting",
    zMove: { boost: { def: 1 } },
    contestType: "Cool"
  },
  matchagotcha: {
    num: 902,
    accuracy: 90,
    basePower: 80,
    category: "Special",
    name: "Matcha Gotcha",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    thawsTarget: true,
    secondary: {
      chance: 20,
      status: "brn"
    },
    target: "allAdjacentFoes",
    type: "Grass"
  },
  maxairstream: {
    num: 766,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Airstream",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.alliesAndSelf()) {
          this.boost({ spe: 1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Flying",
    contestType: "Cool"
  },
  maxdarkness: {
    num: 772,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Darkness",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.foes()) {
          this.boost({ spd: -1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Dark",
    contestType: "Cool"
  },
  maxflare: {
    num: 757,
    accuracy: true,
    basePower: 100,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Flare",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        this.field.setWeather("sunnyday");
      }
    },
    target: "adjacentFoe",
    type: "Fire",
    contestType: "Cool"
  },
  maxflutterby: {
    num: 758,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Flutterby",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.foes()) {
          this.boost({ spa: -1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Bug",
    contestType: "Cool"
  },
  maxgeyser: {
    num: 765,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Geyser",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        this.field.setWeather("raindance");
      }
    },
    target: "adjacentFoe",
    type: "Water",
    contestType: "Cool"
  },
  maxguard: {
    num: 743,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Max Guard",
    pp: 10,
    priority: 4,
    flags: {},
    isMax: true,
    stallingMove: true,
    volatileStatus: "maxguard",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    onHit(pokemon) {
      pokemon.addVolatile("stall");
    },
    condition: {
      duration: 1,
      onStart(target) {
        this.add("-singleturn", target, "Max Guard");
      },
      onTryHitPriority: 3,
      onTryHit(target, source, move) {
        const bypassesMaxGuard = [
          "acupressure",
          "afteryou",
          "allyswitch",
          "aromatherapy",
          "aromaticmist",
          "coaching",
          "confide",
          "copycat",
          "curse",
          "decorate",
          "doomdesire",
          "feint",
          "futuresight",
          "gmaxoneblow",
          "gmaxrapidflow",
          "healbell",
          "holdhands",
          "howl",
          "junglehealing",
          "lifedew",
          "meanlook",
          "perishsong",
          "playnice",
          "powertrick",
          "roar",
          "roleplay",
          "tearfullook"
        ];
        if (bypassesMaxGuard.includes(move.id))
          return;
        if (move.smartTarget) {
          move.smartTarget = false;
        } else {
          this.add("-activate", target, "move: Max Guard");
        }
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        return this.NOT_FAIL;
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    contestType: "Cool"
  },
  maxhailstorm: {
    num: 763,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Hailstorm",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        this.field.setWeather("hail");
      }
    },
    target: "adjacentFoe",
    type: "Ice",
    contestType: "Cool"
  },
  maxknuckle: {
    num: 761,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Knuckle",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.alliesAndSelf()) {
          this.boost({ atk: 1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Fighting",
    contestType: "Cool"
  },
  maxlightning: {
    num: 759,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Lightning",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        this.field.setTerrain("electricterrain");
      }
    },
    target: "adjacentFoe",
    type: "Electric",
    contestType: "Cool"
  },
  maxmindstorm: {
    num: 769,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Mindstorm",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        this.field.setTerrain("psychicterrain");
      }
    },
    target: "adjacentFoe",
    type: "Psychic",
    contestType: "Cool"
  },
  maxooze: {
    num: 764,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Ooze",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.alliesAndSelf()) {
          this.boost({ spa: 1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Poison",
    contestType: "Cool"
  },
  maxovergrowth: {
    num: 773,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Overgrowth",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        this.field.setTerrain("grassyterrain");
      }
    },
    target: "adjacentFoe",
    type: "Grass",
    contestType: "Cool"
  },
  maxphantasm: {
    num: 762,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Phantasm",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.foes()) {
          this.boost({ def: -1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Ghost",
    contestType: "Cool"
  },
  maxquake: {
    num: 771,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Quake",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.alliesAndSelf()) {
          this.boost({ spd: 1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Ground",
    contestType: "Cool"
  },
  maxrockfall: {
    num: 770,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Rockfall",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        this.field.setWeather("sandstorm");
      }
    },
    target: "adjacentFoe",
    type: "Rock",
    contestType: "Cool"
  },
  maxstarfall: {
    num: 767,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Starfall",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        this.field.setTerrain("mistyterrain");
      }
    },
    target: "adjacentFoe",
    type: "Fairy",
    contestType: "Cool"
  },
  maxsteelspike: {
    num: 774,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Steelspike",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.alliesAndSelf()) {
          this.boost({ def: 1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Steel",
    contestType: "Cool"
  },
  maxstrike: {
    num: 760,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Strike",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.foes()) {
          this.boost({ spe: -1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Normal",
    contestType: "Cool"
  },
  maxwyrmwind: {
    num: 768,
    accuracy: true,
    basePower: 10,
    category: "Physical",
    isNonstandard: "Past",
    name: "Max Wyrmwind",
    pp: 10,
    priority: 0,
    flags: {},
    isMax: true,
    self: {
      onHit(source) {
        if (!source.volatiles["dynamax"])
          return;
        for (const pokemon of source.foes()) {
          this.boost({ atk: -1 }, pokemon);
        }
      }
    },
    target: "adjacentFoe",
    type: "Dragon",
    contestType: "Cool"
  },
  meanlook: {
    num: 212,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Mean Look",
    pp: 5,
    priority: 0,
    flags: { reflectable: 1, mirror: 1, metronome: 1 },
    onHit(target, source, move) {
      return target.addVolatile("trapped", source, move, "trapper");
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spd: 1 } },
    contestType: "Beautiful"
  },
  meditate: {
    num: 96,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Meditate",
    pp: 40,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      atk: 1
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { boost: { atk: 1 } },
    contestType: "Beautiful"
  },
  mefirst: {
    num: 382,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Me First",
    pp: 20,
    priority: 0,
    flags: {
      protect: 1,
      bypasssub: 1,
      failencore: 1,
      failmefirst: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    onTryHit(target, pokemon) {
      const action = this.queue.willMove(target);
      if (!action)
        return false;
      const move = this.dex.getActiveMove(action.move.id);
      if (action.zmove || move.isZ || move.isMax)
        return false;
      if (target.volatiles["mustrecharge"])
        return false;
      if (move.category === "Status" || move.flags["failmefirst"])
        return false;
      pokemon.addVolatile("mefirst");
      this.actions.useMove(move, pokemon, target);
      return null;
    },
    condition: {
      duration: 1,
      onBasePowerPriority: 12,
      onBasePower(basePower) {
        return this.chainModify(1.5);
      }
    },
    secondary: null,
    target: "adjacentFoe",
    type: "Normal",
    zMove: { boost: { spe: 2 } },
    contestType: "Clever"
  },
  megadrain: {
    num: 72,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Mega Drain",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { basePower: 120 },
    contestType: "Clever"
  },
  megahorn: {
    num: 224,
    accuracy: 85,
    basePower: 120,
    category: "Physical",
    name: "Megahorn",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cool"
  },
  megakick: {
    num: 25,
    accuracy: 75,
    basePower: 120,
    category: "Physical",
    name: "Mega Kick",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  megapunch: {
    num: 5,
    accuracy: 85,
    basePower: 80,
    category: "Physical",
    name: "Mega Punch",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  memento: {
    num: 262,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Memento",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    boosts: {
      atk: -2,
      spa: -2
    },
    selfdestruct: "ifHit",
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { effect: "healreplacement" },
    contestType: "Tough"
  },
  menacingmoonrazemaelstrom: {
    num: 725,
    accuracy: true,
    basePower: 200,
    category: "Special",
    isNonstandard: "Past",
    name: "Menacing Moonraze Maelstrom",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "lunaliumz",
    ignoreAbility: true,
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  metalburst: {
    num: 368,
    accuracy: 100,
    basePower: 0,
    damageCallback(pokemon) {
      const lastDamagedBy = pokemon.getLastDamagedBy(true);
      if (lastDamagedBy !== void 0) {
        return lastDamagedBy.damage * 1.5 || 1;
      }
      return 0;
    },
    category: "Physical",
    name: "Metal Burst",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, failmefirst: 1 },
    onTry(source) {
      const lastDamagedBy = source.getLastDamagedBy(true);
      if (lastDamagedBy === void 0 || !lastDamagedBy.thisTurn)
        return false;
    },
    onModifyTarget(targetRelayVar, source, target, move) {
      const lastDamagedBy = source.getLastDamagedBy(true);
      if (lastDamagedBy) {
        targetRelayVar.target = this.getAtSlot(lastDamagedBy.slot);
      }
    },
    secondary: null,
    target: "scripted",
    type: "Steel",
    contestType: "Cool"
  },
  metalclaw: {
    num: 232,
    accuracy: 95,
    basePower: 50,
    category: "Physical",
    name: "Metal Claw",
    pp: 35,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      self: {
        boosts: {
          atk: 1
        }
      }
    },
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  metalsound: {
    num: 319,
    accuracy: 85,
    basePower: 0,
    category: "Status",
    name: "Metal Sound",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    boosts: {
      spd: -2
    },
    secondary: null,
    target: "normal",
    type: "Steel",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  meteorassault: {
    num: 794,
    accuracy: 100,
    basePower: 150,
    category: "Physical",
    isNonstandard: "Past",
    name: "Meteor Assault",
    pp: 5,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1, failinstruct: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Fighting"
  },
  meteorbeam: {
    num: 800,
    accuracy: 90,
    basePower: 120,
    category: "Special",
    name: "Meteor Beam",
    pp: 10,
    priority: 0,
    flags: { charge: 1, protect: 1, mirror: 1, metronome: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      this.boost({ spa: 1 }, attacker, attacker, move);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    secondary: null,
    target: "normal",
    type: "Rock"
  },
  meteormash: {
    num: 309,
    accuracy: 90,
    basePower: 90,
    category: "Physical",
    name: "Meteor Mash",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: {
      chance: 20,
      self: {
        boosts: {
          atk: 1
        }
      }
    },
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  metronome: {
    num: 118,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Metronome",
    pp: 10,
    priority: 0,
    flags: { failencore: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    onHit(target, source, effect) {
      const moves = this.dex.moves.all().filter((move) => (![2, 4].includes(this.gen) || !source.moves.includes(move.id)) && (!move.isNonstandard || move.isNonstandard === "Unobtainable") && move.flags["metronome"]);
      let randomMove = "";
      if (moves.length) {
        moves.sort((a, b) => a.num - b.num);
        randomMove = this.sample(moves).id;
      }
      if (!randomMove)
        return false;
      source.side.lastSelectedMove = this.toID(randomMove);
      this.actions.useMove(randomMove, target);
    },
    secondary: null,
    target: "self",
    type: "Normal",
    contestType: "Cute"
  },
  mightycleave: {
    num: 910,
    accuracy: 100,
    basePower: 95,
    category: "Physical",
    name: "Mighty Cleave",
    pp: 5,
    priority: 0,
    flags: { contact: 1, mirror: 1, metronome: 1, slicing: 1 },
    secondary: null,
    target: "normal",
    type: "Rock"
  },
  milkdrink: {
    num: 208,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Milk Drink",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    heal: [1, 2],
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  mimic: {
    num: 102,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Mimic",
    pp: 10,
    priority: 0,
    flags: {
      protect: 1,
      bypasssub: 1,
      allyanim: 1,
      failencore: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    onHit(target, source) {
      const move = target.lastMove;
      if (source.transformed || !move || move.flags["failmimic"] || source.moves.includes(move.id)) {
        return false;
      }
      if (move.isZ || move.isMax)
        return false;
      const mimicIndex = source.moves.indexOf("mimic");
      if (mimicIndex < 0)
        return false;
      source.moveSlots[mimicIndex] = {
        move: move.name,
        id: move.id,
        pp: move.pp,
        maxpp: move.pp,
        target: move.target,
        disabled: false,
        used: false,
        virtual: true
      };
      this.add("-start", source, "Mimic", move.name);
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { accuracy: 1 } },
    contestType: "Cute"
  },
  mindblown: {
    num: 720,
    accuracy: 100,
    basePower: 150,
    category: "Special",
    isNonstandard: "Past",
    name: "Mind Blown",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    mindBlownRecoil: true,
    onAfterMove(pokemon, target, move) {
      if (move.mindBlownRecoil && !move.multihit) {
        const hpBeforeRecoil = pokemon.hp;
        this.damage(Math.round(pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get("Mind Blown"), true);
        if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
          this.runEvent("EmergencyExit", pokemon, pokemon);
        }
      }
    },
    secondary: null,
    target: "allAdjacent",
    type: "Fire",
    contestType: "Cool"
  },
  mindreader: {
    num: 170,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Mind Reader",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onTryHit(target, source) {
      if (source.volatiles["lockon"])
        return false;
    },
    onHit(target, source) {
      source.addVolatile("lockon", target);
      this.add("-activate", source, "move: Mind Reader", "[of] " + target);
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  minimize: {
    num: 107,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Minimize",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    volatileStatus: "minimize",
    condition: {
      noCopy: true,
      onRestart: () => null,
      onSourceModifyDamage(damage, source, target, move) {
        const boostedMoves = [
          "stomp",
          "steamroller",
          "bodyslam",
          "flyingpress",
          "dragonrush",
          "heatcrash",
          "heavyslam",
          "maliciousmoonsault"
        ];
        if (boostedMoves.includes(move.id)) {
          return this.chainModify(2);
        }
      },
      onAccuracy(accuracy, target, source, move) {
        const boostedMoves = [
          "stomp",
          "steamroller",
          "bodyslam",
          "flyingpress",
          "dragonrush",
          "heatcrash",
          "heavyslam",
          "maliciousmoonsault"
        ];
        if (boostedMoves.includes(move.id)) {
          return true;
        }
        return accuracy;
      }
    },
    boosts: {
      evasion: 2
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  miracleeye: {
    num: 357,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Miracle Eye",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    volatileStatus: "miracleeye",
    onTryHit(target) {
      if (target.volatiles["foresight"])
        return false;
    },
    condition: {
      noCopy: true,
      onStart(pokemon) {
        this.add("-start", pokemon, "Miracle Eye");
      },
      onNegateImmunity(pokemon, type) {
        if (pokemon.hasType("Dark") && type === "Psychic")
          return false;
      },
      onModifyBoost(boosts) {
        if (boosts.evasion && boosts.evasion > 0) {
          boosts.evasion = 0;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  mirrorcoat: {
    num: 243,
    accuracy: 100,
    basePower: 0,
    damageCallback(pokemon) {
      if (!pokemon.volatiles["mirrorcoat"])
        return 0;
      return pokemon.volatiles["mirrorcoat"].damage || 1;
    },
    category: "Special",
    name: "Mirror Coat",
    pp: 20,
    priority: -5,
    flags: { protect: 1, failmefirst: 1, noassist: 1 },
    beforeTurnCallback(pokemon) {
      pokemon.addVolatile("mirrorcoat");
    },
    onTry(source) {
      if (!source.volatiles["mirrorcoat"])
        return false;
      if (source.volatiles["mirrorcoat"].slot === null)
        return false;
    },
    condition: {
      duration: 1,
      noCopy: true,
      onStart(target, source, move) {
        this.effectState.slot = null;
        this.effectState.damage = 0;
      },
      onRedirectTargetPriority: -1,
      onRedirectTarget(target, source, source2, move) {
        if (move.id !== "mirrorcoat")
          return;
        if (source !== this.effectState.target || !this.effectState.slot)
          return;
        return this.getAtSlot(this.effectState.slot);
      },
      onDamagingHit(damage, target, source, move) {
        if (!source.isAlly(target) && this.getCategory(move) === "Special") {
          this.effectState.slot = source.getSlot();
          this.effectState.damage = 2 * damage;
        }
      }
    },
    secondary: null,
    target: "scripted",
    type: "Psychic",
    contestType: "Beautiful"
  },
  mirrormove: {
    num: 119,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Mirror Move",
    pp: 20,
    priority: 0,
    flags: { failencore: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    onTryHit(target, pokemon) {
      const move = target.lastMove;
      if (!move?.flags["mirror"] || move.isZ || move.isMax) {
        return false;
      }
      this.actions.useMove(move.id, pokemon, target);
      return null;
    },
    secondary: null,
    target: "normal",
    type: "Flying",
    zMove: { boost: { atk: 2 } },
    contestType: "Clever"
  },
  mirrorshot: {
    num: 429,
    accuracy: 85,
    basePower: 65,
    category: "Special",
    isNonstandard: "Past",
    name: "Mirror Shot",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      boosts: {
        accuracy: -1
      }
    },
    target: "normal",
    type: "Steel",
    contestType: "Beautiful"
  },
  mist: {
    num: 54,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Mist",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    sideCondition: "mist",
    condition: {
      duration: 5,
      onTryBoost(boost, target, source, effect) {
        if (effect.effectType === "Move" && effect.infiltrates && !target.isAlly(source))
          return;
        if (source && target !== source) {
          let showMsg = false;
          let i;
          for (i in boost) {
            if (boost[i] < 0) {
              delete boost[i];
              showMsg = true;
            }
          }
          if (showMsg && !effect.secondaries) {
            this.add("-activate", target, "move: Mist");
          }
        }
      },
      onSideStart(side) {
        this.add("-sidestart", side, "Mist");
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 4,
      onSideEnd(side) {
        this.add("-sideend", side, "Mist");
      }
    },
    secondary: null,
    target: "allySide",
    type: "Ice",
    zMove: { effect: "heal" },
    contestType: "Beautiful"
  },
  mistball: {
    num: 296,
    accuracy: 100,
    basePower: 95,
    category: "Special",
    name: "Mist Ball",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 50,
      boosts: {
        spa: -1
      }
    },
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  mistyexplosion: {
    num: 802,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Misty Explosion",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    selfdestruct: "always",
    onBasePower(basePower, source) {
      if (this.field.isTerrain("mistyterrain") && source.isGrounded()) {
        this.debug("misty terrain boost");
        return this.chainModify(1.5);
      }
    },
    secondary: null,
    target: "allAdjacent",
    type: "Fairy"
  },
  mistyterrain: {
    num: 581,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Misty Terrain",
    pp: 10,
    priority: 0,
    flags: { nonsky: 1, metronome: 1 },
    terrain: "mistyterrain",
    condition: {
      duration: 5,
      durationCallback(source, effect) {
        if (source?.hasItem("terrainextender")) {
          return 8;
        }
        return 5;
      },
      onSetStatus(status, target, source, effect) {
        if (!target.isGrounded() || target.isSemiInvulnerable())
          return;
        if (effect && (effect.status || effect.id === "yawn")) {
          this.add("-activate", target, "move: Misty Terrain");
        }
        return false;
      },
      onTryAddVolatile(status, target, source, effect) {
        if (!target.isGrounded() || target.isSemiInvulnerable())
          return;
        if (status.id === "confusion") {
          if (effect.effectType === "Move" && !effect.secondaries)
            this.add("-activate", target, "move: Misty Terrain");
          return null;
        }
      },
      onBasePowerPriority: 6,
      onBasePower(basePower, attacker, defender, move) {
        if (move.type === "Dragon" && defender.isGrounded() && !defender.isSemiInvulnerable()) {
          this.debug("misty terrain weaken");
          return this.chainModify(0.5);
        }
      },
      onFieldStart(field, source, effect) {
        if (effect?.effectType === "Ability") {
          this.add("-fieldstart", "move: Misty Terrain", "[from] ability: " + effect.name, "[of] " + source);
        } else {
          this.add("-fieldstart", "move: Misty Terrain");
        }
      },
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 7,
      onFieldEnd() {
        this.add("-fieldend", "Misty Terrain");
      }
    },
    secondary: null,
    target: "all",
    type: "Fairy",
    zMove: { boost: { spd: 1 } },
    contestType: "Beautiful"
  },
  moonblast: {
    num: 585,
    accuracy: 100,
    basePower: 95,
    category: "Special",
    name: "Moonblast",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      boosts: {
        spa: -1
      }
    },
    target: "normal",
    type: "Fairy",
    contestType: "Beautiful"
  },
  moongeistbeam: {
    num: 714,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Moongeist Beam",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    ignoreAbility: true,
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  moonlight: {
    num: 236,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Moonlight",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    onHit(pokemon) {
      let factor = 0.5;
      switch (pokemon.effectiveWeather()) {
        case "sunnyday":
        case "desolateland":
          factor = 0.667;
          break;
        case "raindance":
        case "primordialsea":
        case "sandstorm":
        case "hail":
        case "snow":
          factor = 0.25;
          break;
      }
      const success = !!this.heal(this.modify(pokemon.maxhp, factor));
      if (!success) {
        this.add("-fail", pokemon, "heal");
        return this.NOT_FAIL;
      }
      return success;
    },
    secondary: null,
    target: "self",
    type: "Fairy",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  morningsun: {
    num: 234,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Morning Sun",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    onHit(pokemon) {
      let factor = 0.5;
      switch (pokemon.effectiveWeather()) {
        case "sunnyday":
        case "desolateland":
          factor = 0.667;
          break;
        case "raindance":
        case "primordialsea":
        case "sandstorm":
        case "hail":
        case "snow":
          factor = 0.25;
          break;
      }
      const success = !!this.heal(this.modify(pokemon.maxhp, factor));
      if (!success) {
        this.add("-fail", pokemon, "heal");
        return this.NOT_FAIL;
      }
      return success;
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  mortalspin: {
    num: 866,
    accuracy: 100,
    basePower: 30,
    category: "Physical",
    name: "Mortal Spin",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onAfterHit(target, pokemon, move) {
      if (!move.hasSheerForce) {
        if (pokemon.hp && pokemon.removeVolatile("leechseed")) {
          this.add("-end", pokemon, "Leech Seed", "[from] move: Mortal Spin", "[of] " + pokemon);
        }
        const sideConditions = ["spikes", "toxicspikes", "stealthrock", "stickyweb", "gmaxsteelsurge"];
        for (const condition of sideConditions) {
          if (pokemon.hp && pokemon.side.removeSideCondition(condition)) {
            this.add("-sideend", pokemon.side, this.dex.conditions.get(condition).name, "[from] move: Mortal Spin", "[of] " + pokemon);
          }
        }
        if (pokemon.hp && pokemon.volatiles["partiallytrapped"]) {
          pokemon.removeVolatile("partiallytrapped");
        }
      }
    },
    onAfterSubDamage(damage, target, pokemon, move) {
      if (!move.hasSheerForce) {
        if (pokemon.hp && pokemon.removeVolatile("leechseed")) {
          this.add("-end", pokemon, "Leech Seed", "[from] move: Mortal Spin", "[of] " + pokemon);
        }
        const sideConditions = ["spikes", "toxicspikes", "stealthrock", "stickyweb", "gmaxsteelsurge"];
        for (const condition of sideConditions) {
          if (pokemon.hp && pokemon.side.removeSideCondition(condition)) {
            this.add("-sideend", pokemon.side, this.dex.conditions.get(condition).name, "[from] move: Mortal Spin", "[of] " + pokemon);
          }
        }
        if (pokemon.hp && pokemon.volatiles["partiallytrapped"]) {
          pokemon.removeVolatile("partiallytrapped");
        }
      }
    },
    secondary: {
      chance: 100,
      status: "psn"
    },
    target: "allAdjacentFoes",
    type: "Poison"
  },
  mountaingale: {
    num: 836,
    accuracy: 85,
    basePower: 100,
    category: "Physical",
    name: "Mountain Gale",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Ice"
  },
  mudbomb: {
    num: 426,
    accuracy: 85,
    basePower: 65,
    category: "Special",
    isNonstandard: "Past",
    name: "Mud Bomb",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 30,
      boosts: {
        accuracy: -1
      }
    },
    target: "normal",
    type: "Ground",
    contestType: "Cute"
  },
  mudshot: {
    num: 341,
    accuracy: 95,
    basePower: 55,
    category: "Special",
    name: "Mud Shot",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "normal",
    type: "Ground",
    contestType: "Tough"
  },
  mudslap: {
    num: 189,
    accuracy: 100,
    basePower: 20,
    category: "Special",
    name: "Mud-Slap",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        accuracy: -1
      }
    },
    target: "normal",
    type: "Ground",
    contestType: "Cute"
  },
  mudsport: {
    num: 300,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Mud Sport",
    pp: 15,
    priority: 0,
    flags: { nonsky: 1, metronome: 1 },
    pseudoWeather: "mudsport",
    condition: {
      duration: 5,
      onFieldStart(field, source) {
        this.add("-fieldstart", "move: Mud Sport", "[of] " + source);
      },
      onBasePowerPriority: 1,
      onBasePower(basePower, attacker, defender, move) {
        if (move.type === "Electric") {
          this.debug("mud sport weaken");
          return this.chainModify([1352, 4096]);
        }
      },
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 4,
      onFieldEnd() {
        this.add("-fieldend", "move: Mud Sport");
      }
    },
    secondary: null,
    target: "all",
    type: "Ground",
    zMove: { boost: { spd: 1 } },
    contestType: "Cute"
  },
  muddywater: {
    num: 330,
    accuracy: 85,
    basePower: 90,
    category: "Special",
    name: "Muddy Water",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: {
      chance: 30,
      boosts: {
        accuracy: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Water",
    contestType: "Tough"
  },
  multiattack: {
    num: 718,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    isNonstandard: "Past",
    name: "Multi-Attack",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onModifyType(move, pokemon) {
      if (pokemon.ignoringItem())
        return;
      move.type = this.runEvent("Memory", pokemon, null, move, "Normal");
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 185 },
    maxMove: { basePower: 95 },
    contestType: "Tough"
  },
  mysticalfire: {
    num: 595,
    accuracy: 100,
    basePower: 75,
    category: "Special",
    name: "Mystical Fire",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spa: -1
      }
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  mysticalpower: {
    num: 832,
    accuracy: 90,
    basePower: 70,
    category: "Special",
    name: "Mystical Power",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          spa: 1
        }
      }
    },
    target: "normal",
    type: "Psychic"
  },
  nastyplot: {
    num: 417,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Nasty Plot",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      spa: 2
    },
    secondary: null,
    target: "self",
    type: "Dark",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  naturalgift: {
    num: 363,
    accuracy: 100,
    basePower: 0,
    category: "Physical",
    isNonstandard: "Past",
    name: "Natural Gift",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onModifyType(move, pokemon) {
      if (pokemon.ignoringItem())
        return;
      const item = pokemon.getItem();
      if (!item.naturalGift)
        return;
      move.type = item.naturalGift.type;
    },
    onPrepareHit(target, pokemon, move) {
      if (pokemon.ignoringItem())
        return false;
      const item = pokemon.getItem();
      if (!item.naturalGift)
        return false;
      move.basePower = item.naturalGift.basePower;
      this.debug("BP: " + move.basePower);
      pokemon.setItem("");
      pokemon.lastItem = item.id;
      pokemon.usedItemThisTurn = true;
      this.runEvent("AfterUseItem", pokemon, null, null, item);
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Clever"
  },
  naturepower: {
    num: 267,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Nature Power",
    pp: 20,
    priority: 0,
    flags: { failencore: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    onTryHit(target, pokemon) {
      let move = "triattack";
      if (this.field.isTerrain("electricterrain")) {
        move = "thunderbolt";
      } else if (this.field.isTerrain("grassyterrain")) {
        move = "energyball";
      } else if (this.field.isTerrain("mistyterrain")) {
        move = "moonblast";
      } else if (this.field.isTerrain("psychicterrain")) {
        move = "psychic";
      }
      this.actions.useMove(move, pokemon, target);
      return null;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Beautiful"
  },
  naturesmadness: {
    num: 717,
    accuracy: 90,
    basePower: 0,
    damageCallback(pokemon, target) {
      return this.clampIntRange(Math.floor(target.getUndynamaxedHP() / 2), 1);
    },
    category: "Special",
    isNonstandard: "Past",
    name: "Nature's Madness",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Tough"
  },
  needlearm: {
    num: 302,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    isNonstandard: "Past",
    name: "Needle Arm",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Grass",
    contestType: "Clever"
  },
  neverendingnightmare: {
    num: 636,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Never-Ending Nightmare",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "ghostiumz",
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  nightdaze: {
    num: 539,
    accuracy: 95,
    basePower: 85,
    category: "Special",
    name: "Night Daze",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 40,
      boosts: {
        accuracy: -1
      }
    },
    target: "normal",
    type: "Dark",
    contestType: "Cool"
  },
  nightmare: {
    num: 171,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Nightmare",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "nightmare",
    condition: {
      noCopy: true,
      onStart(pokemon) {
        if (pokemon.status !== "slp" && !pokemon.hasAbility("comatose")) {
          return false;
        }
        this.add("-start", pokemon, "Nightmare");
      },
      onResidualOrder: 11,
      onResidual(pokemon) {
        this.damage(pokemon.baseMaxhp / 4);
      }
    },
    secondary: null,
    target: "normal",
    type: "Ghost",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  nightshade: {
    num: 101,
    accuracy: 100,
    basePower: 0,
    damage: "level",
    category: "Special",
    name: "Night Shade",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Clever"
  },
  nightslash: {
    num: 400,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Night Slash",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Cool"
  },
  nobleroar: {
    num: 568,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Noble Roar",
    pp: 30,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    boosts: {
      atk: -1,
      spa: -1
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Tough"
  },
  noretreat: {
    num: 748,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "No Retreat",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    volatileStatus: "noretreat",
    onTry(source, target, move) {
      if (source.volatiles["noretreat"])
        return false;
      if (source.volatiles["trapped"]) {
        delete move.volatileStatus;
      }
    },
    condition: {
      onStart(pokemon) {
        this.add("-start", pokemon, "move: No Retreat");
      },
      onTrapPokemon(pokemon) {
        pokemon.tryTrap();
      }
    },
    boosts: {
      atk: 1,
      def: 1,
      spa: 1,
      spd: 1,
      spe: 1
    },
    secondary: null,
    target: "self",
    type: "Fighting"
  },
  noxioustorque: {
    num: 898,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    isNonstandard: "Unobtainable",
    name: "Noxious Torque",
    pp: 10,
    priority: 0,
    flags: {
      protect: 1,
      failencore: 1,
      failmefirst: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    noSketch: true,
    secondary: {
      chance: 30,
      status: "psn"
    },
    target: "normal",
    type: "Poison"
  },
  nuzzle: {
    num: 609,
    accuracy: 100,
    basePower: 20,
    category: "Physical",
    name: "Nuzzle",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cute"
  },
  oblivionwing: {
    num: 613,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    isNonstandard: "Past",
    name: "Oblivion Wing",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, heal: 1, metronome: 1 },
    drain: [3, 4],
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  obstruct: {
    num: 792,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Obstruct",
    pp: 10,
    priority: 4,
    flags: { failinstruct: 1 },
    stallingMove: true,
    volatileStatus: "obstruct",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    onHit(pokemon) {
      pokemon.addVolatile("stall");
    },
    condition: {
      duration: 1,
      onStart(target) {
        this.add("-singleturn", target, "Protect");
      },
      onTryHitPriority: 3,
      onTryHit(target, source, move) {
        if (!move.flags["protect"] || move.category === "Status") {
          if (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          if (move.isZ || move.isMax)
            target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        if (move.smartTarget) {
          move.smartTarget = false;
        } else {
          this.add("-activate", target, "move: Protect");
        }
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        if (this.checkMoveMakesContact(move, source, target)) {
          this.boost({ def: -2 }, source, target, this.dex.getActiveMove("Obstruct"));
        }
        return this.NOT_FAIL;
      },
      onHit(target, source, move) {
        if (move.isZOrMaxPowered && this.checkMoveMakesContact(move, source, target)) {
          this.boost({ def: -2 }, source, target, this.dex.getActiveMove("Obstruct"));
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Dark"
  },
  oceanicoperetta: {
    num: 697,
    accuracy: true,
    basePower: 195,
    category: "Special",
    isNonstandard: "Past",
    name: "Oceanic Operetta",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "primariumz",
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  octazooka: {
    num: 190,
    accuracy: 85,
    basePower: 65,
    category: "Special",
    isNonstandard: "Past",
    name: "Octazooka",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 50,
      boosts: {
        accuracy: -1
      }
    },
    target: "normal",
    type: "Water",
    contestType: "Tough"
  },
  octolock: {
    num: 753,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Octolock",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onTryImmunity(target) {
      return this.dex.getImmunity("trapped", target);
    },
    volatileStatus: "octolock",
    condition: {
      onStart(pokemon, source) {
        this.add("-start", pokemon, "move: Octolock", "[of] " + source);
      },
      onResidualOrder: 14,
      onResidual(pokemon) {
        const source = this.effectState.source;
        if (source && (!source.isActive || source.hp <= 0 || !source.activeTurns)) {
          delete pokemon.volatiles["octolock"];
          this.add("-end", pokemon, "Octolock", "[partiallytrapped]", "[silent]");
          return;
        }
        this.boost({ def: -1, spd: -1 }, pokemon, source, this.dex.getActiveMove("octolock"));
      },
      onTrapPokemon(pokemon) {
        if (this.effectState.source && this.effectState.source.isActive)
          pokemon.tryTrap();
      }
    },
    secondary: null,
    target: "normal",
    type: "Fighting"
  },
  odorsleuth: {
    num: 316,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Odor Sleuth",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    volatileStatus: "foresight",
    onTryHit(target) {
      if (target.volatiles["miracleeye"])
        return false;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { atk: 1 } },
    contestType: "Clever"
  },
  ominouswind: {
    num: 466,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    isNonstandard: "Past",
    name: "Ominous Wind",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      self: {
        boosts: {
          atk: 1,
          def: 1,
          spa: 1,
          spd: 1,
          spe: 1
        }
      }
    },
    target: "normal",
    type: "Ghost",
    contestType: "Beautiful"
  },
  orderup: {
    num: 856,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Order Up",
    pp: 10,
    priority: 0,
    flags: { protect: 1 },
    onAfterMoveSecondarySelf(pokemon, target, move) {
      if (!pokemon.volatiles["commanded"])
        return;
      const tatsugiri = pokemon.volatiles["commanded"].source;
      if (tatsugiri.baseSpecies.baseSpecies !== "Tatsugiri")
        return;
      switch (tatsugiri.baseSpecies.forme) {
        case "Droopy":
          this.boost({ def: 1 }, pokemon, pokemon);
          break;
        case "Stretchy":
          this.boost({ spe: 1 }, pokemon, pokemon);
          break;
        default:
          this.boost({ atk: 1 }, pokemon, pokemon);
          break;
      }
    },
    secondary: null,
    hasSheerForce: true,
    target: "normal",
    type: "Dragon"
  },
  originpulse: {
    num: 618,
    accuracy: 85,
    basePower: 110,
    category: "Special",
    name: "Origin Pulse",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, pulse: 1 },
    target: "allAdjacentFoes",
    type: "Water",
    contestType: "Beautiful"
  },
  outrage: {
    num: 200,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Outrage",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, failinstruct: 1 },
    self: {
      volatileStatus: "lockedmove"
    },
    onAfterMove(pokemon) {
      if (pokemon.volatiles["lockedmove"] && pokemon.volatiles["lockedmove"].duration === 1) {
        pokemon.removeVolatile("lockedmove");
      }
    },
    secondary: null,
    target: "randomNormal",
    type: "Dragon",
    contestType: "Cool"
  },
  overdrive: {
    num: 786,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Overdrive",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Electric"
  },
  overheat: {
    num: 315,
    accuracy: 90,
    basePower: 130,
    category: "Special",
    name: "Overheat",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    self: {
      boosts: {
        spa: -2
      }
    },
    secondary: null,
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  painsplit: {
    num: 220,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Pain Split",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onHit(target, pokemon) {
      const targetHP = target.getUndynamaxedHP();
      const averagehp = Math.floor((targetHP + pokemon.hp) / 2) || 1;
      const targetChange = targetHP - averagehp;
      target.sethp(target.hp - targetChange);
      this.add("-sethp", target, target.getHealth, "[from] move: Pain Split", "[silent]");
      pokemon.sethp(averagehp);
      this.add("-sethp", pokemon, pokemon.getHealth, "[from] move: Pain Split");
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  paleowave: {
    num: 0,
    accuracy: 100,
    basePower: 85,
    category: "Special",
    isNonstandard: "CAP",
    name: "Paleo Wave",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 20,
      boosts: {
        atk: -1
      }
    },
    target: "normal",
    type: "Rock",
    contestType: "Beautiful"
  },
  paraboliccharge: {
    num: 570,
    accuracy: 100,
    basePower: 65,
    category: "Special",
    name: "Parabolic Charge",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, heal: 1, metronome: 1 },
    drain: [1, 2],
    secondary: null,
    target: "allAdjacent",
    type: "Electric",
    contestType: "Clever"
  },
  partingshot: {
    num: 575,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Parting Shot",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    onHit(target, source, move) {
      const success = this.boost({ atk: -1, spa: -1 }, target, source);
      if (!success && !target.hasAbility("mirrorarmor")) {
        delete move.selfSwitch;
      }
    },
    selfSwitch: true,
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { effect: "healreplacement" },
    contestType: "Cool"
  },
  payback: {
    num: 371,
    accuracy: 100,
    basePower: 50,
    basePowerCallback(pokemon, target, move) {
      if (target.newlySwitched || this.queue.willMove(target)) {
        this.debug("Payback NOT boosted");
        return move.basePower;
      }
      this.debug("Payback damage boost");
      return move.basePower * 2;
    },
    category: "Physical",
    name: "Payback",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Tough"
  },
  payday: {
    num: 6,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Pay Day",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Clever"
  },
  peck: {
    num: 64,
    accuracy: 100,
    basePower: 35,
    category: "Physical",
    name: "Peck",
    pp: 35,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, distance: 1, metronome: 1 },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  perishsong: {
    num: 195,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Perish Song",
    pp: 5,
    priority: 0,
    flags: { sound: 1, distance: 1, bypasssub: 1, metronome: 1 },
    onHitField(target, source, move) {
      let result = false;
      let message = false;
      for (const pokemon of this.getAllActive()) {
        if (this.runEvent("Invulnerability", pokemon, source, move) === false) {
          this.add("-miss", source, pokemon);
          result = true;
        } else if (this.runEvent("TryHit", pokemon, source, move) === null) {
          result = true;
        } else if (!pokemon.volatiles["perishsong"]) {
          pokemon.addVolatile("perishsong");
          this.add("-start", pokemon, "perish3", "[silent]");
          result = true;
          message = true;
        }
      }
      if (!result)
        return false;
      if (message)
        this.add("-fieldactivate", "move: Perish Song");
    },
    condition: {
      duration: 4,
      onEnd(target) {
        this.add("-start", target, "perish0");
        target.faint();
      },
      onResidualOrder: 24,
      onResidual(pokemon) {
        const duration = pokemon.volatiles["perishsong"].duration;
        this.add("-start", pokemon, "perish" + duration);
      }
    },
    secondary: null,
    target: "all",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  petalblizzard: {
    num: 572,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Petal Blizzard",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    secondary: null,
    target: "allAdjacent",
    type: "Grass",
    contestType: "Beautiful"
  },
  petaldance: {
    num: 80,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    name: "Petal Dance",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, dance: 1, metronome: 1, failinstruct: 1 },
    self: {
      volatileStatus: "lockedmove"
    },
    onAfterMove(pokemon) {
      if (pokemon.volatiles["lockedmove"] && pokemon.volatiles["lockedmove"].duration === 1) {
        pokemon.removeVolatile("lockedmove");
      }
    },
    secondary: null,
    target: "randomNormal",
    type: "Grass",
    contestType: "Beautiful"
  },
  phantomforce: {
    num: 566,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Phantom Force",
    pp: 10,
    priority: 0,
    flags: { contact: 1, charge: 1, mirror: 1, metronome: 1, nosleeptalk: 1, noassist: 1, failinstruct: 1 },
    breaksProtect: true,
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    condition: {
      duration: 2,
      onInvulnerability: false
    },
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  photongeyser: {
    num: 722,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    name: "Photon Geyser",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    onModifyMove(move, pokemon) {
      if (pokemon.getStat("atk", false, true) > pokemon.getStat("spa", false, true))
        move.category = "Physical";
    },
    ignoreAbility: true,
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  pikapapow: {
    num: 732,
    accuracy: true,
    basePower: 0,
    basePowerCallback(pokemon) {
      const bp = Math.floor(pokemon.happiness * 10 / 25) || 1;
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Special",
    isNonstandard: "LGPE",
    name: "Pika Papow",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cute"
  },
  pinmissile: {
    num: 42,
    accuracy: 95,
    basePower: 25,
    category: "Physical",
    name: "Pin Missile",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Bug",
    zMove: { basePower: 140 },
    maxMove: { basePower: 130 },
    contestType: "Cool"
  },
  plasmafists: {
    num: 721,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    isNonstandard: "Past",
    name: "Plasma Fists",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1 },
    pseudoWeather: "iondeluge",
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  playnice: {
    num: 589,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Play Nice",
    pp: 20,
    priority: 0,
    flags: { reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    boosts: {
      atk: -1
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  playrough: {
    num: 583,
    accuracy: 90,
    basePower: 90,
    category: "Physical",
    name: "Play Rough",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        atk: -1
      }
    },
    target: "normal",
    type: "Fairy",
    contestType: "Cute"
  },
  pluck: {
    num: 365,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Pluck",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, distance: 1, metronome: 1 },
    onHit(target, source) {
      const item = target.getItem();
      if (source.hp && item.isBerry && target.takeItem(source)) {
        this.add("-enditem", target, item.name, "[from] stealeat", "[move] Pluck", "[of] " + source);
        if (this.singleEvent("Eat", item, null, source, null, null)) {
          this.runEvent("EatItem", source, null, null, item);
          if (item.id === "leppaberry")
            target.staleness = "external";
        }
        if (item.onEat)
          source.ateBerry = true;
      }
    },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cute"
  },
  poisonfang: {
    num: 305,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Poison Fang",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    secondary: {
      chance: 50,
      status: "tox"
    },
    target: "normal",
    type: "Poison",
    contestType: "Clever"
  },
  poisongas: {
    num: 139,
    accuracy: 90,
    basePower: 0,
    category: "Status",
    name: "Poison Gas",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    status: "psn",
    secondary: null,
    target: "allAdjacentFoes",
    type: "Poison",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  poisonjab: {
    num: 398,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Poison Jab",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "psn"
    },
    target: "normal",
    type: "Poison",
    contestType: "Tough"
  },
  poisonpowder: {
    num: 77,
    accuracy: 75,
    basePower: 0,
    category: "Status",
    name: "Poison Powder",
    pp: 35,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1, powder: 1 },
    status: "psn",
    secondary: null,
    target: "normal",
    type: "Poison",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  poisonsting: {
    num: 40,
    accuracy: 100,
    basePower: 15,
    category: "Physical",
    name: "Poison Sting",
    pp: 35,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "psn"
    },
    target: "normal",
    type: "Poison",
    contestType: "Clever"
  },
  poisontail: {
    num: 342,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Poison Tail",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: {
      chance: 10,
      status: "psn"
    },
    target: "normal",
    type: "Poison",
    contestType: "Clever"
  },
  pollenpuff: {
    num: 676,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Pollen Puff",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, allyanim: 1, metronome: 1, bullet: 1 },
    onTryHit(target, source, move) {
      if (source.isAlly(target)) {
        move.basePower = 0;
        move.infiltrates = true;
      }
    },
    onTryMove(source, target, move) {
      if (source.isAlly(target) && source.volatiles["healblock"]) {
        this.attrLastMove("[still]");
        this.add("cant", source, "move: Heal Block", move);
        return false;
      }
    },
    onHit(target, source, move) {
      if (source.isAlly(target)) {
        if (!this.heal(Math.floor(target.baseMaxhp * 0.5))) {
          if (target.volatiles["healblock"] && target.hp !== target.maxhp) {
            this.attrLastMove("[still]");
            this.add("cant", source, "move: Heal Block", move);
          } else {
            this.add("-immune", target);
          }
          return this.NOT_FAIL;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cute"
  },
  poltergeist: {
    num: 809,
    accuracy: 90,
    basePower: 110,
    category: "Physical",
    name: "Poltergeist",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onTry(source, target) {
      return !!target.item;
    },
    onTryHit(target, source, move) {
      this.add("-activate", target, "move: Poltergeist", this.dex.items.get(target.item).name);
    },
    secondary: null,
    target: "normal",
    type: "Ghost"
  },
  populationbomb: {
    num: 860,
    accuracy: 90,
    basePower: 20,
    category: "Physical",
    name: "Population Bomb",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, slicing: 1 },
    multihit: 10,
    multiaccuracy: true,
    secondary: null,
    target: "normal",
    type: "Normal"
  },
  pounce: {
    num: 884,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Pounce",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "normal",
    type: "Bug",
    contestType: "Cute"
  },
  pound: {
    num: 1,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Pound",
    pp: 35,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  powder: {
    num: 600,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Powder",
    pp: 20,
    priority: 1,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1, powder: 1 },
    volatileStatus: "powder",
    condition: {
      duration: 1,
      onStart(target) {
        this.add("-singleturn", target, "Powder");
      },
      onTryMovePriority: -1,
      onTryMove(pokemon, target, move) {
        if (move.type === "Fire") {
          this.add("-activate", pokemon, "move: Powder");
          this.damage(this.clampIntRange(Math.round(pokemon.maxhp / 4), 1));
          this.attrLastMove("[still]");
          return false;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Bug",
    zMove: { boost: { spd: 2 } },
    contestType: "Clever"
  },
  powdersnow: {
    num: 181,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Powder Snow",
    pp: 25,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "frz"
    },
    target: "allAdjacentFoes",
    type: "Ice",
    contestType: "Beautiful"
  },
  powergem: {
    num: 408,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Power Gem",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Beautiful"
  },
  powershift: {
    num: 829,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Unobtainable",
    name: "Power Shift",
    pp: 10,
    priority: 0,
    flags: { snatch: 1 },
    volatileStatus: "powershift",
    condition: {
      onStart(pokemon) {
        this.add("-start", pokemon, "Power Shift");
        const newatk = pokemon.storedStats.def;
        const newdef = pokemon.storedStats.atk;
        pokemon.storedStats.atk = newatk;
        pokemon.storedStats.def = newdef;
      },
      onCopy(pokemon) {
        const newatk = pokemon.storedStats.def;
        const newdef = pokemon.storedStats.atk;
        pokemon.storedStats.atk = newatk;
        pokemon.storedStats.def = newdef;
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Power Shift");
        const newatk = pokemon.storedStats.def;
        const newdef = pokemon.storedStats.atk;
        pokemon.storedStats.atk = newatk;
        pokemon.storedStats.def = newdef;
      },
      onRestart(pokemon) {
        pokemon.removeVolatile("Power Shift");
      }
    },
    secondary: null,
    target: "self",
    type: "Normal"
  },
  powersplit: {
    num: 471,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Power Split",
    pp: 10,
    priority: 0,
    flags: { protect: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      const newatk = Math.floor((target.storedStats.atk + source.storedStats.atk) / 2);
      target.storedStats.atk = newatk;
      source.storedStats.atk = newatk;
      const newspa = Math.floor((target.storedStats.spa + source.storedStats.spa) / 2);
      target.storedStats.spa = newspa;
      source.storedStats.spa = newspa;
      this.add("-activate", source, "move: Power Split", "[of] " + target);
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  powerswap: {
    num: 384,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Power Swap",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      const targetBoosts = {};
      const sourceBoosts = {};
      const atkSpa = ["atk", "spa"];
      for (const stat of atkSpa) {
        targetBoosts[stat] = target.boosts[stat];
        sourceBoosts[stat] = source.boosts[stat];
      }
      source.setBoost(targetBoosts);
      target.setBoost(sourceBoosts);
      this.add("-swapboost", source, target, "atk, spa", "[from] move: Power Swap");
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  powertrick: {
    num: 379,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Power Trick",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    volatileStatus: "powertrick",
    condition: {
      onStart(pokemon) {
        this.add("-start", pokemon, "Power Trick");
        const newatk = pokemon.storedStats.def;
        const newdef = pokemon.storedStats.atk;
        pokemon.storedStats.atk = newatk;
        pokemon.storedStats.def = newdef;
      },
      onCopy(pokemon) {
        const newatk = pokemon.storedStats.def;
        const newdef = pokemon.storedStats.atk;
        pokemon.storedStats.atk = newatk;
        pokemon.storedStats.def = newdef;
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Power Trick");
        const newatk = pokemon.storedStats.def;
        const newdef = pokemon.storedStats.atk;
        pokemon.storedStats.atk = newatk;
        pokemon.storedStats.def = newdef;
      },
      onRestart(pokemon) {
        pokemon.removeVolatile("Power Trick");
      }
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { boost: { atk: 1 } },
    contestType: "Clever"
  },
  powertrip: {
    num: 681,
    accuracy: 100,
    basePower: 20,
    basePowerCallback(pokemon, target, move) {
      const bp = move.basePower + 20 * pokemon.positiveBoosts();
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Power Trip",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Clever"
  },
  poweruppunch: {
    num: 612,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    isNonstandard: "Past",
    name: "Power-Up Punch",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          atk: 1
        }
      }
    },
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  powerwhip: {
    num: 438,
    accuracy: 85,
    basePower: 120,
    category: "Physical",
    name: "Power Whip",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Tough"
  },
  precipiceblades: {
    num: 619,
    accuracy: 85,
    basePower: 120,
    category: "Physical",
    name: "Precipice Blades",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1 },
    target: "allAdjacentFoes",
    type: "Ground",
    contestType: "Cool"
  },
  present: {
    num: 217,
    accuracy: 90,
    basePower: 0,
    category: "Physical",
    name: "Present",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onModifyMove(move, pokemon, target) {
      const rand = this.random(10);
      if (rand < 2) {
        move.heal = [1, 4];
        move.infiltrates = true;
      } else if (rand < 6) {
        move.basePower = 40;
      } else if (rand < 9) {
        move.basePower = 80;
      } else {
        move.basePower = 120;
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  prismaticlaser: {
    num: 711,
    accuracy: 100,
    basePower: 160,
    category: "Special",
    name: "Prismatic Laser",
    pp: 10,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  protect: {
    num: 182,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Protect",
    pp: 10,
    priority: 4,
    flags: { noassist: 1, failcopycat: 1 },
    stallingMove: true,
    volatileStatus: "protect",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    onHit(pokemon) {
      pokemon.addVolatile("stall");
    },
    condition: {
      duration: 1,
      onStart(target) {
        this.add("-singleturn", target, "Protect");
      },
      onTryHitPriority: 3,
      onTryHit(target, source, move) {
        if (!move.flags["protect"]) {
          if (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          if (move.isZ || move.isMax)
            target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        if (move.smartTarget) {
          move.smartTarget = false;
        } else {
          this.add("-activate", target, "move: Protect");
        }
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        return this.NOT_FAIL;
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  psybeam: {
    num: 60,
    accuracy: 100,
    basePower: 65,
    category: "Special",
    name: "Psybeam",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Psychic",
    contestType: "Beautiful"
  },
  psyblade: {
    num: 875,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Psyblade",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    secondary: null,
    onBasePower(basePower, source) {
      if (this.field.isTerrain("electricterrain")) {
        this.debug("psyblade electric terrain boost");
        return this.chainModify(1.5);
      }
    },
    target: "normal",
    type: "Psychic"
  },
  psychup: {
    num: 244,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Psych Up",
    pp: 10,
    priority: 0,
    flags: { bypasssub: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      let i;
      for (i in target.boosts) {
        source.boosts[i] = target.boosts[i];
      }
      const volatilesToCopy = ["focusenergy", "gmaxchistrike", "laserfocus"];
      for (const volatile of volatilesToCopy) {
        if (target.volatiles[volatile]) {
          source.addVolatile(volatile);
          if (volatile === "gmaxchistrike")
            source.volatiles[volatile].layers = target.volatiles[volatile].layers;
        } else {
          source.removeVolatile(volatile);
        }
      }
      this.add("-copyboost", source, target, "[from] move: Psych Up");
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { effect: "heal" },
    contestType: "Clever"
  },
  psychic: {
    num: 94,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Psychic",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  psychicfangs: {
    num: 706,
    accuracy: 100,
    basePower: 85,
    category: "Physical",
    name: "Psychic Fangs",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    onTryHit(pokemon) {
      pokemon.side.removeSideCondition("reflect");
      pokemon.side.removeSideCondition("lightscreen");
      pokemon.side.removeSideCondition("auroraveil");
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  psychicnoise: {
    num: 917,
    accuracy: 100,
    basePower: 75,
    category: "Special",
    name: "Psychic Noise",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: {
      chance: 100,
      volatileStatus: "healblock"
    },
    target: "normal",
    type: "Psychic"
  },
  psychicterrain: {
    num: 678,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Psychic Terrain",
    pp: 10,
    priority: 0,
    flags: { nonsky: 1, metronome: 1 },
    terrain: "psychicterrain",
    condition: {
      duration: 5,
      durationCallback(source, effect) {
        if (source?.hasItem("terrainextender")) {
          return 8;
        }
        return 5;
      },
      onTryHitPriority: 4,
      onTryHit(target, source, effect) {
        if (effect && (effect.priority <= 0.1 || effect.target === "self")) {
          return;
        }
        if (target.isSemiInvulnerable() || target.isAlly(source))
          return;
        if (!target.isGrounded()) {
          const baseMove = this.dex.moves.get(effect.id);
          if (baseMove.priority > 0) {
            this.hint("Psychic Terrain doesn't affect Pok\xE9mon immune to Ground.");
          }
          return;
        }
        this.add("-activate", target, "move: Psychic Terrain");
        return null;
      },
      onBasePowerPriority: 6,
      onBasePower(basePower, attacker, defender, move) {
        if (move.type === "Psychic" && attacker.isGrounded() && !attacker.isSemiInvulnerable()) {
          this.debug("psychic terrain boost");
          return this.chainModify([5325, 4096]);
        }
      },
      onFieldStart(field, source, effect) {
        if (effect?.effectType === "Ability") {
          this.add("-fieldstart", "move: Psychic Terrain", "[from] ability: " + effect.name, "[of] " + source);
        } else {
          this.add("-fieldstart", "move: Psychic Terrain");
        }
      },
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 7,
      onFieldEnd() {
        this.add("-fieldend", "move: Psychic Terrain");
      }
    },
    secondary: null,
    target: "all",
    type: "Psychic",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  psychoboost: {
    num: 354,
    accuracy: 90,
    basePower: 140,
    category: "Special",
    name: "Psycho Boost",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    self: {
      boosts: {
        spa: -2
      }
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  psychocut: {
    num: 427,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Psycho Cut",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  psychoshift: {
    num: 375,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Psycho Shift",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onTryHit(target, source, move) {
      if (!source.status)
        return false;
      move.status = source.status;
    },
    self: {
      onHit(pokemon) {
        pokemon.cureStatus();
      }
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spa: 2 } },
    contestType: "Clever"
  },
  psyshieldbash: {
    num: 828,
    accuracy: 90,
    basePower: 70,
    category: "Physical",
    name: "Psyshield Bash",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          def: 1
        }
      }
    },
    target: "normal",
    type: "Psychic"
  },
  psyshock: {
    num: 473,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    overrideDefensiveStat: "def",
    name: "Psyshock",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Beautiful"
  },
  psystrike: {
    num: 540,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    overrideDefensiveStat: "def",
    name: "Psystrike",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  psywave: {
    num: 149,
    accuracy: 100,
    basePower: 0,
    damageCallback(pokemon) {
      return this.random(50, 151) * pokemon.level / 100;
    },
    category: "Special",
    isNonstandard: "Past",
    name: "Psywave",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  pulverizingpancake: {
    num: 701,
    accuracy: true,
    basePower: 210,
    category: "Physical",
    isNonstandard: "Past",
    name: "Pulverizing Pancake",
    pp: 1,
    priority: 0,
    flags: { contact: 1 },
    isZ: "snorliumz",
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  punishment: {
    num: 386,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      let power = 60 + 20 * target.positiveBoosts();
      if (power > 200)
        power = 200;
      this.debug("BP: " + power);
      return power;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Punishment",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Cool"
  },
  purify: {
    num: 685,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Purify",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, heal: 1, metronome: 1 },
    onHit(target, source) {
      if (!target.cureStatus()) {
        this.add("-fail", source);
        this.attrLastMove("[still]");
        return this.NOT_FAIL;
      }
      this.heal(Math.ceil(source.maxhp * 0.5), source);
    },
    secondary: null,
    target: "normal",
    type: "Poison",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Beautiful"
  },
  pursuit: {
    num: 228,
    accuracy: 100,
    basePower: 40,
    basePowerCallback(pokemon, target, move) {
      if (target.beingCalledBack || target.switchFlag) {
        this.debug("Pursuit damage boost");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Pursuit",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    beforeTurnCallback(pokemon) {
      for (const side of this.sides) {
        if (side.hasAlly(pokemon))
          continue;
        side.addSideCondition("pursuit", pokemon);
        const data = side.getSideConditionData("pursuit");
        if (!data.sources) {
          data.sources = [];
        }
        data.sources.push(pokemon);
      }
    },
    onModifyMove(move, source, target) {
      if (target?.beingCalledBack || target?.switchFlag)
        move.accuracy = true;
    },
    onTryHit(target, pokemon) {
      target.side.removeSideCondition("pursuit");
    },
    condition: {
      duration: 1,
      onBeforeSwitchOut(pokemon) {
        this.debug("Pursuit start");
        let alreadyAdded = false;
        pokemon.removeVolatile("destinybond");
        for (const source of this.effectState.sources) {
          if (!source.isAdjacent(pokemon) || !this.queue.cancelMove(source) || !source.hp)
            continue;
          if (!alreadyAdded) {
            this.add("-activate", pokemon, "move: Pursuit");
            alreadyAdded = true;
          }
          if (source.canMegaEvo || source.canUltraBurst) {
            for (const [actionIndex, action] of this.queue.entries()) {
              if (action.pokemon === source && action.choice === "megaEvo") {
                this.actions.runMegaEvo(source);
                this.queue.list.splice(actionIndex, 1);
                break;
              }
            }
          }
          this.actions.runMove("pursuit", source, source.getLocOf(pokemon));
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  pyroball: {
    num: 780,
    accuracy: 90,
    basePower: 120,
    category: "Physical",
    name: "Pyro Ball",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1, bullet: 1 },
    secondary: {
      chance: 10,
      status: "brn"
    },
    target: "normal",
    type: "Fire"
  },
  quash: {
    num: 511,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Quash",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    onHit(target) {
      if (this.activePerHalf === 1)
        return false;
      const action = this.queue.willMove(target);
      if (!action)
        return false;
      action.order = 201;
      this.add("-activate", target, "move: Quash");
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  quickattack: {
    num: 98,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Quick Attack",
    pp: 30,
    priority: 1,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  quickguard: {
    num: 501,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Quick Guard",
    pp: 15,
    priority: 3,
    flags: { snatch: 1 },
    sideCondition: "quickguard",
    onTry() {
      return !!this.queue.willAct();
    },
    onHitSide(side, source) {
      source.addVolatile("stall");
    },
    condition: {
      duration: 1,
      onSideStart(target, source) {
        this.add("-singleturn", source, "Quick Guard");
      },
      onTryHitPriority: 4,
      onTryHit(target, source, move) {
        if (move.priority <= 0.1)
          return;
        if (!move.flags["protect"]) {
          if (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          if (move.isZ || move.isMax)
            target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        this.add("-activate", target, "move: Quick Guard");
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        return this.NOT_FAIL;
      }
    },
    secondary: null,
    target: "allySide",
    type: "Fighting",
    zMove: { boost: { def: 1 } },
    contestType: "Cool"
  },
  quiverdance: {
    num: 483,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Quiver Dance",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, dance: 1, metronome: 1 },
    boosts: {
      spa: 1,
      spd: 1,
      spe: 1
    },
    secondary: null,
    target: "self",
    type: "Bug",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  rage: {
    num: 99,
    accuracy: 100,
    basePower: 20,
    category: "Physical",
    isNonstandard: "Past",
    name: "Rage",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      volatileStatus: "rage"
    },
    condition: {
      onStart(pokemon) {
        this.add("-singlemove", pokemon, "Rage");
      },
      onHit(target, source, move) {
        if (target !== source && move.category !== "Status") {
          this.boost({ atk: 1 });
        }
      },
      onBeforeMovePriority: 100,
      onBeforeMove(pokemon) {
        this.debug("removing Rage before attack");
        pokemon.removeVolatile("rage");
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  ragefist: {
    num: 889,
    accuracy: 100,
    basePower: 50,
    basePowerCallback(pokemon) {
      return Math.min(350, 50 + 50 * pokemon.timesAttacked);
    },
    category: "Physical",
    name: "Rage Fist",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1 },
    secondary: null,
    target: "normal",
    type: "Ghost"
  },
  ragepowder: {
    num: 476,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Rage Powder",
    pp: 20,
    priority: 2,
    flags: { noassist: 1, failcopycat: 1, powder: 1 },
    volatileStatus: "ragepowder",
    onTry(source) {
      return this.activePerHalf > 1;
    },
    condition: {
      duration: 1,
      onStart(pokemon) {
        this.add("-singleturn", pokemon, "move: Rage Powder");
      },
      onFoeRedirectTargetPriority: 1,
      onFoeRedirectTarget(target, source, source2, move) {
        const ragePowderUser = this.effectState.target;
        if (ragePowderUser.isSkyDropped())
          return;
        if (source.runStatusImmunity("powder") && this.validTarget(ragePowderUser, source, move.target)) {
          if (move.smartTarget)
            move.smartTarget = false;
          this.debug("Rage Powder redirected target of move");
          return ragePowderUser;
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Bug",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  ragingbull: {
    num: 873,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Raging Bull",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    onTryHit(pokemon) {
      pokemon.side.removeSideCondition("reflect");
      pokemon.side.removeSideCondition("lightscreen");
      pokemon.side.removeSideCondition("auroraveil");
    },
    onModifyType(move, pokemon) {
      switch (pokemon.species.name) {
        case "Tauros-Paldea-Combat":
          move.type = "Fighting";
          break;
        case "Tauros-Paldea-Blaze":
          move.type = "Fire";
          break;
        case "Tauros-Paldea-Aqua":
          move.type = "Water";
          break;
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal"
  },
  ragingfury: {
    num: 833,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Raging Fury",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    self: {
      volatileStatus: "lockedmove"
    },
    onAfterMove(pokemon) {
      if (pokemon.volatiles["lockedmove"]?.duration === 1) {
        pokemon.removeVolatile("lockedmove");
      }
    },
    secondary: null,
    target: "randomNormal",
    type: "Fire"
  },
  raindance: {
    num: 240,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Rain Dance",
    pp: 5,
    priority: 0,
    flags: { metronome: 1 },
    weather: "RainDance",
    secondary: null,
    target: "all",
    type: "Water",
    zMove: { boost: { spe: 1 } },
    contestType: "Beautiful"
  },
  rapidspin: {
    num: 229,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Rapid Spin",
    pp: 40,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onAfterHit(target, pokemon, move) {
      if (!move.hasSheerForce) {
        if (pokemon.hp && pokemon.removeVolatile("leechseed")) {
          this.add("-end", pokemon, "Leech Seed", "[from] move: Rapid Spin", "[of] " + pokemon);
        }
        const sideConditions = ["spikes", "toxicspikes", "stealthrock", "stickyweb", "gmaxsteelsurge"];
        for (const condition of sideConditions) {
          if (pokemon.hp && pokemon.side.removeSideCondition(condition)) {
            this.add("-sideend", pokemon.side, this.dex.conditions.get(condition).name, "[from] move: Rapid Spin", "[of] " + pokemon);
          }
        }
        if (pokemon.hp && pokemon.volatiles["partiallytrapped"]) {
          pokemon.removeVolatile("partiallytrapped");
        }
      }
    },
    onAfterSubDamage(damage, target, pokemon, move) {
      if (!move.hasSheerForce) {
        if (pokemon.hp && pokemon.removeVolatile("leechseed")) {
          this.add("-end", pokemon, "Leech Seed", "[from] move: Rapid Spin", "[of] " + pokemon);
        }
        const sideConditions = ["spikes", "toxicspikes", "stealthrock", "stickyweb", "gmaxsteelsurge"];
        for (const condition of sideConditions) {
          if (pokemon.hp && pokemon.side.removeSideCondition(condition)) {
            this.add("-sideend", pokemon.side, this.dex.conditions.get(condition).name, "[from] move: Rapid Spin", "[of] " + pokemon);
          }
        }
        if (pokemon.hp && pokemon.volatiles["partiallytrapped"]) {
          pokemon.removeVolatile("partiallytrapped");
        }
      }
    },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          spe: 1
        }
      }
    },
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  razorleaf: {
    num: 75,
    accuracy: 95,
    basePower: 55,
    category: "Physical",
    name: "Razor Leaf",
    pp: 25,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    critRatio: 2,
    secondary: null,
    target: "allAdjacentFoes",
    type: "Grass",
    contestType: "Cool"
  },
  razorshell: {
    num: 534,
    accuracy: 95,
    basePower: 75,
    category: "Physical",
    name: "Razor Shell",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    secondary: {
      chance: 50,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  razorwind: {
    num: 13,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    isNonstandard: "Past",
    name: "Razor Wind",
    pp: 10,
    priority: 0,
    flags: { charge: 1, protect: 1, mirror: 1, metronome: 1, nosleeptalk: 1, failinstruct: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    critRatio: 2,
    secondary: null,
    target: "allAdjacentFoes",
    type: "Normal",
    contestType: "Cool"
  },
  recover: {
    num: 105,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Recover",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    heal: [1, 2],
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  recycle: {
    num: 278,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Recycle",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onHit(pokemon) {
      if (pokemon.item || !pokemon.lastItem)
        return false;
      const item = pokemon.lastItem;
      pokemon.lastItem = "";
      this.add("-item", pokemon, this.dex.items.get(item), "[from] move: Recycle");
      pokemon.setItem(item);
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { spe: 2 } },
    contestType: "Clever"
  },
  reflect: {
    num: 115,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Reflect",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    sideCondition: "reflect",
    condition: {
      duration: 5,
      durationCallback(target, source, effect) {
        if (source?.hasItem("lightclay")) {
          return 8;
        }
        return 5;
      },
      onAnyModifyDamage(damage, source, target, move) {
        if (target !== source && this.effectState.target.hasAlly(target) && this.getCategory(move) === "Physical") {
          if (!target.getMoveHitData(move).crit && !move.infiltrates) {
            this.debug("Reflect weaken");
            if (this.activePerHalf > 1)
              return this.chainModify([2732, 4096]);
            return this.chainModify(0.5);
          }
        }
      },
      onSideStart(side) {
        this.add("-sidestart", side, "Reflect");
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 1,
      onSideEnd(side) {
        this.add("-sideend", side, "Reflect");
      }
    },
    secondary: null,
    target: "allySide",
    type: "Psychic",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  reflecttype: {
    num: 513,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Reflect Type",
    pp: 15,
    priority: 0,
    flags: { protect: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      if (source.species && (source.species.num === 493 || source.species.num === 773))
        return false;
      if (source.terastallized)
        return false;
      const oldApparentType = source.apparentType;
      let newBaseTypes = target.getTypes(true).filter((type) => type !== "???");
      if (!newBaseTypes.length) {
        if (target.addedType) {
          newBaseTypes = ["Normal"];
        } else {
          return false;
        }
      }
      this.add("-start", source, "typechange", "[from] move: Reflect Type", "[of] " + target);
      source.setType(newBaseTypes);
      source.addedType = target.addedType;
      source.knownType = target.isAlly(source) && target.knownType;
      if (!source.knownType)
        source.apparentType = oldApparentType;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  refresh: {
    num: 287,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Refresh",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onHit(pokemon) {
      if (["", "slp", "frz"].includes(pokemon.status))
        return false;
      pokemon.cureStatus();
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "heal" },
    contestType: "Cute"
  },
  relicsong: {
    num: 547,
    accuracy: 100,
    basePower: 75,
    category: "Special",
    name: "Relic Song",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1 },
    secondary: {
      chance: 10,
      status: "slp"
    },
    onHit(target, pokemon, move) {
      if (pokemon.baseSpecies.baseSpecies === "Meloetta" && !pokemon.transformed) {
        move.willChangeForme = true;
      }
    },
    onAfterMoveSecondarySelf(pokemon, target, move) {
      if (move.willChangeForme) {
        const meloettaForme = pokemon.species.id === "meloettapirouette" ? "" : "-Pirouette";
        pokemon.formeChange("Meloetta" + meloettaForme, this.effect, false, "[msg]");
      }
    },
    target: "allAdjacentFoes",
    type: "Normal",
    contestType: "Beautiful"
  },
  rest: {
    num: 156,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Rest",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    onTry(source) {
      if (source.status === "slp" || source.hasAbility("comatose"))
        return false;
      if (source.hp === source.maxhp) {
        this.add("-fail", source, "heal");
        return null;
      }
      if (source.hasAbility(["insomnia", "vitalspirit"])) {
        this.add("-fail", source, "[from] ability: " + source.getAbility().name, "[of] " + source);
        return null;
      }
    },
    onHit(target, source, move) {
      const result = target.setStatus("slp", source, move);
      if (!result)
        return result;
      target.statusState.time = 3;
      target.statusState.startTime = 3;
      this.heal(target.maxhp);
    },
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  retaliate: {
    num: 514,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Retaliate",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, pokemon) {
      if (pokemon.side.faintedLastTurn) {
        this.debug("Boosted for a faint last turn");
        return this.chainModify(2);
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  return: {
    num: 216,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon) {
      return Math.floor(pokemon.happiness * 10 / 25) || 1;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Return",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Cute"
  },
  revelationdance: {
    num: 686,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Revelation Dance",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, dance: 1, metronome: 1 },
    onModifyType(move, pokemon) {
      let type = pokemon.getTypes()[0];
      if (type === "Bird")
        type = "???";
      if (type === "Stellar")
        type = pokemon.getTypes(false, true)[0];
      move.type = type;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Beautiful"
  },
  revenge: {
    num: 279,
    accuracy: 100,
    basePower: 60,
    basePowerCallback(pokemon, target, move) {
      const damagedByTarget = pokemon.attackedBy.some(
        (p) => p.source === target && p.damage > 0 && p.thisTurn
      );
      if (damagedByTarget) {
        this.debug("BP doubled for getting hit by " + target);
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Revenge",
    pp: 10,
    priority: -4,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  reversal: {
    num: 179,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target) {
      const ratio = Math.max(Math.floor(pokemon.hp * 48 / pokemon.maxhp), 1);
      let bp;
      if (ratio < 2) {
        bp = 200;
      } else if (ratio < 5) {
        bp = 150;
      } else if (ratio < 10) {
        bp = 100;
      } else if (ratio < 17) {
        bp = 80;
      } else if (ratio < 33) {
        bp = 40;
      } else {
        bp = 20;
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Reversal",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    zMove: { basePower: 160 },
    contestType: "Cool"
  },
  revivalblessing: {
    num: 863,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Revival Blessing",
    pp: 1,
    noPPBoosts: true,
    priority: 0,
    flags: { heal: 1 },
    onTryHit(source) {
      if (!source.side.pokemon.filter((ally) => ally.fainted).length) {
        return false;
      }
    },
    slotCondition: "revivalblessing",
    // No this not a real switchout move
    // This is needed to trigger a switch protocol to choose a fainted party member
    // Feel free to refactor
    selfSwitch: true,
    condition: {
      duration: 1
      // reviving implemented in side.ts, kind of
    },
    noSketch: true,
    secondary: null,
    target: "self",
    type: "Normal"
  },
  risingvoltage: {
    num: 804,
    accuracy: 100,
    basePower: 70,
    basePowerCallback(source, target, move) {
      if (this.field.isTerrain("electricterrain") && target.isGrounded()) {
        if (!source.isAlly(target))
          this.hint(`${move.name}'s BP doubled on grounded target.`);
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Special",
    name: "Rising Voltage",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Electric",
    maxMove: { basePower: 140 }
  },
  roar: {
    num: 46,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Roar",
    pp: 20,
    priority: -6,
    flags: { reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, allyanim: 1, metronome: 1, noassist: 1, failcopycat: 1 },
    forceSwitch: true,
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Cool"
  },
  roaroftime: {
    num: 459,
    accuracy: 90,
    basePower: 150,
    category: "Special",
    name: "Roar of Time",
    pp: 5,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Dragon",
    contestType: "Beautiful"
  },
  rockblast: {
    num: 350,
    accuracy: 90,
    basePower: 25,
    category: "Physical",
    name: "Rock Blast",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Rock",
    zMove: { basePower: 140 },
    maxMove: { basePower: 130 },
    contestType: "Tough"
  },
  rockclimb: {
    num: 431,
    accuracy: 85,
    basePower: 90,
    category: "Physical",
    isNonstandard: "Past",
    name: "Rock Climb",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  rockpolish: {
    num: 397,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Rock Polish",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      spe: 2
    },
    secondary: null,
    target: "self",
    type: "Rock",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Tough"
  },
  rockslide: {
    num: 157,
    accuracy: 90,
    basePower: 75,
    category: "Physical",
    name: "Rock Slide",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "allAdjacentFoes",
    type: "Rock",
    contestType: "Tough"
  },
  rocksmash: {
    num: 249,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Rock Smash",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 50,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  rockthrow: {
    num: 88,
    accuracy: 90,
    basePower: 50,
    category: "Physical",
    name: "Rock Throw",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Tough"
  },
  rocktomb: {
    num: 317,
    accuracy: 95,
    basePower: 60,
    category: "Physical",
    name: "Rock Tomb",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spe: -1
      }
    },
    target: "normal",
    type: "Rock",
    contestType: "Clever"
  },
  rockwrecker: {
    num: 439,
    accuracy: 90,
    basePower: 150,
    category: "Physical",
    name: "Rock Wrecker",
    pp: 5,
    priority: 0,
    flags: { recharge: 1, protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    self: {
      volatileStatus: "mustrecharge"
    },
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Tough"
  },
  roleplay: {
    num: 272,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Role Play",
    pp: 10,
    priority: 0,
    flags: { bypasssub: 1, allyanim: 1, metronome: 1 },
    onTryHit(target, source) {
      if (target.ability === source.ability)
        return false;
      if (target.getAbility().flags["failroleplay"] || source.getAbility().flags["cantsuppress"])
        return false;
    },
    onHit(target, source) {
      const oldAbility = source.setAbility(target.ability);
      if (oldAbility) {
        this.add("-ability", source, source.getAbility().name, "[from] move: Role Play", "[of] " + target);
        return;
      }
      return oldAbility;
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 1 } },
    contestType: "Cute"
  },
  rollingkick: {
    num: 27,
    accuracy: 85,
    basePower: 60,
    category: "Physical",
    isNonstandard: "Past",
    name: "Rolling Kick",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  rollout: {
    num: 205,
    accuracy: 90,
    basePower: 30,
    basePowerCallback(pokemon, target, move) {
      let bp = move.basePower;
      const rolloutData = pokemon.volatiles["rollout"];
      if (rolloutData?.hitCount) {
        bp *= Math.pow(2, rolloutData.contactHitCount);
      }
      if (rolloutData && pokemon.status !== "slp") {
        rolloutData.hitCount++;
        rolloutData.contactHitCount++;
        if (rolloutData.hitCount < 5) {
          rolloutData.duration = 2;
        }
      }
      if (pokemon.volatiles["defensecurl"]) {
        bp *= 2;
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    name: "Rollout",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, failinstruct: 1, noparentalbond: 1 },
    onModifyMove(move, pokemon, target) {
      if (pokemon.volatiles["rollout"] || pokemon.status === "slp" || !target)
        return;
      pokemon.addVolatile("rollout");
      pokemon.volatiles["rollout"].targetSlot = move.sourceEffect ? pokemon.lastMoveTargetLoc : pokemon.getLocOf(target);
    },
    onAfterMove(source, target, move) {
      const rolloutData = source.volatiles["rollout"];
      if (rolloutData && rolloutData.hitCount === 5 && rolloutData.contactHitCount < 5) {
        source.addVolatile("rolloutstorage");
        source.volatiles["rolloutstorage"].contactHitCount = rolloutData.contactHitCount;
      }
    },
    condition: {
      duration: 1,
      onLockMove: "rollout",
      onStart() {
        this.effectState.hitCount = 0;
        this.effectState.contactHitCount = 0;
      },
      onResidual(target) {
        if (target.lastMove && target.lastMove.id === "struggle") {
          delete target.volatiles["rollout"];
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Cute"
  },
  roost: {
    num: 355,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Roost",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    heal: [1, 2],
    self: {
      volatileStatus: "roost"
    },
    condition: {
      duration: 1,
      onResidualOrder: 25,
      onStart(target) {
        if (target.terastallized) {
          if (target.hasType("Flying")) {
            this.add("-hint", "If a Terastallized Pokemon uses Roost, it remains Flying-type.");
          }
          return false;
        }
        this.add("-singleturn", target, "move: Roost");
      },
      onTypePriority: -1,
      onType(types, pokemon) {
        this.effectState.typeWas = types;
        return types.filter((type) => type !== "Flying");
      }
    },
    secondary: null,
    target: "self",
    type: "Flying",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  rototiller: {
    num: 563,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Rototiller",
    pp: 10,
    priority: 0,
    flags: { distance: 1, nonsky: 1, metronome: 1 },
    onHitField(target, source) {
      const targets = [];
      let anyAirborne = false;
      for (const pokemon of this.getAllActive()) {
        if (!pokemon.runImmunity("Ground")) {
          this.add("-immune", pokemon);
          anyAirborne = true;
          continue;
        }
        if (pokemon.hasType("Grass")) {
          targets.push(pokemon);
        }
      }
      if (!targets.length && !anyAirborne)
        return false;
      for (const pokemon of targets) {
        this.boost({ atk: 1, spa: 1 }, pokemon, source);
      }
    },
    secondary: null,
    target: "all",
    type: "Ground",
    zMove: { boost: { atk: 1 } },
    contestType: "Tough"
  },
  round: {
    num: 496,
    accuracy: 100,
    basePower: 60,
    basePowerCallback(target, source, move) {
      if (move.sourceEffect === "round") {
        this.debug("BP doubled");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Special",
    name: "Round",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    onTry(source, target, move) {
      for (const action of this.queue.list) {
        if (!action.pokemon || !action.move || action.maxMove || action.zmove)
          continue;
        if (action.move.id === "round") {
          this.queue.prioritizeAction(action, move);
          return;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Beautiful"
  },
  ruination: {
    num: 877,
    accuracy: 90,
    basePower: 0,
    damageCallback(pokemon, target) {
      return this.clampIntRange(Math.floor(target.getUndynamaxedHP() / 2), 1);
    },
    category: "Special",
    name: "Ruination",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Tough"
  },
  sacredfire: {
    num: 221,
    accuracy: 95,
    basePower: 100,
    category: "Physical",
    name: "Sacred Fire",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1, metronome: 1 },
    secondary: {
      chance: 50,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  sacredsword: {
    num: 533,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Sacred Sword",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    ignoreEvasion: true,
    ignoreDefensive: true,
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  safeguard: {
    num: 219,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Safeguard",
    pp: 25,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    sideCondition: "safeguard",
    condition: {
      duration: 5,
      durationCallback(target, source, effect) {
        if (source?.hasAbility("persistent")) {
          this.add("-activate", source, "ability: Persistent", "[move] Safeguard");
          return 7;
        }
        return 5;
      },
      onSetStatus(status, target, source, effect) {
        if (!effect || !source)
          return;
        if (effect.id === "yawn")
          return;
        if (effect.effectType === "Move" && effect.infiltrates && !target.isAlly(source))
          return;
        if (target !== source) {
          this.debug("interrupting setStatus");
          if (effect.name === "Synchronize" || effect.effectType === "Move" && !effect.secondaries) {
            this.add("-activate", target, "move: Safeguard");
          }
          return null;
        }
      },
      onTryAddVolatile(status, target, source, effect) {
        if (!effect || !source)
          return;
        if (effect.effectType === "Move" && effect.infiltrates && !target.isAlly(source))
          return;
        if ((status.id === "confusion" || status.id === "yawn") && target !== source) {
          if (effect.effectType === "Move" && !effect.secondaries)
            this.add("-activate", target, "move: Safeguard");
          return null;
        }
      },
      onSideStart(side, source) {
        if (source?.hasAbility("persistent")) {
          this.add("-sidestart", side, "Safeguard", "[persistent]");
        } else {
          this.add("-sidestart", side, "Safeguard");
        }
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 3,
      onSideEnd(side) {
        this.add("-sideend", side, "Safeguard");
      }
    },
    secondary: null,
    target: "allySide",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Beautiful"
  },
  saltcure: {
    num: 864,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Salt Cure",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    condition: {
      noCopy: true,
      onStart(pokemon) {
        this.add("-start", pokemon, "Salt Cure");
      },
      onResidualOrder: 13,
      onResidual(pokemon) {
        this.damage(pokemon.baseMaxhp / (pokemon.hasType(["Water", "Steel"]) ? 4 : 8));
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Salt Cure");
      }
    },
    secondary: {
      chance: 100,
      volatileStatus: "saltcure"
    },
    target: "normal",
    type: "Rock"
  },
  sandattack: {
    num: 28,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Sand Attack",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      accuracy: -1
    },
    secondary: null,
    target: "normal",
    type: "Ground",
    zMove: { boost: { evasion: 1 } },
    contestType: "Cute"
  },
  sandsearstorm: {
    num: 848,
    accuracy: 80,
    basePower: 100,
    category: "Special",
    name: "Sandsear Storm",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    onModifyMove(move, pokemon, target) {
      if (target && ["raindance", "primordialsea"].includes(target.effectiveWeather())) {
        move.accuracy = true;
      }
    },
    secondary: {
      chance: 20,
      status: "brn"
    },
    target: "allAdjacentFoes",
    type: "Ground"
  },
  sandstorm: {
    num: 201,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Sandstorm",
    pp: 10,
    priority: 0,
    flags: { metronome: 1, wind: 1 },
    weather: "Sandstorm",
    secondary: null,
    target: "all",
    type: "Rock",
    zMove: { boost: { spe: 1 } },
    contestType: "Tough"
  },
  sandtomb: {
    num: 328,
    accuracy: 85,
    basePower: 35,
    category: "Physical",
    name: "Sand Tomb",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Ground",
    contestType: "Clever"
  },
  sappyseed: {
    num: 738,
    accuracy: 90,
    basePower: 100,
    category: "Physical",
    isNonstandard: "LGPE",
    name: "Sappy Seed",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1 },
    onHit(target, source) {
      if (target.hasType("Grass"))
        return null;
      target.addVolatile("leechseed", source);
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Clever"
  },
  savagespinout: {
    num: 634,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Savage Spin-Out",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "buginiumz",
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cool"
  },
  scald: {
    num: 503,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Scald",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1, metronome: 1 },
    thawsTarget: true,
    secondary: {
      chance: 30,
      status: "brn"
    },
    target: "normal",
    type: "Water",
    contestType: "Tough"
  },
  scaleshot: {
    num: 799,
    accuracy: 90,
    basePower: 25,
    category: "Physical",
    name: "Scale Shot",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    selfBoost: {
      boosts: {
        def: -1,
        spe: 1
      }
    },
    secondary: null,
    target: "normal",
    type: "Dragon",
    zMove: { basePower: 140 },
    maxMove: { basePower: 130 }
  },
  scaryface: {
    num: 184,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Scary Face",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    boosts: {
      spe: -2
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Tough"
  },
  scorchingsands: {
    num: 815,
    accuracy: 100,
    basePower: 70,
    category: "Special",
    name: "Scorching Sands",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1, metronome: 1 },
    thawsTarget: true,
    secondary: {
      chance: 30,
      status: "brn"
    },
    target: "normal",
    type: "Ground"
  },
  scratch: {
    num: 10,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Scratch",
    pp: 35,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  screech: {
    num: 103,
    accuracy: 85,
    basePower: 0,
    category: "Status",
    name: "Screech",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    boosts: {
      def: -2
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { atk: 1 } },
    contestType: "Clever"
  },
  searingshot: {
    num: 545,
    accuracy: 100,
    basePower: 100,
    category: "Special",
    isNonstandard: "Past",
    name: "Searing Shot",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 30,
      status: "brn"
    },
    target: "allAdjacent",
    type: "Fire",
    contestType: "Cool"
  },
  searingsunrazesmash: {
    num: 724,
    accuracy: true,
    basePower: 200,
    category: "Physical",
    isNonstandard: "Past",
    name: "Searing Sunraze Smash",
    pp: 1,
    priority: 0,
    flags: { contact: 1 },
    isZ: "solganiumz",
    ignoreAbility: true,
    secondary: null,
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  secretpower: {
    num: 290,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    isNonstandard: "Past",
    name: "Secret Power",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onModifyMove(move, pokemon) {
      if (this.field.isTerrain(""))
        return;
      move.secondaries = [];
      if (this.field.isTerrain("electricterrain")) {
        move.secondaries.push({
          chance: 30,
          status: "par"
        });
      } else if (this.field.isTerrain("grassyterrain")) {
        move.secondaries.push({
          chance: 30,
          status: "slp"
        });
      } else if (this.field.isTerrain("mistyterrain")) {
        move.secondaries.push({
          chance: 30,
          boosts: {
            spa: -1
          }
        });
      } else if (this.field.isTerrain("psychicterrain")) {
        move.secondaries.push({
          chance: 30,
          boosts: {
            spe: -1
          }
        });
      }
    },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Normal",
    contestType: "Clever"
  },
  secretsword: {
    num: 548,
    accuracy: 100,
    basePower: 85,
    category: "Special",
    overrideDefensiveStat: "def",
    name: "Secret Sword",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, slicing: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Beautiful"
  },
  seedbomb: {
    num: 402,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Seed Bomb",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Tough"
  },
  seedflare: {
    num: 465,
    accuracy: 85,
    basePower: 120,
    category: "Special",
    name: "Seed Flare",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 40,
      boosts: {
        spd: -2
      }
    },
    target: "normal",
    type: "Grass",
    contestType: "Beautiful"
  },
  seismictoss: {
    num: 69,
    accuracy: 100,
    basePower: 0,
    damage: "level",
    category: "Physical",
    name: "Seismic Toss",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    maxMove: { basePower: 75 },
    contestType: "Tough"
  },
  selfdestruct: {
    num: 120,
    accuracy: 100,
    basePower: 200,
    category: "Physical",
    name: "Self-Destruct",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, noparentalbond: 1 },
    selfdestruct: "always",
    secondary: null,
    target: "allAdjacent",
    type: "Normal",
    contestType: "Beautiful"
  },
  shadowball: {
    num: 247,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Shadow Ball",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 20,
      boosts: {
        spd: -1
      }
    },
    target: "normal",
    type: "Ghost",
    contestType: "Clever"
  },
  shadowbone: {
    num: 708,
    accuracy: 100,
    basePower: 85,
    category: "Physical",
    isNonstandard: "Past",
    name: "Shadow Bone",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  shadowclaw: {
    num: 421,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Shadow Claw",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  shadowforce: {
    num: 467,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Shadow Force",
    pp: 5,
    priority: 0,
    flags: { contact: 1, charge: 1, mirror: 1, metronome: 1, nosleeptalk: 1, noassist: 1, failinstruct: 1 },
    breaksProtect: true,
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    condition: {
      duration: 2,
      onInvulnerability: false
    },
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  shadowpunch: {
    num: 325,
    accuracy: true,
    basePower: 60,
    category: "Physical",
    name: "Shadow Punch",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Clever"
  },
  shadowsneak: {
    num: 425,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Shadow Sneak",
    pp: 30,
    priority: 1,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Clever"
  },
  shadowstrike: {
    num: 0,
    accuracy: 95,
    basePower: 80,
    category: "Physical",
    isNonstandard: "CAP",
    name: "Shadow Strike",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: {
      chance: 50,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Ghost",
    contestType: "Clever"
  },
  sharpen: {
    num: 159,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Sharpen",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      atk: 1
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { atk: 1 } },
    contestType: "Cute"
  },
  shatteredpsyche: {
    num: 648,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Shattered Psyche",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "psychiumz",
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  shedtail: {
    num: 880,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Shed Tail",
    pp: 10,
    priority: 0,
    flags: {},
    volatileStatus: "substitute",
    onTryHit(source) {
      if (!this.canSwitch(source.side) || source.volatiles["commanded"]) {
        this.add("-fail", source);
        return this.NOT_FAIL;
      }
      if (source.volatiles["substitute"]) {
        this.add("-fail", source, "move: Shed Tail");
        return this.NOT_FAIL;
      }
      if (source.hp <= Math.ceil(source.maxhp / 2)) {
        this.add("-fail", source, "move: Shed Tail", "[weak]");
        return this.NOT_FAIL;
      }
    },
    onHit(target) {
      this.directDamage(Math.ceil(target.maxhp / 2));
    },
    self: {
      onHit(source) {
        source.skipBeforeSwitchOutEventFlag = true;
      }
    },
    selfSwitch: "shedtail",
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" }
  },
  sheercold: {
    num: 329,
    accuracy: 30,
    basePower: 0,
    category: "Special",
    name: "Sheer Cold",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    ohko: "Ice",
    target: "normal",
    type: "Ice",
    zMove: { basePower: 180 },
    maxMove: { basePower: 130 },
    contestType: "Beautiful"
  },
  shellsidearm: {
    num: 801,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Shell Side Arm",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onPrepareHit(target, source, move) {
      if (!source.isAlly(target)) {
        this.attrLastMove("[anim] Shell Side Arm " + move.category);
      }
    },
    onModifyMove(move, pokemon, target) {
      if (!target)
        return;
      const atk = pokemon.getStat("atk", false, true);
      const spa = pokemon.getStat("spa", false, true);
      const def = target.getStat("def", false, true);
      const spd = target.getStat("spd", false, true);
      const physical = Math.floor(Math.floor(Math.floor(Math.floor(2 * pokemon.level / 5 + 2) * 90 * atk) / def) / 50);
      const special = Math.floor(Math.floor(Math.floor(Math.floor(2 * pokemon.level / 5 + 2) * 90 * spa) / spd) / 50);
      if (physical > special || physical === special && this.random(2) === 0) {
        move.category = "Physical";
        move.flags.contact = 1;
      }
    },
    onHit(target, source, move) {
      if (!source.isAlly(target))
        this.hint(move.category + " Shell Side Arm");
    },
    onAfterSubDamage(damage, target, source, move) {
      if (!source.isAlly(target))
        this.hint(move.category + " Shell Side Arm");
    },
    secondary: {
      chance: 20,
      status: "psn"
    },
    target: "normal",
    type: "Poison"
  },
  shellsmash: {
    num: 504,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Shell Smash",
    pp: 15,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: -1,
      spd: -1,
      atk: 2,
      spa: 2,
      spe: 2
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Tough"
  },
  shelltrap: {
    num: 704,
    accuracy: 100,
    basePower: 150,
    category: "Special",
    isNonstandard: "Past",
    name: "Shell Trap",
    pp: 5,
    priority: -3,
    flags: { protect: 1, failmefirst: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failinstruct: 1 },
    priorityChargeCallback(pokemon) {
      pokemon.addVolatile("shelltrap");
    },
    onTryMove(pokemon) {
      if (!pokemon.volatiles["shelltrap"]?.gotHit) {
        this.attrLastMove("[still]");
        this.add("cant", pokemon, "Shell Trap", "Shell Trap");
        return null;
      }
    },
    condition: {
      duration: 1,
      onStart(pokemon) {
        this.add("-singleturn", pokemon, "move: Shell Trap");
      },
      onHit(pokemon, source, move) {
        if (!pokemon.isAlly(source) && move.category === "Physical") {
          this.effectState.gotHit = true;
          const action = this.queue.willMove(pokemon);
          if (action) {
            this.queue.prioritizeAction(action);
          }
        }
      }
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Fire",
    contestType: "Tough"
  },
  shelter: {
    num: 842,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Shelter",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 2
    },
    secondary: null,
    target: "self",
    type: "Steel"
  },
  shiftgear: {
    num: 508,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Shift Gear",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      spe: 2,
      atk: 1
    },
    secondary: null,
    target: "self",
    type: "Steel",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  shockwave: {
    num: 351,
    accuracy: true,
    basePower: 60,
    category: "Special",
    name: "Shock Wave",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  shoreup: {
    num: 659,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Shore Up",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    onHit(pokemon) {
      let factor = 0.5;
      if (this.field.isWeather("sandstorm")) {
        factor = 0.667;
      }
      const success = !!this.heal(this.modify(pokemon.maxhp, factor));
      if (!success) {
        this.add("-fail", pokemon, "heal");
        return this.NOT_FAIL;
      }
      return success;
    },
    secondary: null,
    target: "self",
    type: "Ground",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  signalbeam: {
    num: 324,
    accuracy: 100,
    basePower: 75,
    category: "Special",
    isNonstandard: "Past",
    name: "Signal Beam",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Bug",
    contestType: "Beautiful"
  },
  silktrap: {
    num: 852,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Silk Trap",
    pp: 10,
    priority: 4,
    flags: {},
    stallingMove: true,
    volatileStatus: "silktrap",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    onHit(pokemon) {
      pokemon.addVolatile("stall");
    },
    condition: {
      duration: 1,
      onStart(target) {
        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;
          return;
        }
        if (move.smartTarget) {
          move.smartTarget = false;
        } else {
          this.add("-activate", target, "move: Protect");
        }
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        if (this.checkMoveMakesContact(move, source, target)) {
          this.boost({ spe: -1 }, source, target, this.dex.getActiveMove("Silk Trap"));
        }
        return this.NOT_FAIL;
      },
      onHit(target, source, move) {
        if (move.isZOrMaxPowered && this.checkMoveMakesContact(move, source, target)) {
          this.boost({ spe: -1 }, source, target, this.dex.getActiveMove("Silk Trap"));
        }
      }
    },
    target: "self",
    type: "Bug"
  },
  silverwind: {
    num: 318,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    isNonstandard: "Past",
    name: "Silver Wind",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      self: {
        boosts: {
          atk: 1,
          def: 1,
          spa: 1,
          spd: 1,
          spe: 1
        }
      }
    },
    target: "normal",
    type: "Bug",
    contestType: "Beautiful"
  },
  simplebeam: {
    num: 493,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Simple Beam",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onTryHit(target) {
      if (target.getAbility().flags["cantsuppress"] || target.ability === "simple" || target.ability === "truant") {
        return false;
      }
    },
    onHit(pokemon) {
      const oldAbility = pokemon.setAbility("simple");
      if (oldAbility) {
        this.add("-ability", pokemon, "Simple", "[from] move: Simple Beam");
        return;
      }
      return oldAbility;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spa: 1 } },
    contestType: "Cute"
  },
  sing: {
    num: 47,
    accuracy: 55,
    basePower: 0,
    category: "Status",
    name: "Sing",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    status: "slp",
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Cute"
  },
  sinisterarrowraid: {
    num: 695,
    accuracy: true,
    basePower: 180,
    category: "Physical",
    isNonstandard: "Past",
    name: "Sinister Arrow Raid",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "decidiumz",
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  sizzlyslide: {
    num: 735,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    isNonstandard: "LGPE",
    name: "Sizzly Slide",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, defrost: 1 },
    secondary: {
      chance: 100,
      status: "brn"
    },
    target: "normal",
    type: "Fire",
    contestType: "Clever"
  },
  sketch: {
    num: 166,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Sketch",
    pp: 1,
    noPPBoosts: true,
    priority: 0,
    flags: {
      bypasssub: 1,
      allyanim: 1,
      failencore: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    onHit(target, source) {
      const move = target.lastMove;
      if (source.transformed || !move || source.moves.includes(move.id))
        return false;
      if (move.noSketch || move.isZ || move.isMax)
        return false;
      const sketchIndex = source.moves.indexOf("sketch");
      if (sketchIndex < 0)
        return false;
      const sketchedMove = {
        move: move.name,
        id: move.id,
        pp: move.pp,
        maxpp: move.pp,
        target: move.target,
        disabled: false,
        used: false
      };
      source.moveSlots[sketchIndex] = sketchedMove;
      source.baseMoveSlots[sketchIndex] = sketchedMove;
      this.add("-activate", source, "move: Sketch", move.name);
    },
    noSketch: true,
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Clever"
  },
  skillswap: {
    num: 285,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Skill Swap",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    onTryHit(target, source) {
      const targetAbility = target.getAbility();
      const sourceAbility = source.getAbility();
      if (sourceAbility.flags["failskillswap"] || targetAbility.flags["failskillswap"] || target.volatiles["dynamax"]) {
        return false;
      }
      const sourceCanBeSet = this.runEvent("SetAbility", source, source, this.effect, targetAbility);
      if (!sourceCanBeSet)
        return sourceCanBeSet;
      const targetCanBeSet = this.runEvent("SetAbility", target, source, this.effect, sourceAbility);
      if (!targetCanBeSet)
        return targetCanBeSet;
    },
    onHit(target, source, move) {
      const targetAbility = target.getAbility();
      const sourceAbility = source.getAbility();
      if (target.isAlly(source)) {
        this.add("-activate", source, "move: Skill Swap", "", "", "[of] " + target);
      } else {
        this.add("-activate", source, "move: Skill Swap", targetAbility, sourceAbility, "[of] " + target);
      }
      this.singleEvent("End", sourceAbility, source.abilityState, source);
      this.singleEvent("End", targetAbility, target.abilityState, target);
      source.ability = targetAbility.id;
      target.ability = sourceAbility.id;
      source.abilityState = { id: this.toID(source.ability), target: source };
      target.abilityState = { id: this.toID(target.ability), target };
      if (!target.isAlly(source))
        target.volatileStaleness = "external";
      this.singleEvent("Start", targetAbility, source.abilityState, source);
      this.singleEvent("Start", sourceAbility, target.abilityState, target);
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  skittersmack: {
    num: 806,
    accuracy: 90,
    basePower: 70,
    category: "Physical",
    name: "Skitter Smack",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spa: -1
      }
    },
    target: "normal",
    type: "Bug"
  },
  skullbash: {
    num: 130,
    accuracy: 100,
    basePower: 130,
    category: "Physical",
    isNonstandard: "Past",
    name: "Skull Bash",
    pp: 10,
    priority: 0,
    flags: { contact: 1, charge: 1, protect: 1, mirror: 1, metronome: 1, nosleeptalk: 1, failinstruct: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      this.boost({ def: 1 }, attacker, attacker, move);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  skyattack: {
    num: 143,
    accuracy: 90,
    basePower: 140,
    category: "Physical",
    name: "Sky Attack",
    pp: 5,
    priority: 0,
    flags: { charge: 1, protect: 1, mirror: 1, distance: 1, metronome: 1, nosleeptalk: 1, failinstruct: 1 },
    critRatio: 2,
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  skydrop: {
    num: 507,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    isNonstandard: "Past",
    name: "Sky Drop",
    pp: 10,
    priority: 0,
    flags: {
      contact: 1,
      charge: 1,
      protect: 1,
      mirror: 1,
      gravity: 1,
      distance: 1,
      metronome: 1,
      nosleeptalk: 1,
      noassist: 1,
      failinstruct: 1
    },
    onModifyMove(move, source) {
      if (!source.volatiles["skydrop"]) {
        move.accuracy = true;
        delete move.flags["contact"];
      }
    },
    onMoveFail(target, source) {
      if (source.volatiles["twoturnmove"] && source.volatiles["twoturnmove"].duration === 1) {
        source.removeVolatile("skydrop");
        source.removeVolatile("twoturnmove");
        if (target === this.effectState.target) {
          this.add("-end", target, "Sky Drop", "[interrupt]");
        }
      }
    },
    onTry(source, target) {
      return !target.fainted;
    },
    onTryHit(target, source, move) {
      if (source.removeVolatile(move.id)) {
        if (target !== source.volatiles["twoturnmove"].source)
          return false;
        if (target.hasType("Flying")) {
          this.add("-immune", target);
          return null;
        }
      } else {
        if (target.volatiles["substitute"] || target.isAlly(source)) {
          return false;
        }
        if (target.getWeight() >= 2e3) {
          this.add("-fail", target, "move: Sky Drop", "[heavy]");
          return null;
        }
        this.add("-prepare", source, move.name, target);
        source.addVolatile("twoturnmove", target);
        return null;
      }
    },
    onHit(target, source) {
      if (target.hp)
        this.add("-end", target, "Sky Drop");
    },
    condition: {
      duration: 2,
      onAnyDragOut(pokemon) {
        if (pokemon === this.effectState.target || pokemon === this.effectState.source)
          return false;
      },
      onFoeTrapPokemonPriority: -15,
      onFoeTrapPokemon(defender) {
        if (defender !== this.effectState.source)
          return;
        defender.trapped = true;
      },
      onFoeBeforeMovePriority: 12,
      onFoeBeforeMove(attacker, defender, move) {
        if (attacker === this.effectState.source) {
          attacker.activeMoveActions--;
          this.debug("Sky drop nullifying.");
          return null;
        }
      },
      onRedirectTargetPriority: 99,
      onRedirectTarget(target, source, source2) {
        if (source !== this.effectState.target)
          return;
        if (this.effectState.source.fainted)
          return;
        return this.effectState.source;
      },
      onAnyInvulnerability(target, source, move) {
        if (target !== this.effectState.target && target !== this.effectState.source) {
          return;
        }
        if (source === this.effectState.target && target === this.effectState.source) {
          return;
        }
        if (["gust", "twister", "skyuppercut", "thunder", "hurricane", "smackdown", "thousandarrows"].includes(move.id)) {
          return;
        }
        return false;
      },
      onAnyBasePower(basePower, target, source, move) {
        if (target !== this.effectState.target && target !== this.effectState.source) {
          return;
        }
        if (source === this.effectState.target && target === this.effectState.source) {
          return;
        }
        if (move.id === "gust" || move.id === "twister") {
          this.debug("BP doubled on midair target");
          return this.chainModify(2);
        }
      },
      onFaint(target) {
        if (target.volatiles["skydrop"] && target.volatiles["twoturnmove"].source) {
          this.add("-end", target.volatiles["twoturnmove"].source, "Sky Drop", "[interrupt]");
        }
      }
    },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Tough"
  },
  skyuppercut: {
    num: 327,
    accuracy: 90,
    basePower: 85,
    category: "Physical",
    isNonstandard: "Past",
    name: "Sky Uppercut",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  slackoff: {
    num: 303,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Slack Off",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    heal: [1, 2],
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  slam: {
    num: 21,
    accuracy: 75,
    basePower: 80,
    category: "Physical",
    name: "Slam",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  slash: {
    num: 163,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Slash",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  sleeppowder: {
    num: 79,
    accuracy: 75,
    basePower: 0,
    category: "Status",
    name: "Sleep Powder",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1, powder: 1 },
    status: "slp",
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  sleeptalk: {
    num: 214,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Sleep Talk",
    pp: 10,
    priority: 0,
    flags: { failencore: 1, nosleeptalk: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    sleepUsable: true,
    onTry(source) {
      return source.status === "slp" || source.hasAbility("comatose");
    },
    onHit(pokemon) {
      const moves = [];
      for (const moveSlot of pokemon.moveSlots) {
        const moveid = moveSlot.id;
        if (!moveid)
          continue;
        const move = this.dex.moves.get(moveid);
        if (move.flags["nosleeptalk"] || move.flags["charge"] || move.isZ && move.basePower !== 1 || move.isMax) {
          continue;
        }
        moves.push(moveid);
      }
      let randomMove = "";
      if (moves.length)
        randomMove = this.sample(moves);
      if (!randomMove) {
        return false;
      }
      this.actions.useMove(randomMove, pokemon);
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "crit2" },
    contestType: "Cute"
  },
  sludge: {
    num: 124,
    accuracy: 100,
    basePower: 65,
    category: "Special",
    name: "Sludge",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "psn"
    },
    target: "normal",
    type: "Poison",
    contestType: "Tough"
  },
  sludgebomb: {
    num: 188,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Sludge Bomb",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 30,
      status: "psn"
    },
    target: "normal",
    type: "Poison",
    contestType: "Tough"
  },
  sludgewave: {
    num: 482,
    accuracy: 100,
    basePower: 95,
    category: "Special",
    name: "Sludge Wave",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "psn"
    },
    target: "allAdjacent",
    type: "Poison",
    contestType: "Tough"
  },
  smackdown: {
    num: 479,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Smack Down",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    volatileStatus: "smackdown",
    condition: {
      noCopy: true,
      onStart(pokemon) {
        let applies = false;
        if (pokemon.hasType("Flying") || pokemon.hasAbility("levitate"))
          applies = true;
        if (pokemon.hasItem("ironball") || pokemon.volatiles["ingrain"] || this.field.getPseudoWeather("gravity"))
          applies = false;
        if (pokemon.removeVolatile("fly") || pokemon.removeVolatile("bounce")) {
          applies = true;
          this.queue.cancelMove(pokemon);
          pokemon.removeVolatile("twoturnmove");
        }
        if (pokemon.volatiles["magnetrise"]) {
          applies = true;
          delete pokemon.volatiles["magnetrise"];
        }
        if (pokemon.volatiles["telekinesis"]) {
          applies = true;
          delete pokemon.volatiles["telekinesis"];
        }
        if (!applies)
          return false;
        this.add("-start", pokemon, "Smack Down");
      },
      onRestart(pokemon) {
        if (pokemon.removeVolatile("fly") || pokemon.removeVolatile("bounce")) {
          this.queue.cancelMove(pokemon);
          pokemon.removeVolatile("twoturnmove");
          this.add("-start", pokemon, "Smack Down");
        }
      }
      // groundedness implemented in battle.engine.js:BattlePokemon#isGrounded
    },
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Tough"
  },
  smartstrike: {
    num: 684,
    accuracy: true,
    basePower: 70,
    category: "Physical",
    name: "Smart Strike",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  smellingsalts: {
    num: 265,
    accuracy: 100,
    basePower: 70,
    basePowerCallback(pokemon, target, move) {
      if (target.status === "par") {
        this.debug("BP doubled on paralyzed target");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Smelling Salts",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onHit(target) {
      if (target.status === "par")
        target.cureStatus();
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  smog: {
    num: 123,
    accuracy: 70,
    basePower: 30,
    category: "Special",
    name: "Smog",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 40,
      status: "psn"
    },
    target: "normal",
    type: "Poison",
    contestType: "Tough"
  },
  smokescreen: {
    num: 108,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Smokescreen",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      accuracy: -1
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { evasion: 1 } },
    contestType: "Clever"
  },
  snaptrap: {
    num: 779,
    accuracy: 100,
    basePower: 35,
    category: "Physical",
    isNonstandard: "Past",
    name: "Snap Trap",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Grass"
  },
  snarl: {
    num: 555,
    accuracy: 95,
    basePower: 55,
    category: "Special",
    name: "Snarl",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spa: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Dark",
    contestType: "Tough"
  },
  snatch: {
    num: 289,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Snatch",
    pp: 10,
    priority: 4,
    flags: { bypasssub: 1, mustpressure: 1, noassist: 1, failcopycat: 1 },
    volatileStatus: "snatch",
    condition: {
      duration: 1,
      onStart(pokemon) {
        this.add("-singleturn", pokemon, "Snatch");
      },
      onAnyPrepareHitPriority: -1,
      onAnyPrepareHit(source, target, move) {
        const snatchUser = this.effectState.source;
        if (snatchUser.isSkyDropped())
          return;
        if (!move || move.isZ || move.isMax || !move.flags["snatch"] || move.sourceEffect === "snatch") {
          return;
        }
        snatchUser.removeVolatile("snatch");
        this.add("-activate", snatchUser, "move: Snatch", "[of] " + source);
        this.actions.useMove(move.id, snatchUser);
        return null;
      }
    },
    secondary: null,
    target: "self",
    type: "Dark",
    zMove: { boost: { spe: 2 } },
    contestType: "Clever"
  },
  snipeshot: {
    num: 745,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Snipe Shot",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    tracksTarget: true,
    secondary: null,
    target: "normal",
    type: "Water"
  },
  snore: {
    num: 173,
    accuracy: 100,
    basePower: 50,
    category: "Special",
    name: "Snore",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1 },
    sleepUsable: true,
    onTry(source) {
      return source.status === "slp" || source.hasAbility("comatose");
    },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  snowscape: {
    num: 883,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Snowscape",
    pp: 10,
    priority: 0,
    flags: {},
    weather: "snow",
    secondary: null,
    target: "all",
    type: "Ice"
  },
  soak: {
    num: 487,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Soak",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onHit(target) {
      if (target.getTypes().join() === "Water" || !target.setType("Water")) {
        this.add("-fail", target);
        return null;
      }
      this.add("-start", target, "typechange", "Water");
    },
    secondary: null,
    target: "normal",
    type: "Water",
    zMove: { boost: { spa: 1 } },
    contestType: "Cute"
  },
  softboiled: {
    num: 135,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Soft-Boiled",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    heal: [1, 2],
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  solarbeam: {
    num: 76,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    name: "Solar Beam",
    pp: 10,
    priority: 0,
    flags: { charge: 1, protect: 1, mirror: 1, metronome: 1, nosleeptalk: 1, failinstruct: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (["sunnyday", "desolateland"].includes(attacker.effectiveWeather())) {
        this.attrLastMove("[still]");
        this.addMove("-anim", attacker, move.name, defender);
        return;
      }
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    onBasePower(basePower, pokemon, target) {
      const weakWeathers = ["raindance", "primordialsea", "sandstorm", "hail", "snow"];
      if (weakWeathers.includes(pokemon.effectiveWeather())) {
        this.debug("weakened by weather");
        return this.chainModify(0.5);
      }
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  solarblade: {
    num: 669,
    accuracy: 100,
    basePower: 125,
    category: "Physical",
    name: "Solar Blade",
    pp: 10,
    priority: 0,
    flags: { contact: 1, charge: 1, protect: 1, mirror: 1, metronome: 1, nosleeptalk: 1, failinstruct: 1, slicing: 1 },
    onTryMove(attacker, defender, move) {
      if (attacker.removeVolatile(move.id)) {
        return;
      }
      this.add("-prepare", attacker, move.name);
      if (["sunnyday", "desolateland"].includes(attacker.effectiveWeather())) {
        this.attrLastMove("[still]");
        this.addMove("-anim", attacker, move.name, defender);
        return;
      }
      if (!this.runEvent("ChargeMove", attacker, defender, move)) {
        return;
      }
      attacker.addVolatile("twoturnmove", defender);
      return null;
    },
    onBasePower(basePower, pokemon, target) {
      const weakWeathers = ["raindance", "primordialsea", "sandstorm", "hail", "snow"];
      if (weakWeathers.includes(pokemon.effectiveWeather())) {
        this.debug("weakened by weather");
        return this.chainModify(0.5);
      }
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  sonicboom: {
    num: 49,
    accuracy: 90,
    basePower: 0,
    damage: 20,
    category: "Special",
    isNonstandard: "Past",
    name: "Sonic Boom",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  soulstealing7starstrike: {
    num: 699,
    accuracy: true,
    basePower: 195,
    category: "Physical",
    isNonstandard: "Past",
    name: "Soul-Stealing 7-Star Strike",
    pp: 1,
    priority: 0,
    flags: { contact: 1 },
    isZ: "marshadiumz",
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  spacialrend: {
    num: 460,
    accuracy: 95,
    basePower: 100,
    category: "Special",
    name: "Spacial Rend",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Dragon",
    contestType: "Beautiful"
  },
  spark: {
    num: 209,
    accuracy: 100,
    basePower: 65,
    category: "Physical",
    name: "Spark",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  sparklingaria: {
    num: 664,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Sparkling Aria",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: {
      dustproof: true,
      chance: 100,
      volatileStatus: "sparklingaria"
    },
    onAfterMove(source, target, move) {
      for (const pokemon of this.getAllActive()) {
        if (pokemon !== source && pokemon.removeVolatile("sparklingaria") && pokemon.status === "brn" && !source.fainted) {
          pokemon.cureStatus();
        }
      }
    },
    target: "allAdjacent",
    type: "Water",
    contestType: "Tough"
  },
  sparklyswirl: {
    num: 740,
    accuracy: 85,
    basePower: 120,
    category: "Special",
    isNonstandard: "LGPE",
    name: "Sparkly Swirl",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    self: {
      onHit(pokemon, source, move) {
        this.add("-activate", source, "move: Aromatherapy");
        for (const ally of source.side.pokemon) {
          if (ally !== source && (ally.volatiles["substitute"] && !move.infiltrates)) {
            continue;
          }
          ally.cureStatus();
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Clever"
  },
  spectralthief: {
    num: 712,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    isNonstandard: "Past",
    name: "Spectral Thief",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, bypasssub: 1 },
    stealsBoosts: true,
    // Boost stealing implemented in scripts.js
    secondary: null,
    target: "normal",
    type: "Ghost",
    contestType: "Cool"
  },
  speedswap: {
    num: 683,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Speed Swap",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, bypasssub: 1, allyanim: 1, metronome: 1 },
    onHit(target, source) {
      const targetSpe = target.storedStats.spe;
      target.storedStats.spe = source.storedStats.spe;
      source.storedStats.spe = targetSpe;
      this.add("-activate", source, "move: Speed Swap", "[of] " + target);
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  spicyextract: {
    num: 858,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Spicy Extract",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1 },
    boosts: {
      atk: 2,
      def: -2
    },
    secondary: null,
    target: "normal",
    type: "Grass"
  },
  spiderweb: {
    num: 169,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Spider Web",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    onHit(target, source, move) {
      return target.addVolatile("trapped", source, move, "trapper");
    },
    secondary: null,
    target: "normal",
    type: "Bug",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  spikecannon: {
    num: 131,
    accuracy: 100,
    basePower: 20,
    category: "Physical",
    isNonstandard: "Past",
    name: "Spike Cannon",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Normal",
    maxMove: { basePower: 120 },
    contestType: "Cool"
  },
  spikes: {
    num: 191,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Spikes",
    pp: 20,
    priority: 0,
    flags: { reflectable: 1, nonsky: 1, metronome: 1, mustpressure: 1 },
    sideCondition: "spikes",
    condition: {
      // this is a side condition
      onSideStart(side) {
        this.add("-sidestart", side, "Spikes");
        this.effectState.layers = 1;
      },
      onSideRestart(side) {
        if (this.effectState.layers >= 3)
          return false;
        this.add("-sidestart", side, "Spikes");
        this.effectState.layers++;
      },
      onEntryHazard(pokemon) {
        if (!pokemon.isGrounded() || pokemon.hasItem("heavydutyboots"))
          return;
        const damageAmounts = [0, 3, 4, 6];
        this.damage(damageAmounts[this.effectState.layers] * pokemon.maxhp / 24);
      }
    },
    secondary: null,
    target: "foeSide",
    type: "Ground",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  spikyshield: {
    num: 596,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Spiky Shield",
    pp: 10,
    priority: 4,
    flags: { noassist: 1, failcopycat: 1 },
    stallingMove: true,
    volatileStatus: "spikyshield",
    onPrepareHit(pokemon) {
      return !!this.queue.willAct() && this.runEvent("StallMove", pokemon);
    },
    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 (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          if (move.isZ || move.isMax)
            target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        if (move.smartTarget) {
          move.smartTarget = false;
        } else {
          this.add("-activate", target, "move: Protect");
        }
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        if (this.checkMoveMakesContact(move, source, target)) {
          this.damage(source.baseMaxhp / 8, source, target);
        }
        return this.NOT_FAIL;
      },
      onHit(target, source, move) {
        if (move.isZOrMaxPowered && this.checkMoveMakesContact(move, source, target)) {
          this.damage(source.baseMaxhp / 8, source, target);
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Grass",
    zMove: { boost: { def: 1 } },
    contestType: "Tough"
  },
  spinout: {
    num: 859,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Spin Out",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      boosts: {
        spe: -2
      }
    },
    secondary: null,
    target: "normal",
    type: "Steel"
  },
  spiritbreak: {
    num: 789,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Spirit Break",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spa: -1
      }
    },
    target: "normal",
    type: "Fairy"
  },
  spiritshackle: {
    num: 662,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Spirit Shackle",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      onHit(target, source, move) {
        if (source.isActive)
          target.addVolatile("trapped", source, move, "trapper");
      }
    },
    target: "normal",
    type: "Ghost",
    contestType: "Tough"
  },
  spitup: {
    num: 255,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon) {
      if (!pokemon.volatiles["stockpile"]?.layers)
        return false;
      return pokemon.volatiles["stockpile"].layers * 100;
    },
    category: "Special",
    name: "Spit Up",
    pp: 10,
    priority: 0,
    flags: { protect: 1, metronome: 1 },
    onTry(source) {
      return !!source.volatiles["stockpile"];
    },
    onAfterMove(pokemon) {
      pokemon.removeVolatile("stockpile");
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  spite: {
    num: 180,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Spite",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    onHit(target) {
      let move = target.lastMove;
      if (!move || move.isZ)
        return false;
      if (move.isMax && move.baseMove)
        move = this.dex.moves.get(move.baseMove);
      const ppDeducted = target.deductPP(move.id, 4);
      if (!ppDeducted)
        return false;
      this.add("-activate", target, "move: Spite", move.name, ppDeducted);
    },
    secondary: null,
    target: "normal",
    type: "Ghost",
    zMove: { effect: "heal" },
    contestType: "Tough"
  },
  splash: {
    num: 150,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Splash",
    pp: 40,
    priority: 0,
    flags: { gravity: 1, metronome: 1 },
    onTry(source, target, move) {
      if (this.field.getPseudoWeather("Gravity")) {
        this.add("cant", source, "move: Gravity", move);
        return null;
      }
    },
    onTryHit(target, source) {
      this.add("-nothing");
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { atk: 3 } },
    contestType: "Cute"
  },
  splinteredstormshards: {
    num: 727,
    accuracy: true,
    basePower: 190,
    category: "Physical",
    isNonstandard: "Past",
    name: "Splintered Stormshards",
    pp: 1,
    priority: 0,
    flags: {},
    onHit() {
      this.field.clearTerrain();
    },
    onAfterSubDamage() {
      this.field.clearTerrain();
    },
    isZ: "lycaniumz",
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Cool"
  },
  splishysplash: {
    num: 730,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    isNonstandard: "LGPE",
    name: "Splishy Splash",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "allAdjacentFoes",
    type: "Water",
    contestType: "Cool"
  },
  spore: {
    num: 147,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Spore",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1, powder: 1 },
    status: "slp",
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  spotlight: {
    num: 671,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Spotlight",
    pp: 15,
    priority: 3,
    flags: { protect: 1, reflectable: 1, allyanim: 1, noassist: 1, failcopycat: 1 },
    volatileStatus: "spotlight",
    onTryHit(target) {
      if (this.activePerHalf === 1)
        return false;
    },
    condition: {
      duration: 1,
      onStart(pokemon) {
        this.add("-singleturn", pokemon, "move: Spotlight");
      },
      onFoeRedirectTargetPriority: 2,
      onFoeRedirectTarget(target, source, source2, move) {
        if (this.validTarget(this.effectState.target, source, move.target)) {
          this.debug("Spotlight redirected target of move");
          return this.effectState.target;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spd: 1 } },
    contestType: "Cute"
  },
  springtidestorm: {
    num: 831,
    accuracy: 80,
    basePower: 100,
    category: "Special",
    name: "Springtide Storm",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, wind: 1 },
    secondary: {
      chance: 30,
      boosts: {
        atk: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Fairy"
  },
  stealthrock: {
    num: 446,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Stealth Rock",
    pp: 20,
    priority: 0,
    flags: { reflectable: 1, metronome: 1, mustpressure: 1 },
    sideCondition: "stealthrock",
    condition: {
      // this is a side condition
      onSideStart(side) {
        this.add("-sidestart", side, "move: Stealth Rock");
      },
      onEntryHazard(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);
      }
    },
    secondary: null,
    target: "foeSide",
    type: "Rock",
    zMove: { boost: { def: 1 } },
    contestType: "Cool"
  },
  steameruption: {
    num: 592,
    accuracy: 95,
    basePower: 110,
    category: "Special",
    name: "Steam Eruption",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, defrost: 1 },
    thawsTarget: true,
    secondary: {
      chance: 30,
      status: "brn"
    },
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  steamroller: {
    num: 537,
    accuracy: 100,
    basePower: 65,
    category: "Physical",
    isNonstandard: "Past",
    name: "Steamroller",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Bug",
    contestType: "Tough"
  },
  steelbeam: {
    num: 796,
    accuracy: 95,
    basePower: 140,
    category: "Special",
    name: "Steel Beam",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    mindBlownRecoil: true,
    onAfterMove(pokemon, target, move) {
      if (move.mindBlownRecoil && !move.multihit) {
        const hpBeforeRecoil = pokemon.hp;
        this.damage(Math.round(pokemon.maxhp / 2), pokemon, pokemon, this.dex.conditions.get("Steel Beam"), true);
        if (pokemon.hp <= pokemon.maxhp / 2 && hpBeforeRecoil > pokemon.maxhp / 2) {
          this.runEvent("EmergencyExit", pokemon, pokemon);
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Steel"
  },
  steelroller: {
    num: 798,
    accuracy: 100,
    basePower: 130,
    category: "Physical",
    name: "Steel Roller",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onTry() {
      return !this.field.isTerrain("");
    },
    onHit() {
      this.field.clearTerrain();
    },
    onAfterSubDamage() {
      this.field.clearTerrain();
    },
    secondary: null,
    target: "normal",
    type: "Steel"
  },
  steelwing: {
    num: 211,
    accuracy: 90,
    basePower: 70,
    category: "Physical",
    name: "Steel Wing",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      self: {
        boosts: {
          def: 1
        }
      }
    },
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  stickyweb: {
    num: 564,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Sticky Web",
    pp: 20,
    priority: 0,
    flags: { reflectable: 1, metronome: 1 },
    sideCondition: "stickyweb",
    condition: {
      onSideStart(side) {
        this.add("-sidestart", side, "move: Sticky Web");
      },
      onEntryHazard(pokemon) {
        if (!pokemon.isGrounded() || 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"));
      }
    },
    secondary: null,
    target: "foeSide",
    type: "Bug",
    zMove: { boost: { spe: 1 } },
    contestType: "Tough"
  },
  stockpile: {
    num: 254,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Stockpile",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onTry(source) {
      if (source.volatiles["stockpile"] && source.volatiles["stockpile"].layers >= 3)
        return false;
    },
    volatileStatus: "stockpile",
    condition: {
      noCopy: true,
      onStart(target) {
        this.effectState.layers = 1;
        this.effectState.def = 0;
        this.effectState.spd = 0;
        this.add("-start", target, "stockpile" + this.effectState.layers);
        const [curDef, curSpD] = [target.boosts.def, target.boosts.spd];
        this.boost({ def: 1, spd: 1 }, target, target);
        if (curDef !== target.boosts.def)
          this.effectState.def--;
        if (curSpD !== target.boosts.spd)
          this.effectState.spd--;
      },
      onRestart(target) {
        if (this.effectState.layers >= 3)
          return false;
        this.effectState.layers++;
        this.add("-start", target, "stockpile" + this.effectState.layers);
        const curDef = target.boosts.def;
        const curSpD = target.boosts.spd;
        this.boost({ def: 1, spd: 1 }, target, target);
        if (curDef !== target.boosts.def)
          this.effectState.def--;
        if (curSpD !== target.boosts.spd)
          this.effectState.spd--;
      },
      onEnd(target) {
        if (this.effectState.def || this.effectState.spd) {
          const boosts = {};
          if (this.effectState.def)
            boosts.def = this.effectState.def;
          if (this.effectState.spd)
            boosts.spd = this.effectState.spd;
          this.boost(boosts, target, target);
        }
        this.add("-end", target, "Stockpile");
        if (this.effectState.def !== this.effectState.layers * -1 || this.effectState.spd !== this.effectState.layers * -1) {
          this.hint("In Gen 7, Stockpile keeps track of how many times it successfully altered each stat individually.");
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "heal" },
    contestType: "Tough"
  },
  stokedsparksurfer: {
    num: 700,
    accuracy: true,
    basePower: 175,
    category: "Special",
    isNonstandard: "Past",
    name: "Stoked Sparksurfer",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "aloraichiumz",
    secondary: {
      chance: 100,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  stomp: {
    num: 23,
    accuracy: 100,
    basePower: 65,
    category: "Physical",
    name: "Stomp",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  stompingtantrum: {
    num: 707,
    accuracy: 100,
    basePower: 75,
    basePowerCallback(pokemon, target, move) {
      if (pokemon.moveLastTurnResult === false) {
        this.debug("doubling Stomping Tantrum BP due to previous move failure");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    name: "Stomping Tantrum",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Ground",
    contestType: "Tough"
  },
  stoneaxe: {
    num: 830,
    accuracy: 90,
    basePower: 65,
    category: "Physical",
    name: "Stone Axe",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    onAfterHit(target, source, move) {
      if (!move.hasSheerForce && source.hp) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("stealthrock");
        }
      }
    },
    onAfterSubDamage(damage, target, source, move) {
      if (!move.hasSheerForce && source.hp) {
        for (const side of source.side.foeSidesWithConditions()) {
          side.addSideCondition("stealthrock");
        }
      }
    },
    secondary: {},
    // Sheer Force-boosted
    target: "normal",
    type: "Rock"
  },
  stoneedge: {
    num: 444,
    accuracy: 80,
    basePower: 100,
    category: "Physical",
    name: "Stone Edge",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondary: null,
    target: "normal",
    type: "Rock",
    contestType: "Tough"
  },
  storedpower: {
    num: 500,
    accuracy: 100,
    basePower: 20,
    basePowerCallback(pokemon, target, move) {
      const bp = move.basePower + 20 * pokemon.positiveBoosts();
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Special",
    name: "Stored Power",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Clever"
  },
  stormthrow: {
    num: 480,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    isNonstandard: "Past",
    name: "Storm Throw",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    willCrit: true,
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  strangesteam: {
    num: 790,
    accuracy: 95,
    basePower: 90,
    category: "Special",
    name: "Strange Steam",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "confusion"
    },
    target: "normal",
    type: "Fairy"
  },
  strength: {
    num: 70,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Strength",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  strengthsap: {
    num: 668,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Strength Sap",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, heal: 1, metronome: 1 },
    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: "Grass",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  stringshot: {
    num: 81,
    accuracy: 95,
    basePower: 0,
    category: "Status",
    name: "String Shot",
    pp: 40,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      spe: -2
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Bug",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  struggle: {
    num: 165,
    accuracy: true,
    basePower: 50,
    category: "Physical",
    name: "Struggle",
    pp: 1,
    noPPBoosts: true,
    priority: 0,
    flags: {
      contact: 1,
      protect: 1,
      failencore: 1,
      failmefirst: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    noSketch: true,
    onModifyMove(move, pokemon, target) {
      move.type = "???";
      this.add("-activate", pokemon, "move: Struggle");
    },
    struggleRecoil: true,
    secondary: null,
    target: "randomNormal",
    type: "Normal",
    contestType: "Tough"
  },
  strugglebug: {
    num: 522,
    accuracy: 100,
    basePower: 50,
    category: "Special",
    name: "Struggle Bug",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        spa: -1
      }
    },
    target: "allAdjacentFoes",
    type: "Bug",
    contestType: "Cute"
  },
  stuffcheeks: {
    num: 747,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Stuff Cheeks",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onDisableMove(pokemon) {
      if (!pokemon.getItem().isBerry)
        pokemon.disableMove("stuffcheeks");
    },
    onTry(source) {
      return source.getItem().isBerry;
    },
    onHit(pokemon) {
      if (!this.boost({ def: 2 }))
        return null;
      pokemon.eatItem(true);
    },
    secondary: null,
    target: "self",
    type: "Normal"
  },
  stunspore: {
    num: 78,
    accuracy: 75,
    basePower: 0,
    category: "Status",
    name: "Stun Spore",
    pp: 30,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1, powder: 1 },
    status: "par",
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  submission: {
    num: 66,
    accuracy: 80,
    basePower: 80,
    category: "Physical",
    isNonstandard: "Past",
    name: "Submission",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [1, 4],
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  substitute: {
    num: 164,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Substitute",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, nonsky: 1, metronome: 1 },
    volatileStatus: "substitute",
    onTryHit(source) {
      if (source.volatiles["substitute"]) {
        this.add("-fail", source, "move: Substitute");
        return this.NOT_FAIL;
      }
      if (source.hp <= source.maxhp / 4 || source.maxhp === 1) {
        this.add("-fail", source, "move: Substitute", "[weak]");
        return this.NOT_FAIL;
      }
    },
    onHit(target) {
      this.directDamage(target.maxhp / 4);
    },
    condition: {
      onStart(target, source, effect) {
        if (effect?.id === "shedtail") {
          this.add("-start", target, "Substitute", "[from] move: Shed Tail");
        } else {
          this.add("-start", target, "Substitute");
        }
        this.effectState.hp = Math.floor(target.maxhp / 4);
        if (target.volatiles["partiallytrapped"]) {
          this.add("-end", target, target.volatiles["partiallytrapped"].sourceEffect, "[partiallytrapped]", "[silent]");
          delete target.volatiles["partiallytrapped"];
        }
      },
      onTryPrimaryHitPriority: -1,
      onTryPrimaryHit(target, source, move) {
        if (target === source || move.flags["bypasssub"] || move.infiltrates) {
          return;
        }
        let damage = this.actions.getDamage(source, target, move);
        if (!damage && damage !== 0) {
          this.add("-fail", source);
          this.attrLastMove("[still]");
          return null;
        }
        damage = this.runEvent("SubDamage", target, source, move, damage);
        if (!damage) {
          return damage;
        }
        if (damage > target.volatiles["substitute"].hp) {
          damage = target.volatiles["substitute"].hp;
        }
        target.volatiles["substitute"].hp -= damage;
        source.lastDamage = damage;
        if (target.volatiles["substitute"].hp <= 0) {
          if (move.ohko)
            this.add("-ohko");
          target.removeVolatile("substitute");
        } else {
          this.add("-activate", target, "move: Substitute", "[damage]");
        }
        if (move.recoil || move.id === "chloroblast") {
          this.damage(this.actions.calcRecoilDamage(damage, move, source), source, target, "recoil");
        }
        if (move.drain) {
          this.heal(Math.ceil(damage * move.drain[0] / move.drain[1]), source, target, "drain");
        }
        this.singleEvent("AfterSubDamage", move, null, target, source, move, damage);
        this.runEvent("AfterSubDamage", target, source, move, damage);
        return this.HIT_SUBSTITUTE;
      },
      onEnd(target) {
        this.add("-end", target, "Substitute");
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  subzeroslammer: {
    num: 650,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Subzero Slammer",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "iciumz",
    secondary: null,
    target: "normal",
    type: "Ice",
    contestType: "Cool"
  },
  suckerpunch: {
    num: 389,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Sucker Punch",
    pp: 5,
    priority: 1,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onTry(source, target) {
      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"]) {
        return false;
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  sunnyday: {
    num: 241,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Sunny Day",
    pp: 5,
    priority: 0,
    flags: { metronome: 1 },
    weather: "sunnyday",
    secondary: null,
    target: "all",
    type: "Fire",
    zMove: { boost: { spe: 1 } },
    contestType: "Beautiful"
  },
  sunsteelstrike: {
    num: 713,
    accuracy: 100,
    basePower: 100,
    category: "Physical",
    name: "Sunsteel Strike",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    ignoreAbility: true,
    secondary: null,
    target: "normal",
    type: "Steel",
    contestType: "Cool"
  },
  supercellslam: {
    num: 916,
    accuracy: 95,
    basePower: 100,
    category: "Physical",
    name: "Supercell Slam",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    hasCrashDamage: true,
    onMoveFail(target, source, move) {
      this.damage(source.baseMaxhp / 2, source, source, this.dex.conditions.get("Supercell Slam"));
    },
    secondary: null,
    target: "normal",
    type: "Electric"
  },
  superfang: {
    num: 162,
    accuracy: 90,
    basePower: 0,
    damageCallback(pokemon, target) {
      return this.clampIntRange(target.getUndynamaxedHP() / 2, 1);
    },
    category: "Physical",
    name: "Super Fang",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  superpower: {
    num: 276,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Superpower",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    self: {
      boosts: {
        atk: -1,
        def: -1
      }
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  supersonic: {
    num: 48,
    accuracy: 55,
    basePower: 0,
    category: "Status",
    name: "Supersonic",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    volatileStatus: "confusion",
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  supersonicskystrike: {
    num: 626,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Supersonic Skystrike",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "flyiniumz",
    secondary: null,
    target: "normal",
    type: "Flying",
    contestType: "Cool"
  },
  surf: {
    num: 57,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Surf",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacent",
    type: "Water",
    contestType: "Beautiful"
  },
  surgingstrikes: {
    num: 818,
    accuracy: 100,
    basePower: 25,
    category: "Physical",
    name: "Surging Strikes",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1 },
    willCrit: true,
    multihit: 3,
    secondary: null,
    target: "normal",
    type: "Water",
    zMove: { basePower: 140 },
    maxMove: { basePower: 130 }
  },
  swagger: {
    num: 207,
    accuracy: 85,
    basePower: 0,
    category: "Status",
    name: "Swagger",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    volatileStatus: "confusion",
    boosts: {
      atk: 2
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Cute"
  },
  swallow: {
    num: 256,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Swallow",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    onTry(source) {
      return !!source.volatiles["stockpile"];
    },
    onHit(pokemon) {
      const healAmount = [0.25, 0.5, 1];
      const success = !!this.heal(this.modify(pokemon.maxhp, healAmount[pokemon.volatiles["stockpile"].layers - 1]));
      if (!success)
        this.add("-fail", pokemon, "heal");
      pokemon.removeVolatile("stockpile");
      return success || this.NOT_FAIL;
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Tough"
  },
  sweetkiss: {
    num: 186,
    accuracy: 75,
    basePower: 0,
    category: "Status",
    name: "Sweet Kiss",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    volatileStatus: "confusion",
    secondary: null,
    target: "normal",
    type: "Fairy",
    zMove: { boost: { spa: 1 } },
    contestType: "Cute"
  },
  sweetscent: {
    num: 230,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Sweet Scent",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      evasion: -2
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Normal",
    zMove: { boost: { accuracy: 1 } },
    contestType: "Cute"
  },
  swift: {
    num: 129,
    accuracy: true,
    basePower: 60,
    category: "Special",
    name: "Swift",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Normal",
    contestType: "Cool"
  },
  switcheroo: {
    num: 415,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Switcheroo",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, allyanim: 1, noassist: 1, failcopycat: 1 },
    onTryImmunity(target) {
      return !target.hasAbility("stickyhold");
    },
    onHit(target, source, move) {
      const yourItem = target.takeItem(source);
      const myItem = source.takeItem();
      if (target.item || source.item || !yourItem && !myItem) {
        if (yourItem)
          target.item = yourItem.id;
        if (myItem)
          source.item = myItem.id;
        return false;
      }
      if (myItem && !this.singleEvent("TakeItem", myItem, source.itemState, target, source, move, myItem) || yourItem && !this.singleEvent("TakeItem", yourItem, target.itemState, source, target, move, yourItem)) {
        if (yourItem)
          target.item = yourItem.id;
        if (myItem)
          source.item = myItem.id;
        return false;
      }
      this.add("-activate", source, "move: Trick", "[of] " + target);
      if (myItem) {
        target.setItem(myItem);
        this.add("-item", target, myItem, "[from] move: Switcheroo");
      } else {
        this.add("-enditem", target, yourItem, "[silent]", "[from] move: Switcheroo");
      }
      if (yourItem) {
        source.setItem(yourItem);
        this.add("-item", source, yourItem, "[from] move: Switcheroo");
      } else {
        this.add("-enditem", source, myItem, "[silent]", "[from] move: Switcheroo");
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { boost: { spe: 2 } },
    contestType: "Clever"
  },
  swordsdance: {
    num: 14,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Swords Dance",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, dance: 1, metronome: 1 },
    boosts: {
      atk: 2
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  synchronoise: {
    num: 485,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    isNonstandard: "Past",
    name: "Synchronoise",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onTryImmunity(target, source) {
      return target.hasType(source.getTypes());
    },
    secondary: null,
    target: "allAdjacent",
    type: "Psychic",
    contestType: "Clever"
  },
  synthesis: {
    num: 235,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Synthesis",
    pp: 5,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    onHit(pokemon) {
      let factor = 0.5;
      switch (pokemon.effectiveWeather()) {
        case "sunnyday":
        case "desolateland":
          factor = 0.667;
          break;
        case "raindance":
        case "primordialsea":
        case "sandstorm":
        case "hail":
        case "snow":
          factor = 0.25;
          break;
      }
      const success = !!this.heal(this.modify(pokemon.maxhp, factor));
      if (!success) {
        this.add("-fail", pokemon, "heal");
        return this.NOT_FAIL;
      }
      return success;
    },
    secondary: null,
    target: "self",
    type: "Grass",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Clever"
  },
  syrupbomb: {
    num: 903,
    accuracy: 85,
    basePower: 60,
    category: "Special",
    name: "Syrup Bomb",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    condition: {
      noCopy: true,
      duration: 4,
      onStart(pokemon) {
        this.add("-start", pokemon, "Syrup Bomb");
      },
      onResidualOrder: 14,
      onResidual() {
        this.boost({ spe: -1 });
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Syrup Bomb", "[silent]");
      }
    },
    secondary: {
      chance: 100,
      volatileStatus: "syrupbomb"
    },
    target: "normal",
    type: "Grass"
  },
  tachyoncutter: {
    num: 911,
    accuracy: true,
    basePower: 50,
    category: "Special",
    name: "Tachyon Cutter",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    multihit: 2,
    secondary: null,
    target: "normal",
    type: "Steel",
    zMove: { basePower: 180 },
    maxMove: { basePower: 140 },
    contestType: "Clever"
  },
  tackle: {
    num: 33,
    accuracy: 100,
    basePower: 40,
    category: "Physical",
    name: "Tackle",
    pp: 35,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  tailglow: {
    num: 294,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Tail Glow",
    pp: 20,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      spa: 3
    },
    secondary: null,
    target: "self",
    type: "Bug",
    zMove: { effect: "clearnegativeboost" },
    contestType: "Beautiful"
  },
  tailslap: {
    num: 541,
    accuracy: 85,
    basePower: 25,
    category: "Physical",
    name: "Tail Slap",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 140 },
    maxMove: { basePower: 130 },
    contestType: "Cute"
  },
  tailwhip: {
    num: 39,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Tail Whip",
    pp: 30,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      def: -1
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Normal",
    zMove: { boost: { atk: 1 } },
    contestType: "Cute"
  },
  tailwind: {
    num: 366,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Tailwind",
    pp: 15,
    priority: 0,
    flags: { snatch: 1, metronome: 1, wind: 1 },
    sideCondition: "tailwind",
    condition: {
      duration: 4,
      durationCallback(target, source, effect) {
        if (source?.hasAbility("persistent")) {
          this.add("-activate", source, "ability: Persistent", "[move] Tailwind");
          return 6;
        }
        return 4;
      },
      onSideStart(side, source) {
        if (source?.hasAbility("persistent")) {
          this.add("-sidestart", side, "move: Tailwind", "[persistent]");
        } else {
          this.add("-sidestart", side, "move: Tailwind");
        }
      },
      onModifySpe(spe, pokemon) {
        return this.chainModify(2);
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 5,
      onSideEnd(side) {
        this.add("-sideend", side, "move: Tailwind");
      }
    },
    secondary: null,
    target: "allySide",
    type: "Flying",
    zMove: { effect: "crit2" },
    contestType: "Cool"
  },
  takedown: {
    num: 36,
    accuracy: 85,
    basePower: 90,
    category: "Physical",
    name: "Take Down",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [1, 4],
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  takeheart: {
    num: 850,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Take Heart",
    pp: 15,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    onHit(pokemon) {
      const success = !!this.boost({ spa: 1, spd: 1 });
      return pokemon.cureStatus() || success;
    },
    secondary: null,
    target: "self",
    type: "Psychic"
  },
  tarshot: {
    num: 749,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Tar Shot",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    volatileStatus: "tarshot",
    condition: {
      onStart(pokemon) {
        if (pokemon.terastallized)
          return false;
        this.add("-start", pokemon, "Tar Shot");
      },
      onEffectivenessPriority: -2,
      onEffectiveness(typeMod, target, type, move) {
        if (move.type !== "Fire")
          return;
        if (!target)
          return;
        if (type !== target.getTypes()[0])
          return;
        return typeMod + 1;
      }
    },
    boosts: {
      spe: -1
    },
    secondary: null,
    target: "normal",
    type: "Rock"
  },
  taunt: {
    num: 269,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Taunt",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    volatileStatus: "taunt",
    condition: {
      duration: 3,
      onStart(target) {
        if (target.activeTurns && !this.queue.willMove(target)) {
          this.effectState.duration++;
        }
        this.add("-start", target, "move: Taunt");
      },
      onResidualOrder: 15,
      onEnd(target) {
        this.add("-end", target, "move: Taunt");
      },
      onDisableMove(pokemon) {
        for (const moveSlot of pokemon.moveSlots) {
          const move = this.dex.moves.get(moveSlot.id);
          if (move.category === "Status" && move.id !== "mefirst") {
            pokemon.disableMove(moveSlot.id);
          }
        }
      },
      onBeforeMovePriority: 5,
      onBeforeMove(attacker, defender, move) {
        if (!move.isZ && !move.isMax && move.category === "Status" && move.id !== "mefirst") {
          this.add("cant", attacker, "move: Taunt", move);
          return false;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { boost: { atk: 1 } },
    contestType: "Clever"
  },
  tearfullook: {
    num: 715,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Tearful Look",
    pp: 20,
    priority: 0,
    flags: { reflectable: 1, mirror: 1, metronome: 1 },
    boosts: {
      atk: -1,
      spa: -1
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  teatime: {
    num: 752,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Teatime",
    pp: 10,
    priority: 0,
    flags: { bypasssub: 1, metronome: 1 },
    onHitField(target, source, move) {
      const targets = [];
      for (const pokemon of this.getAllActive()) {
        if (this.runEvent("Invulnerability", pokemon, source, move) === false) {
          this.add("-miss", source, pokemon);
        } else if (this.runEvent("TryHit", pokemon, source, move) && pokemon.getItem().isBerry) {
          targets.push(pokemon);
        }
      }
      this.add("-fieldactivate", "move: Teatime");
      if (!targets.length) {
        this.add("-fail", source, "move: Teatime");
        this.attrLastMove("[still]");
        return this.NOT_FAIL;
      }
      for (const pokemon of targets) {
        pokemon.eatItem(true);
      }
    },
    secondary: null,
    target: "all",
    type: "Normal"
  },
  technoblast: {
    num: 546,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    isNonstandard: "Past",
    name: "Techno Blast",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    onModifyType(move, pokemon) {
      if (pokemon.ignoringItem())
        return;
      move.type = this.runEvent("Drive", pokemon, null, move, "Normal");
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cool"
  },
  tectonicrage: {
    num: 630,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Tectonic Rage",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "groundiumz",
    secondary: null,
    target: "normal",
    type: "Ground",
    contestType: "Cool"
  },
  teeterdance: {
    num: 298,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Teeter Dance",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, dance: 1, metronome: 1 },
    volatileStatus: "confusion",
    secondary: null,
    target: "allAdjacent",
    type: "Normal",
    zMove: { boost: { spa: 1 } },
    contestType: "Cute"
  },
  telekinesis: {
    num: 477,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Telekinesis",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, gravity: 1, allyanim: 1, metronome: 1 },
    volatileStatus: "telekinesis",
    onTry(source, target, move) {
      if (this.field.getPseudoWeather("Gravity")) {
        this.attrLastMove("[still]");
        this.add("cant", source, "move: Gravity", move);
        return null;
      }
    },
    condition: {
      duration: 3,
      onStart(target) {
        if (["Diglett", "Dugtrio", "Palossand", "Sandygast"].includes(target.baseSpecies.baseSpecies) || target.baseSpecies.name === "Gengar-Mega") {
          this.add("-immune", target);
          return null;
        }
        if (target.volatiles["smackdown"] || target.volatiles["ingrain"])
          return false;
        this.add("-start", target, "Telekinesis");
      },
      onAccuracyPriority: -1,
      onAccuracy(accuracy, target, source, move) {
        if (move && !move.ohko)
          return true;
      },
      onImmunity(type) {
        if (type === "Ground")
          return false;
      },
      onUpdate(pokemon) {
        if (pokemon.baseSpecies.name === "Gengar-Mega") {
          delete pokemon.volatiles["telekinesis"];
          this.add("-end", pokemon, "Telekinesis", "[silent]");
        }
      },
      onResidualOrder: 19,
      onEnd(target) {
        this.add("-end", target, "Telekinesis");
      }
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spa: 1 } },
    contestType: "Clever"
  },
  teleport: {
    num: 100,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Teleport",
    pp: 20,
    priority: -6,
    flags: { metronome: 1 },
    onTry(source) {
      return !!this.canSwitch(source.side);
    },
    selfSwitch: true,
    secondary: null,
    target: "self",
    type: "Psychic",
    zMove: { effect: "heal" },
    contestType: "Cool"
  },
  temperflare: {
    num: 915,
    accuracy: 100,
    basePower: 75,
    basePowerCallback(pokemon, target, move) {
      if (pokemon.moveLastTurnResult === false) {
        this.debug("doubling Temper Flare BP due to previous move failure");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    name: "Temper Flare",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Fire"
  },
  terablast: {
    num: 851,
    accuracy: 100,
    basePower: 80,
    basePowerCallback(pokemon, target, move) {
      if (pokemon.terastallized === "Stellar") {
        return 100;
      }
      return move.basePower;
    },
    category: "Special",
    name: "Tera Blast",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, mustpressure: 1 },
    onPrepareHit(target, source, move) {
      if (source.terastallized) {
        this.attrLastMove("[anim] Tera Blast " + source.teraType);
      }
    },
    onModifyType(move, pokemon, target) {
      if (pokemon.terastallized) {
        move.type = pokemon.teraType;
      }
    },
    onModifyMove(move, pokemon) {
      if (pokemon.terastallized && pokemon.getStat("atk", false, true) > pokemon.getStat("spa", false, true)) {
        move.category = "Physical";
      }
      if (pokemon.terastallized === "Stellar") {
        move.self = { boosts: { atk: -1, spa: -1 } };
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal"
  },
  terastarstorm: {
    num: 906,
    accuracy: 100,
    basePower: 120,
    category: "Special",
    name: "Tera Starstorm",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, noassist: 1, failcopycat: 1, failmimic: 1 },
    onModifyType(move, pokemon) {
      if (pokemon.species.name === "Terapagos-Stellar") {
        move.type = "Stellar";
        if (pokemon.terastallized && pokemon.getStat("atk", false, true) > pokemon.getStat("spa", false, true)) {
          move.category = "Physical";
        }
      }
    },
    onModifyMove(move, pokemon) {
      if (pokemon.species.name === "Terapagos-Stellar") {
        move.target = "allAdjacentFoes";
      }
    },
    noSketch: true,
    secondary: null,
    target: "normal",
    type: "Normal"
  },
  terrainpulse: {
    num: 805,
    accuracy: 100,
    basePower: 50,
    category: "Special",
    name: "Terrain Pulse",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, pulse: 1 },
    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;
      }
    },
    onModifyMove(move, pokemon) {
      if (this.field.terrain && pokemon.isGrounded()) {
        move.basePower *= 2;
        this.debug("BP doubled in Terrain");
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 }
  },
  thief: {
    num: 168,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Thief",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, failmefirst: 1, noassist: 1, failcopycat: 1 },
    onAfterHit(target, source, move) {
      if (source.item || source.volatiles["gem"]) {
        return;
      }
      const yourItem = target.takeItem(source);
      if (!yourItem) {
        return;
      }
      if (!this.singleEvent("TakeItem", yourItem, target.itemState, source, target, move, yourItem) || !source.setItem(yourItem)) {
        target.item = yourItem.id;
        return;
      }
      this.add("-enditem", target, yourItem, "[silent]", "[from] move: Thief", "[of] " + source);
      this.add("-item", source, yourItem, "[from] move: Thief", "[of] " + target);
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    contestType: "Tough"
  },
  thousandarrows: {
    num: 614,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    isNonstandard: "Past",
    name: "Thousand Arrows",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1 },
    onEffectiveness(typeMod, target, type, move) {
      if (move.type !== "Ground")
        return;
      if (!target)
        return;
      if (!target.runImmunity("Ground")) {
        if (target.hasType("Flying"))
          return 0;
      }
    },
    volatileStatus: "smackdown",
    ignoreImmunity: { "Ground": true },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Ground",
    zMove: { basePower: 180 },
    contestType: "Beautiful"
  },
  thousandwaves: {
    num: 615,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    isNonstandard: "Past",
    name: "Thousand Waves",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1 },
    onHit(target, source, move) {
      if (source.isActive)
        target.addVolatile("trapped", source, move, "trapper");
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Ground",
    contestType: "Tough"
  },
  thrash: {
    num: 37,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Thrash",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, failinstruct: 1 },
    self: {
      volatileStatus: "lockedmove"
    },
    onAfterMove(pokemon) {
      if (pokemon.volatiles["lockedmove"] && pokemon.volatiles["lockedmove"].duration === 1) {
        pokemon.removeVolatile("lockedmove");
      }
    },
    secondary: null,
    target: "randomNormal",
    type: "Normal",
    contestType: "Tough"
  },
  throatchop: {
    num: 675,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Throat Chop",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    condition: {
      duration: 2,
      onStart(target) {
        this.add("-start", target, "Throat Chop", "[silent]");
      },
      onDisableMove(pokemon) {
        for (const moveSlot of pokemon.moveSlots) {
          if (this.dex.moves.get(moveSlot.id).flags["sound"]) {
            pokemon.disableMove(moveSlot.id);
          }
        }
      },
      onBeforeMovePriority: 6,
      onBeforeMove(pokemon, target, move) {
        if (!move.isZ && !move.isMax && move.flags["sound"]) {
          this.add("cant", pokemon, "move: Throat Chop");
          return false;
        }
      },
      onModifyMove(move, pokemon, target) {
        if (!move.isZ && !move.isMax && move.flags["sound"]) {
          this.add("cant", pokemon, "move: Throat Chop");
          return false;
        }
      },
      onResidualOrder: 22,
      onEnd(target) {
        this.add("-end", target, "Throat Chop", "[silent]");
      }
    },
    secondary: {
      chance: 100,
      onHit(target) {
        target.addVolatile("throatchop");
      }
    },
    target: "normal",
    type: "Dark",
    contestType: "Clever"
  },
  thunder: {
    num: 87,
    accuracy: 70,
    basePower: 110,
    category: "Special",
    name: "Thunder",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onModifyMove(move, pokemon, target) {
      switch (target?.effectiveWeather()) {
        case "raindance":
        case "primordialsea":
          move.accuracy = true;
          break;
        case "sunnyday":
        case "desolateland":
          move.accuracy = 50;
          break;
      }
    },
    secondary: {
      chance: 30,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  thunderbolt: {
    num: 85,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Thunderbolt",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  thundercage: {
    num: 819,
    accuracy: 90,
    basePower: 80,
    category: "Special",
    name: "Thunder Cage",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Electric"
  },
  thunderclap: {
    num: 909,
    accuracy: 100,
    basePower: 70,
    category: "Special",
    name: "Thunderclap",
    pp: 5,
    priority: 1,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onTry(source, target) {
      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"]) {
        return false;
      }
    },
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Clever"
  },
  thunderfang: {
    num: 422,
    accuracy: 95,
    basePower: 65,
    category: "Physical",
    name: "Thunder Fang",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, bite: 1 },
    secondaries: [
      {
        chance: 10,
        status: "par"
      },
      {
        chance: 10,
        volatileStatus: "flinch"
      }
    ],
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  thunderouskick: {
    num: 823,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Thunderous Kick",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      boosts: {
        def: -1
      }
    },
    target: "normal",
    type: "Fighting"
  },
  thunderpunch: {
    num: 9,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Thunder Punch",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  thundershock: {
    num: 84,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Thunder Shock",
    pp: 30,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 10,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  thunderwave: {
    num: 86,
    accuracy: 90,
    basePower: 0,
    category: "Status",
    name: "Thunder Wave",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    status: "par",
    ignoreImmunity: false,
    secondary: null,
    target: "normal",
    type: "Electric",
    zMove: { boost: { spd: 1 } },
    contestType: "Cool"
  },
  tickle: {
    num: 321,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Tickle",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    boosts: {
      atk: -1,
      def: -1
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  tidyup: {
    num: 882,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Tidy Up",
    pp: 10,
    priority: 0,
    flags: {},
    onHit(pokemon) {
      let success = false;
      for (const active of this.getAllActive()) {
        if (active.removeVolatile("substitute"))
          success = true;
      }
      const removeAll = ["spikes", "toxicspikes", "stealthrock", "stickyweb", "gmaxsteelsurge"];
      const sides = [pokemon.side, ...pokemon.side.foeSidesWithConditions()];
      for (const side of sides) {
        for (const sideCondition of removeAll) {
          if (side.removeSideCondition(sideCondition)) {
            this.add("-sideend", side, this.dex.conditions.get(sideCondition).name);
            success = true;
          }
        }
      }
      if (success)
        this.add("-activate", pokemon, "move: Tidy Up");
      return !!this.boost({ atk: 1, spe: 1 }, pokemon, pokemon, null, false, true) || success;
    },
    secondary: null,
    target: "self",
    type: "Normal"
  },
  topsyturvy: {
    num: 576,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Topsy-Turvy",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onHit(target) {
      let success = false;
      let i;
      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: Topsy-Turvy");
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { boost: { atk: 1 } },
    contestType: "Clever"
  },
  torchsong: {
    num: 871,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Torch Song",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          spa: 1
        }
      }
    },
    target: "normal",
    type: "Fire",
    contestType: "Beautiful"
  },
  torment: {
    num: 259,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Torment",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, bypasssub: 1, metronome: 1 },
    volatileStatus: "torment",
    condition: {
      noCopy: true,
      onStart(pokemon, source, effect) {
        if (pokemon.volatiles["dynamax"]) {
          delete pokemon.volatiles["torment"];
          return false;
        }
        if (effect?.id === "gmaxmeltdown")
          this.effectState.duration = 3;
        this.add("-start", pokemon, "Torment");
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Torment");
      },
      onDisableMove(pokemon) {
        if (pokemon.lastMove && pokemon.lastMove.id !== "struggle")
          pokemon.disableMove(pokemon.lastMove.id);
      }
    },
    secondary: null,
    target: "normal",
    type: "Dark",
    zMove: { boost: { def: 1 } },
    contestType: "Tough"
  },
  toxic: {
    num: 92,
    accuracy: 90,
    basePower: 0,
    category: "Status",
    name: "Toxic",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    // No Guard-like effect for Poison-type users implemented in Scripts#tryMoveHit
    status: "tox",
    secondary: null,
    target: "normal",
    type: "Poison",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  toxicspikes: {
    num: 390,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Toxic Spikes",
    pp: 20,
    priority: 0,
    flags: { reflectable: 1, nonsky: 1, metronome: 1, mustpressure: 1 },
    sideCondition: "toxicspikes",
    condition: {
      // this is a side condition
      onSideStart(side) {
        this.add("-sidestart", side, "move: Toxic Spikes");
        this.effectState.layers = 1;
      },
      onSideRestart(side) {
        if (this.effectState.layers >= 2)
          return false;
        this.add("-sidestart", side, "move: Toxic Spikes");
        this.effectState.layers++;
      },
      onEntryHazard(pokemon) {
        if (!pokemon.isGrounded())
          return;
        if (pokemon.hasType("Poison")) {
          this.add("-sideend", pokemon.side, "move: Toxic Spikes", "[of] " + pokemon);
          pokemon.side.removeSideCondition("toxicspikes");
        } else if (pokemon.hasType("Steel") || pokemon.hasItem("heavydutyboots")) {
          return;
        } else if (this.effectState.layers >= 2) {
          pokemon.trySetStatus("tox", pokemon.side.foe.active[0]);
        } else {
          pokemon.trySetStatus("psn", pokemon.side.foe.active[0]);
        }
      }
    },
    secondary: null,
    target: "foeSide",
    type: "Poison",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  toxicthread: {
    num: 672,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Toxic Thread",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    status: "psn",
    boosts: {
      spe: -1
    },
    secondary: null,
    target: "normal",
    type: "Poison",
    zMove: { boost: { spe: 1 } },
    contestType: "Tough"
  },
  trailblaze: {
    num: 885,
    accuracy: 100,
    basePower: 50,
    category: "Physical",
    name: "Trailblaze",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          spe: 1
        }
      }
    },
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  transform: {
    num: 144,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Transform",
    pp: 10,
    priority: 0,
    flags: { allyanim: 1, failencore: 1, noassist: 1, failcopycat: 1, failmimic: 1, failinstruct: 1 },
    onHit(target, pokemon) {
      if (!pokemon.transformInto(target)) {
        return false;
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { effect: "heal" },
    contestType: "Clever"
  },
  triattack: {
    num: 161,
    accuracy: 100,
    basePower: 80,
    category: "Special",
    name: "Tri Attack",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      onHit(target, source) {
        const result = this.random(3);
        if (result === 0) {
          target.trySetStatus("brn", source);
        } else if (result === 1) {
          target.trySetStatus("par", source);
        } else {
          target.trySetStatus("frz", source);
        }
      }
    },
    target: "normal",
    type: "Normal",
    contestType: "Beautiful"
  },
  trick: {
    num: 271,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Trick",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, allyanim: 1, noassist: 1, failcopycat: 1 },
    onTryImmunity(target) {
      return !target.hasAbility("stickyhold");
    },
    onHit(target, source, move) {
      const yourItem = target.takeItem(source);
      const myItem = source.takeItem();
      if (target.item || source.item || !yourItem && !myItem) {
        if (yourItem)
          target.item = yourItem.id;
        if (myItem)
          source.item = myItem.id;
        return false;
      }
      if (myItem && !this.singleEvent("TakeItem", myItem, source.itemState, target, source, move, myItem) || yourItem && !this.singleEvent("TakeItem", yourItem, target.itemState, source, target, move, yourItem)) {
        if (yourItem)
          target.item = yourItem.id;
        if (myItem)
          source.item = myItem.id;
        return false;
      }
      this.add("-activate", source, "move: Trick", "[of] " + target);
      if (myItem) {
        target.setItem(myItem);
        this.add("-item", target, myItem, "[from] move: Trick");
      } else {
        this.add("-enditem", target, yourItem, "[silent]", "[from] move: Trick");
      }
      if (yourItem) {
        source.setItem(yourItem);
        this.add("-item", source, yourItem, "[from] move: Trick");
      } else {
        this.add("-enditem", source, myItem, "[silent]", "[from] move: Trick");
      }
    },
    secondary: null,
    target: "normal",
    type: "Psychic",
    zMove: { boost: { spe: 2 } },
    contestType: "Clever"
  },
  trickortreat: {
    num: 567,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Trick-or-Treat",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onHit(target) {
      if (target.hasType("Ghost"))
        return false;
      if (!target.addType("Ghost"))
        return false;
      this.add("-start", target, "typeadd", "Ghost", "[from] move: Trick-or-Treat");
      if (target.side.active.length === 2 && target.position === 1) {
        const action = this.queue.willMove(target);
        if (action && action.move.id === "curse") {
          action.targetLoc = -1;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Ghost",
    zMove: { boost: { atk: 1, def: 1, spa: 1, spd: 1, spe: 1 } },
    contestType: "Cute"
  },
  trickroom: {
    num: 433,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Trick Room",
    pp: 5,
    priority: -7,
    flags: { mirror: 1, metronome: 1 },
    pseudoWeather: "trickroom",
    condition: {
      duration: 5,
      durationCallback(source, effect) {
        if (source?.hasAbility("persistent")) {
          this.add("-activate", source, "ability: Persistent", "[move] Trick Room");
          return 7;
        }
        return 5;
      },
      onFieldStart(target, source) {
        if (source?.hasAbility("persistent")) {
          this.add("-fieldstart", "move: Trick Room", "[of] " + source, "[persistent]");
        } else {
          this.add("-fieldstart", "move: Trick Room", "[of] " + source);
        }
      },
      onFieldRestart(target, source) {
        this.field.removePseudoWeather("trickroom");
      },
      // Speed modification is changed in Pokemon.getActionSpeed() in sim/pokemon.js
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 1,
      onFieldEnd() {
        this.add("-fieldend", "move: Trick Room");
      }
    },
    secondary: null,
    target: "all",
    type: "Psychic",
    zMove: { boost: { accuracy: 1 } },
    contestType: "Clever"
  },
  triplearrows: {
    num: 843,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Triple Arrows",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    critRatio: 2,
    secondaries: [
      {
        chance: 50,
        boosts: {
          def: -1
        }
      },
      {
        chance: 30,
        volatileStatus: "flinch"
      }
    ],
    target: "normal",
    type: "Fighting"
  },
  tripleaxel: {
    num: 813,
    accuracy: 90,
    basePower: 20,
    basePowerCallback(pokemon, target, move) {
      return 20 * move.hit;
    },
    category: "Physical",
    name: "Triple Axel",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: 3,
    multiaccuracy: true,
    secondary: null,
    target: "normal",
    type: "Ice",
    zMove: { basePower: 120 },
    maxMove: { basePower: 140 }
  },
  tripledive: {
    num: 865,
    accuracy: 95,
    basePower: 30,
    category: "Physical",
    name: "Triple Dive",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: 3,
    secondary: null,
    target: "normal",
    type: "Water"
  },
  triplekick: {
    num: 167,
    accuracy: 90,
    basePower: 10,
    basePowerCallback(pokemon, target, move) {
      return 10 * move.hit;
    },
    category: "Physical",
    name: "Triple Kick",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    multihit: 3,
    multiaccuracy: true,
    secondary: null,
    target: "normal",
    type: "Fighting",
    zMove: { basePower: 120 },
    maxMove: { basePower: 80 },
    contestType: "Cool"
  },
  tropkick: {
    num: 688,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "Trop Kick",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 100,
      boosts: {
        atk: -1
      }
    },
    target: "normal",
    type: "Grass",
    contestType: "Cute"
  },
  trumpcard: {
    num: 376,
    accuracy: true,
    basePower: 0,
    basePowerCallback(source, target, move) {
      const callerMoveId = move.sourceEffect || move.id;
      const moveSlot = callerMoveId === "instruct" ? source.getMoveData(move.id) : source.getMoveData(callerMoveId);
      let bp;
      if (!moveSlot) {
        bp = 40;
      } else {
        switch (moveSlot.pp) {
          case 0:
            bp = 200;
            break;
          case 1:
            bp = 80;
            break;
          case 2:
            bp = 60;
            break;
          case 3:
            bp = 50;
            break;
          default:
            bp = 40;
            break;
        }
      }
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Special",
    isNonstandard: "Past",
    name: "Trump Card",
    pp: 5,
    noPPBoosts: true,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Cool"
  },
  twinbeam: {
    num: 888,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Twin Beam",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1 },
    multihit: 2,
    secondary: null,
    target: "normal",
    type: "Psychic",
    contestType: "Cool"
  },
  twineedle: {
    num: 41,
    accuracy: 100,
    basePower: 25,
    category: "Physical",
    isNonstandard: "Past",
    name: "Twineedle",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    multihit: 2,
    secondary: {
      chance: 20,
      status: "psn"
    },
    target: "normal",
    type: "Bug",
    maxMove: { basePower: 100 },
    contestType: "Cool"
  },
  twinkletackle: {
    num: 656,
    accuracy: true,
    basePower: 1,
    category: "Physical",
    isNonstandard: "Past",
    name: "Twinkle Tackle",
    pp: 1,
    priority: 0,
    flags: {},
    isZ: "fairiumz",
    secondary: null,
    target: "normal",
    type: "Fairy",
    contestType: "Cool"
  },
  twister: {
    num: 239,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Twister",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "flinch"
    },
    target: "allAdjacentFoes",
    type: "Dragon",
    contestType: "Cool"
  },
  uturn: {
    num: 369,
    accuracy: 100,
    basePower: 70,
    category: "Physical",
    name: "U-turn",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    selfSwitch: true,
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cute"
  },
  upperhand: {
    num: 918,
    accuracy: 100,
    basePower: 65,
    category: "Physical",
    name: "Upper Hand",
    pp: 15,
    priority: 3,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onTryHit(target, pokemon) {
      const action = this.queue.willMove(target);
      const move = action?.choice === "move" ? action.move : null;
      if (!move || move.priority <= 0.1 || move.category === "Status") {
        return false;
      }
    },
    secondary: {
      chance: 100,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Fighting"
  },
  uproar: {
    num: 253,
    accuracy: 100,
    basePower: 90,
    category: "Special",
    name: "Uproar",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, sound: 1, bypasssub: 1, metronome: 1, nosleeptalk: 1, failinstruct: 1 },
    self: {
      volatileStatus: "uproar"
    },
    onTryHit(target) {
      const activeTeam = target.side.activeTeam();
      const foeActiveTeam = target.side.foe.activeTeam();
      for (const [i, allyActive] of activeTeam.entries()) {
        if (allyActive && allyActive.status === "slp")
          allyActive.cureStatus();
        const foeActive = foeActiveTeam[i];
        if (foeActive && foeActive.status === "slp")
          foeActive.cureStatus();
      }
    },
    condition: {
      duration: 3,
      onStart(target) {
        this.add("-start", target, "Uproar");
      },
      onResidual(target) {
        if (target.volatiles["throatchop"]) {
          target.removeVolatile("uproar");
          return;
        }
        if (target.lastMove && target.lastMove.id === "struggle") {
          delete target.volatiles["uproar"];
        }
        this.add("-start", target, "Uproar", "[upkeep]");
      },
      onResidualOrder: 28,
      onResidualSubOrder: 1,
      onEnd(target) {
        this.add("-end", target, "Uproar");
      },
      onLockMove: "uproar",
      onAnySetStatus(status, pokemon) {
        if (status.id === "slp") {
          if (pokemon === this.effectState.target) {
            this.add("-fail", pokemon, "slp", "[from] Uproar", "[msg]");
          } else {
            this.add("-fail", pokemon, "slp", "[from] Uproar");
          }
          return null;
        }
      }
    },
    secondary: null,
    target: "randomNormal",
    type: "Normal",
    contestType: "Cute"
  },
  vacuumwave: {
    num: 410,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Vacuum Wave",
    pp: 30,
    priority: 1,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  vcreate: {
    num: 557,
    accuracy: 95,
    basePower: 180,
    category: "Physical",
    isNonstandard: "Unobtainable",
    name: "V-create",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    self: {
      boosts: {
        spe: -1,
        def: -1,
        spd: -1
      }
    },
    secondary: null,
    target: "normal",
    type: "Fire",
    zMove: { basePower: 220 },
    contestType: "Cool"
  },
  veeveevolley: {
    num: 741,
    accuracy: true,
    basePower: 0,
    basePowerCallback(pokemon) {
      const bp = Math.floor(pokemon.happiness * 10 / 25) || 1;
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Physical",
    isNonstandard: "LGPE",
    name: "Veevee Volley",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Cute"
  },
  venomdrench: {
    num: 599,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Venom Drench",
    pp: 20,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    onHit(target, source, move) {
      if (target.status === "psn" || target.status === "tox") {
        return !!this.boost({ atk: -1, spa: -1, spe: -1 }, target, source, move);
      }
      return false;
    },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Poison",
    zMove: { boost: { def: 1 } },
    contestType: "Clever"
  },
  venoshock: {
    num: 474,
    accuracy: 100,
    basePower: 65,
    category: "Special",
    name: "Venoshock",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    onBasePower(basePower, pokemon, target) {
      if (target.status === "psn" || target.status === "tox") {
        return this.chainModify(2);
      }
    },
    secondary: null,
    target: "normal",
    type: "Poison",
    contestType: "Beautiful"
  },
  victorydance: {
    num: 837,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Victory Dance",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, dance: 1, metronome: 1 },
    boosts: {
      atk: 1,
      def: 1,
      spe: 1
    },
    secondary: null,
    target: "self",
    type: "Fighting"
  },
  vinewhip: {
    num: 22,
    accuracy: 100,
    basePower: 45,
    category: "Physical",
    name: "Vine Whip",
    pp: 25,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Cool"
  },
  visegrip: {
    num: 11,
    accuracy: 100,
    basePower: 55,
    category: "Physical",
    name: "Vise Grip",
    pp: 30,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  vitalthrow: {
    num: 233,
    accuracy: true,
    basePower: 70,
    category: "Physical",
    isNonstandard: "Past",
    name: "Vital Throw",
    pp: 10,
    priority: -1,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Cool"
  },
  voltswitch: {
    num: 521,
    accuracy: 100,
    basePower: 70,
    category: "Special",
    name: "Volt Switch",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    selfSwitch: true,
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  volttackle: {
    num: 344,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Volt Tackle",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [33, 100],
    secondary: {
      chance: 10,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  wakeupslap: {
    num: 358,
    accuracy: 100,
    basePower: 70,
    basePowerCallback(pokemon, target, move) {
      if (target.status === "slp" || target.hasAbility("comatose")) {
        this.debug("BP doubled on sleeping target");
        return move.basePower * 2;
      }
      return move.basePower;
    },
    category: "Physical",
    isNonstandard: "Past",
    name: "Wake-Up Slap",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    onHit(target) {
      if (target.status === "slp")
        target.cureStatus();
    },
    secondary: null,
    target: "normal",
    type: "Fighting",
    contestType: "Tough"
  },
  waterfall: {
    num: 127,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Waterfall",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Water",
    contestType: "Tough"
  },
  watergun: {
    num: 55,
    accuracy: 100,
    basePower: 40,
    category: "Special",
    name: "Water Gun",
    pp: 25,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Cute"
  },
  waterpledge: {
    num: 518,
    accuracy: 100,
    basePower: 80,
    basePowerCallback(target, source, move) {
      if (["firepledge", "grasspledge"].includes(move.sourceEffect)) {
        this.add("-combine");
        return 150;
      }
      return move.basePower;
    },
    category: "Special",
    name: "Water Pledge",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, nonsky: 1, metronome: 1, pledgecombo: 1 },
    onPrepareHit(target, source, move) {
      for (const action of this.queue) {
        if (action.choice !== "move")
          continue;
        const otherMove = action.move;
        const otherMoveUser = action.pokemon;
        if (!otherMove || !action.pokemon || !otherMoveUser.isActive || otherMoveUser.fainted || action.maxMove || action.zmove) {
          continue;
        }
        if (otherMoveUser.isAlly(source) && ["firepledge", "grasspledge"].includes(otherMove.id)) {
          this.queue.prioritizeAction(action, move);
          this.add("-waiting", source, otherMoveUser);
          return null;
        }
      }
    },
    onModifyMove(move) {
      if (move.sourceEffect === "grasspledge") {
        move.type = "Grass";
        move.forceSTAB = true;
        move.sideCondition = "grasspledge";
      }
      if (move.sourceEffect === "firepledge") {
        move.type = "Water";
        move.forceSTAB = true;
        move.self = { sideCondition: "waterpledge" };
      }
    },
    condition: {
      duration: 4,
      onSideStart(targetSide) {
        this.add("-sidestart", targetSide, "Water Pledge");
      },
      onSideResidualOrder: 26,
      onSideResidualSubOrder: 7,
      onSideEnd(targetSide) {
        this.add("-sideend", targetSide, "Water Pledge");
      },
      onModifyMove(move, pokemon) {
        if (move.secondaries && move.id !== "secretpower") {
          this.debug("doubling secondary chance");
          for (const secondary of move.secondaries) {
            if (pokemon.hasAbility("serenegrace") && secondary.volatileStatus === "flinch")
              continue;
            if (secondary.chance)
              secondary.chance *= 2;
          }
          if (move.self?.chance)
            move.self.chance *= 2;
        }
      }
    },
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  waterpulse: {
    num: 352,
    accuracy: 100,
    basePower: 60,
    category: "Special",
    name: "Water Pulse",
    pp: 20,
    priority: 0,
    flags: { protect: 1, mirror: 1, distance: 1, metronome: 1, pulse: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "confusion"
    },
    target: "any",
    type: "Water",
    contestType: "Beautiful"
  },
  watershuriken: {
    num: 594,
    accuracy: 100,
    basePower: 15,
    basePowerCallback(pokemon, target, move) {
      if (pokemon.species.name === "Greninja-Ash" && pokemon.hasAbility("battlebond") && !pokemon.transformed) {
        return move.basePower + 5;
      }
      return move.basePower;
    },
    category: "Special",
    name: "Water Shuriken",
    pp: 20,
    priority: 1,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    multihit: [2, 5],
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Cool"
  },
  watersport: {
    num: 346,
    accuracy: true,
    basePower: 0,
    category: "Status",
    isNonstandard: "Past",
    name: "Water Sport",
    pp: 15,
    priority: 0,
    flags: { nonsky: 1, metronome: 1 },
    pseudoWeather: "watersport",
    condition: {
      duration: 5,
      onFieldStart(field, source) {
        this.add("-fieldstart", "move: Water Sport", "[of] " + source);
      },
      onBasePowerPriority: 1,
      onBasePower(basePower, attacker, defender, move) {
        if (move.type === "Fire") {
          this.debug("water sport weaken");
          return this.chainModify([1352, 4096]);
        }
      },
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 3,
      onFieldEnd() {
        this.add("-fieldend", "move: Water Sport");
      }
    },
    secondary: null,
    target: "all",
    type: "Water",
    zMove: { boost: { spd: 1 } },
    contestType: "Cute"
  },
  waterspout: {
    num: 323,
    accuracy: 100,
    basePower: 150,
    basePowerCallback(pokemon, target, move) {
      const bp = move.basePower * pokemon.hp / pokemon.maxhp;
      this.debug("BP: " + bp);
      return bp;
    },
    category: "Special",
    name: "Water Spout",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "allAdjacentFoes",
    type: "Water",
    contestType: "Beautiful"
  },
  wavecrash: {
    num: 834,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Wave Crash",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [33, 100],
    secondary: null,
    target: "normal",
    type: "Water"
  },
  weatherball: {
    num: 311,
    accuracy: 100,
    basePower: 50,
    category: "Special",
    name: "Weather Ball",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    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 "hail":
        case "snow":
          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 "hail":
        case "snow":
          move.basePower *= 2;
          break;
      }
      this.debug("BP: " + move.basePower);
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 160 },
    maxMove: { basePower: 130 },
    contestType: "Beautiful"
  },
  whirlpool: {
    num: 250,
    accuracy: 85,
    basePower: 35,
    category: "Special",
    name: "Whirlpool",
    pp: 15,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Water",
    contestType: "Beautiful"
  },
  whirlwind: {
    num: 18,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Whirlwind",
    pp: 20,
    priority: -6,
    flags: { reflectable: 1, mirror: 1, bypasssub: 1, allyanim: 1, metronome: 1, noassist: 1, failcopycat: 1, wind: 1 },
    forceSwitch: true,
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  wickedblow: {
    num: 817,
    accuracy: 100,
    basePower: 75,
    category: "Physical",
    name: "Wicked Blow",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, punch: 1 },
    willCrit: true,
    secondary: null,
    target: "normal",
    type: "Dark"
  },
  wickedtorque: {
    num: 897,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    isNonstandard: "Unobtainable",
    name: "Wicked Torque",
    pp: 10,
    priority: 0,
    flags: {
      protect: 1,
      failencore: 1,
      failmefirst: 1,
      nosleeptalk: 1,
      noassist: 1,
      failcopycat: 1,
      failmimic: 1,
      failinstruct: 1
    },
    noSketch: true,
    secondary: {
      chance: 10,
      status: "slp"
    },
    target: "normal",
    type: "Dark"
  },
  wideguard: {
    num: 469,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Wide Guard",
    pp: 10,
    priority: 3,
    flags: { snatch: 1 },
    sideCondition: "wideguard",
    onTry() {
      return !!this.queue.willAct();
    },
    onHitSide(side, source) {
      source.addVolatile("stall");
    },
    condition: {
      duration: 1,
      onSideStart(target, source) {
        this.add("-singleturn", source, "Wide Guard");
      },
      onTryHitPriority: 4,
      onTryHit(target, source, move) {
        if (move?.target !== "allAdjacent" && move.target !== "allAdjacentFoes") {
          return;
        }
        if (move.isZ || move.isMax) {
          if (["gmaxoneblow", "gmaxrapidflow"].includes(move.id))
            return;
          target.getMoveHitData(move).zBrokeProtect = true;
          return;
        }
        this.add("-activate", target, "move: Wide Guard");
        const lockedmove = source.getVolatile("lockedmove");
        if (lockedmove) {
          if (source.volatiles["lockedmove"].duration === 2) {
            delete source.volatiles["lockedmove"];
          }
        }
        return this.NOT_FAIL;
      }
    },
    secondary: null,
    target: "allySide",
    type: "Rock",
    zMove: { boost: { def: 1 } },
    contestType: "Tough"
  },
  wildboltstorm: {
    num: 847,
    accuracy: 80,
    basePower: 100,
    category: "Special",
    name: "Wildbolt Storm",
    pp: 10,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, wind: 1 },
    onModifyMove(move, pokemon, target) {
      if (target && ["raindance", "primordialsea"].includes(target.effectiveWeather())) {
        move.accuracy = true;
      }
    },
    secondary: {
      chance: 20,
      status: "par"
    },
    target: "allAdjacentFoes",
    type: "Electric"
  },
  wildcharge: {
    num: 528,
    accuracy: 100,
    basePower: 90,
    category: "Physical",
    name: "Wild Charge",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [1, 4],
    secondary: null,
    target: "normal",
    type: "Electric",
    contestType: "Tough"
  },
  willowisp: {
    num: 261,
    accuracy: 85,
    basePower: 0,
    category: "Status",
    name: "Will-O-Wisp",
    pp: 15,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    status: "brn",
    secondary: null,
    target: "normal",
    type: "Fire",
    zMove: { boost: { atk: 1 } },
    contestType: "Beautiful"
  },
  wingattack: {
    num: 17,
    accuracy: 100,
    basePower: 60,
    category: "Physical",
    name: "Wing Attack",
    pp: 35,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, distance: 1, metronome: 1 },
    secondary: null,
    target: "any",
    type: "Flying",
    contestType: "Cool"
  },
  wish: {
    num: 273,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Wish",
    pp: 10,
    priority: 0,
    flags: { snatch: 1, heal: 1, metronome: 1 },
    slotCondition: "Wish",
    condition: {
      duration: 2,
      onStart(pokemon, source) {
        this.effectState.hp = source.maxhp / 2;
      },
      onResidualOrder: 4,
      onEnd(target) {
        if (target && !target.fainted) {
          const damage = this.heal(this.effectState.hp, target, target);
          if (damage) {
            this.add("-heal", target, target.getHealth, "[from] move: Wish", "[wisher] " + this.effectState.source.name);
          }
        }
      }
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { spd: 1 } },
    contestType: "Cute"
  },
  withdraw: {
    num: 110,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Withdraw",
    pp: 40,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      def: 1
    },
    secondary: null,
    target: "self",
    type: "Water",
    zMove: { boost: { def: 1 } },
    contestType: "Cute"
  },
  wonderroom: {
    num: 472,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Wonder Room",
    pp: 10,
    priority: 0,
    flags: { mirror: 1, metronome: 1 },
    pseudoWeather: "wonderroom",
    condition: {
      duration: 5,
      durationCallback(source, effect) {
        if (source?.hasAbility("persistent")) {
          this.add("-activate", source, "ability: Persistent", "[move] Wonder Room");
          return 7;
        }
        return 5;
      },
      onModifyMove(move, source, target) {
        if (!move.overrideOffensiveStat)
          return;
        const statAndBoosts = move.overrideOffensiveStat;
        if (!["def", "spd"].includes(statAndBoosts))
          return;
        move.overrideOffensiveStat = statAndBoosts === "def" ? "spd" : "def";
        this.hint(`${move.name} uses ${statAndBoosts === "def" ? "" : "Sp. "}Def boosts when Wonder Room is active.`);
      },
      onFieldStart(field, source) {
        if (source?.hasAbility("persistent")) {
          this.add("-fieldstart", "move: Wonder Room", "[of] " + source, "[persistent]");
        } else {
          this.add("-fieldstart", "move: Wonder Room", "[of] " + source);
        }
      },
      onFieldRestart(target, source) {
        this.field.removePseudoWeather("wonderroom");
      },
      // Swapping defenses partially implemented in sim/pokemon.js:Pokemon#calculateStat and Pokemon#getStat
      onFieldResidualOrder: 27,
      onFieldResidualSubOrder: 5,
      onFieldEnd() {
        this.add("-fieldend", "move: Wonder Room");
      }
    },
    secondary: null,
    target: "all",
    type: "Psychic",
    zMove: { boost: { spd: 1 } },
    contestType: "Clever"
  },
  woodhammer: {
    num: 452,
    accuracy: 100,
    basePower: 120,
    category: "Physical",
    name: "Wood Hammer",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    recoil: [33, 100],
    secondary: null,
    target: "normal",
    type: "Grass",
    contestType: "Tough"
  },
  workup: {
    num: 526,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Work Up",
    pp: 30,
    priority: 0,
    flags: { snatch: 1, metronome: 1 },
    boosts: {
      atk: 1,
      spa: 1
    },
    secondary: null,
    target: "self",
    type: "Normal",
    zMove: { boost: { atk: 1 } },
    contestType: "Tough"
  },
  worryseed: {
    num: 388,
    accuracy: 100,
    basePower: 0,
    category: "Status",
    name: "Worry Seed",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, allyanim: 1, metronome: 1 },
    onTryImmunity(target) {
      if (target.ability === "truant" || target.ability === "insomnia") {
        return false;
      }
    },
    onTryHit(target) {
      if (target.getAbility().flags["cantsuppress"]) {
        return false;
      }
    },
    onHit(pokemon) {
      const oldAbility = pokemon.setAbility("insomnia");
      if (oldAbility) {
        this.add("-ability", pokemon, "Insomnia", "[from] move: Worry Seed");
        if (pokemon.status === "slp") {
          pokemon.cureStatus();
        }
        return;
      }
      return oldAbility;
    },
    secondary: null,
    target: "normal",
    type: "Grass",
    zMove: { boost: { spe: 1 } },
    contestType: "Clever"
  },
  wrap: {
    num: 35,
    accuracy: 90,
    basePower: 15,
    category: "Physical",
    name: "Wrap",
    pp: 20,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    volatileStatus: "partiallytrapped",
    secondary: null,
    target: "normal",
    type: "Normal",
    contestType: "Tough"
  },
  wringout: {
    num: 378,
    accuracy: 100,
    basePower: 0,
    basePowerCallback(pokemon, target, move) {
      const hp = target.hp;
      const maxHP = target.maxhp;
      const bp = Math.floor(Math.floor((120 * (100 * Math.floor(hp * 4096 / maxHP)) + 2048 - 1) / 4096) / 100) || 1;
      this.debug("BP for " + hp + "/" + maxHP + " HP: " + bp);
      return bp;
    },
    category: "Special",
    isNonstandard: "Past",
    name: "Wring Out",
    pp: 5,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { basePower: 190 },
    maxMove: { basePower: 140 },
    contestType: "Tough"
  },
  xscissor: {
    num: 404,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "X-Scissor",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1, slicing: 1 },
    secondary: null,
    target: "normal",
    type: "Bug",
    contestType: "Cool"
  },
  yawn: {
    num: 281,
    accuracy: true,
    basePower: 0,
    category: "Status",
    name: "Yawn",
    pp: 10,
    priority: 0,
    flags: { protect: 1, reflectable: 1, mirror: 1, metronome: 1 },
    volatileStatus: "yawn",
    onTryHit(target) {
      if (target.status || !target.runStatusImmunity("slp")) {
        return false;
      }
    },
    condition: {
      noCopy: true,
      // doesn't get copied by Baton Pass
      duration: 2,
      onStart(target, source) {
        this.add("-start", target, "move: Yawn", "[of] " + source);
      },
      onResidualOrder: 23,
      onEnd(target) {
        this.add("-end", target, "move: Yawn", "[silent]");
        target.trySetStatus("slp", this.effectState.source);
      }
    },
    secondary: null,
    target: "normal",
    type: "Normal",
    zMove: { boost: { spe: 1 } },
    contestType: "Cute"
  },
  zapcannon: {
    num: 192,
    accuracy: 50,
    basePower: 120,
    category: "Special",
    name: "Zap Cannon",
    pp: 5,
    priority: 0,
    flags: { protect: 1, mirror: 1, metronome: 1, bullet: 1 },
    secondary: {
      chance: 100,
      status: "par"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  zenheadbutt: {
    num: 428,
    accuracy: 90,
    basePower: 80,
    category: "Physical",
    name: "Zen Headbutt",
    pp: 15,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 20,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Psychic",
    contestType: "Clever"
  },
  zingzap: {
    num: 716,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    name: "Zing Zap",
    pp: 10,
    priority: 0,
    flags: { contact: 1, protect: 1, mirror: 1, metronome: 1 },
    secondary: {
      chance: 30,
      volatileStatus: "flinch"
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  },
  zippyzap: {
    num: 729,
    accuracy: 100,
    basePower: 80,
    category: "Physical",
    isNonstandard: "LGPE",
    name: "Zippy Zap",
    pp: 10,
    priority: 2,
    flags: { contact: 1, protect: 1, mirror: 1 },
    secondary: {
      chance: 100,
      self: {
        boosts: {
          evasion: 1
        }
      }
    },
    target: "normal",
    type: "Electric",
    contestType: "Cool"
  }
};
//# sourceMappingURL=moves.js.map
