"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 abilities_exports = {};
__export(abilities_exports, {
  Abilities: () => Abilities
});
module.exports = __toCommonJS(abilities_exports);
const Abilities = {
  noability: {
    isNonstandard: "Past",
    flags: {},
    name: "No Ability",
    rating: 0.1,
    num: 0
  },
  adaptability: {
    onModifySTAB(stab, source, target, move) {
      if (move.forceSTAB || source.hasType(move.type)) {
        if (stab === 2) {
          return 2.25;
        }
        return 2;
      }
    },
    flags: {},
    name: "Adaptability",
    rating: 4,
    num: 91
  },
  aerilate: {
    onModifyTypePriority: -1,
    onModifyType(move, pokemon) {
      const noModifyType = [
        "judgment",
        "multiattack",
        "naturalgift",
        "revelationdance",
        "technoblast",
        "terrainpulse",
        "weatherball"
      ];
      if (move.type === "Normal" && !noModifyType.includes(move.id) && !(move.isZ && move.category !== "Status") && !(move.name === "Tera Blast" && pokemon.terastallized)) {
        move.type = "Flying";
        move.typeChangerBoosted = this.effect;
      }
    },
    onBasePowerPriority: 23,
    onBasePower(basePower, pokemon, target, move) {
      if (move.typeChangerBoosted === this.effect)
        return this.chainModify([4915, 4096]);
    },
    flags: {},
    name: "Aerilate",
    rating: 4,
    num: 184
  },
	aftermath: {
    onDamagingHitOrder: 1,
    onDamagingHit(damage, target, source, move) {
      if (!target.hp && this.checkMoveMakesContact(move, source, target, true)) {
        const adjacentPokemon = [...target.adjacentFoes(), ...target.adjacentAllies()];
        
        for (const adjacent of adjacentPokemon) {
          if (adjacent.side.getSideCondition('wideguard')) {
            continue;
          }
          if (!adjacent.volatiles['stall']) {
            this.damage(adjacent.baseMaxhp / 4, adjacent, target);
          }
        }
      }
    },
    flags: {},
    name: "Aftermath",
    rating: 2,
    num: 106
  },
  airlock: {
    onSwitchIn(pokemon) {
      this.effectState.switchingIn = true;
    },
    onStart(pokemon) {
      if (this.effectState.switchingIn) {
        this.add("-ability", pokemon, "Air Lock");
        this.effectState.switchingIn = false;
      }
      this.eachEvent("WeatherChange", this.effect);
    },
    onEnd(pokemon) {
      this.eachEvent("WeatherChange", this.effect);
    },
    suppressWeather: true,
    flags: {},
    name: "Air Lock",
    rating: 1.5,
    num: 76
  },
  analytic: {
    onBasePowerPriority: 21,
    onBasePower(basePower, pokemon) {
      let boosted = true;
      for (const target of this.getAllActive()) {
        if (target === pokemon)
          continue;
        if (this.queue.willMove(target)) {
          boosted = false;
          break;
        }
      }
      if (boosted) {
        this.debug("Analytic boost");
        return this.chainModify([5325, 4096]);
      }
    },
    flags: {},
    name: "Analytic",
    rating: 2.5,
    num: 148
  },
  angerpoint: {
    onHit(target, source, move) {
      if (!target.hp) return;
      if (target.volatiles['angerpointboost']) return;
  
      if (move?.effectType === "Move" && target.getMoveHitData(move).crit) {
        this.boost({ atk: 3 }, target, target);
        target.addVolatile('angerpointboost');
      }
  
      if (target.volatiles['taunt']) {
        this.boost({ atk: 3 }, target, target);
        target.addVolatile('angerpointboost');
      }
    },
    flags: {},
    name: "Anger Point",
    rating: 1,
    num: 83
  },
  angershell: {
    onDamage(damage, target, source, effect) {
      if (effect.effectType === "Move" && !effect.multihit && (!effect.negateSecondary && !(effect.hasSheerForce && source.hasAbility("sheerforce")))) {
        this.effectState.checkedAngerShell = false;
      } else {
        this.effectState.checkedAngerShell = true;
      }
    },
    onTryEatItem(item) {
      const healingItems = [
        "aguavberry",
        "enigmaberry",
        "figyberry",
        "iapapaberry",
        "magoberry",
        "sitrusberry",
        "wikiberry",
        "oranberry",
        "berryjuice"
      ];
      if (healingItems.includes(item.id)) {
        return this.effectState.checkedAngerShell;
      }
      return true;
    },
    onAfterMoveSecondary(target, source, move) {
      this.effectState.checkedAngerShell = true;
      if (!source || source === target || !target.hp || !move.totalDamage)
        return;
      const lastAttackedBy = target.getLastAttackedBy();
      if (!lastAttackedBy)
        return;
      const damage = move.multihit ? move.totalDamage : lastAttackedBy.damage;
      if (target.hp <= target.maxhp / 2 && target.hp + damage > target.maxhp / 2) {
        this.boost({ atk: 1, spa: 1, spe: 1, def: -1, spd: -1 }, target, target);
      }
    },
    flags: {},
    name: "Anger Shell",
    rating: 3,
    num: 271
  },
  anticipation: {
    onStart(pokemon) {
      for (const target of pokemon.foes()) {
        for (const moveSlot of target.moveSlots) {
          const move = this.dex.moves.get(moveSlot.move);
          if (move.category === "Status") continue;
          const moveType = move.id === "hiddenpower" ? target.hpType : move.type;
          if (this.dex.getImmunity(moveType, pokemon) && this.dex.getEffectiveness(moveType, pokemon) > 0 || move.ohko) {
            this.add('-ability', pokemon, 'Anticipation');
            pokemon.addVolatile('anticipation');
            return;
          }
        }
      }
    },
    condition: {
      duration: 1,
      onSourceModifyDamage(damage, source, target, move) {
        if (move.type && this.dex.getEffectiveness(move.type, target) > 0) {
          this.debug('Anticipation reduce damage');
          return this.chainModify(0.75);
        }
      },
    },
    flags: {},
    name: "Anticipation",
    rating: 0.5,
    num: 107
  },
  arenatrap: {
    onBeforeTurn(pokemon) {
      for (const side of this.sides) {
        if (side.hasAlly(pokemon)) continue;
        side.addSideCondition('arenatrap', pokemon);
        const data = side.getSideConditionData('arenatrap');
        if (!data.sources) {
          data.sources = [];
        }
        data.sources.push(pokemon);
      }
    },
    onBasePower(relayVar, source, target, move) {
      if ((target.beingCalledBack || target.switchFlag) && target.isGrounded()) {
        this.debug('arenatrap damage boost');
        return move.basePower * 2;
      }
      return move.basePower;
    },
    condition: {
      duration: 1,
      onBeforeSwitchOut(pokemon) {
        if (!pokemon.isGrounded()) return;

        const move = this.queue.willMove(pokemon.foes()[0]);
        const moveName = move?.moveid?.toString() || "";
        this.debug('arenatrap activation');
        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.foes()[0], 'ability: Arena Trap');
            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(moveName, source, source.getLocOf(pokemon));
        }
      },
    },
    flags: {},
    name: "Arena Trap",
    rating: 5,
    num: 71
  },
  armortail: {
    onFoeTryMove(target, source, move) {
      const targetAllExceptions = ["perishsong", "flowershield", "rototiller"];
      if (move.target === "foeSide" || move.target === "all" && !targetAllExceptions.includes(move.id)) {
        return;
      }
      const armorTailHolder = this.effectState.target;
      if ((source.isAlly(armorTailHolder) || move.target === "all") && move.priority > 0.1) {
        this.attrLastMove("[still]");
        this.add("cant", armorTailHolder, "ability: Armor Tail", move, "[of] " + target);
        return false;
      }
    },
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      if (boost.def && boost.def < 0) {
        delete boost.def;
        if (!effect.secondaries && effect.id !== "octolock") {
          this.add("-fail", target, "unboost", "Defense", "[from] ability: Armor Tail", "[of] " + target);
        }
      }
    },
    flags: { breakable: 1 },
    name: "Armor Tail",
    rating: 2.5,
    num: 296
  },
  aromaveil: {
    onAllyTryAddVolatile(status, target, source, effect) {
      if (["attract", "disable", "encore", "healblock", "taunt", "torment"].includes(status.id)) {
        if (effect.effectType === "Move") {
          const effectHolder = this.effectState.target;
          this.add("-block", target, "ability: Aroma Veil", "[of] " + effectHolder);
        }
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Aroma Veil",
    rating: 2,
    num: 165
  },
  asoneglastrier: {
    onPreStart(pokemon) {
      this.add("-ability", pokemon, "As One");
      this.add("-ability", pokemon, "Unnerve");
      this.effectState.unnerved = true;
    },
    onEnd() {
      this.effectState.unnerved = false;
    },
    onFoeTryEatItem() {
      return !this.effectState.unnerved;
    },
    onSourceAfterFaint(length, target, source, effect) {
      if (effect && effect.effectType === "Move") {
        this.boost({ atk: length }, source, source, this.dex.abilities.get("chillingneigh"));
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "As One (Glastrier)",
    rating: 3.5,
    num: 266
  },
  asonespectrier: {
    onPreStart(pokemon) {
      this.add("-ability", pokemon, "As One");
      this.add("-ability", pokemon, "Unnerve");
      this.effectState.unnerved = true;
    },
    onEnd() {
      this.effectState.unnerved = false;
    },
    onFoeTryEatItem() {
      return !this.effectState.unnerved;
    },
    onSourceAfterFaint(length, target, source, effect) {
      if (effect && effect.effectType === "Move") {
        this.boost({ spa: length }, source, source, this.dex.abilities.get("grimneigh"));
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "As One (Spectrier)",
    rating: 3.5,
    num: 267
  },
  aurabreak: {
    onStart(pokemon) {
      if (this.suppressingAbility(pokemon))
        return;
      this.add("-ability", pokemon, "Aura Break");
    },
    onAnyTryPrimaryHit(target, source, move) {
      if (target === source || move.category === "Status")
        return;
      move.hasAuraBreak = true;
    },
    flags: { breakable: 1 },
    name: "Aura Break",
    rating: 1,
    num: 188
  },
  baddreams: {
    onResidualOrder: 28,
    onResidualSubOrder: 2,
    onResidual(pokemon) {
      if (!pokemon.hp)
        return;
      for (const target of pokemon.foes()) {
        if (target.status === "slp" || target.hasAbility("comatose")) {
          this.damage(target.baseMaxhp / 8, target, pokemon);
        }
      }
    },
    flags: {},
    name: "Bad Dreams",
    rating: 1.5,
    num: 123
  },
	ballfetch: {
		flags: {},
		name: "Ball Fetch",
		rating: 0,
		num: 237,
	},
  battery: {
    onAllyBasePowerPriority: 22,
    onAllyBasePower(basePower, attacker, defender, move) {
      if (attacker !== this.effectState.target && move.category === "Special") {
        this.debug("Battery boost");
        return this.chainModify([5325, 4096]);
      }
    },
    flags: {},
    name: "Battery",
    rating: 0,
    num: 217
  },
  battlearmor: {
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      if (boost.atk && boost.atk < 0) {
        delete boost.atk;
        if (!effect.secondaries) {
          this.add("-fail", target, "unboost", "Attack", "[from] ability: Battle Armor", "[of] " + target);
        }
      }
	},
    onCriticalHit: false,
    flags: { breakable: 1 },
    name: "Battle Armor",
    rating: 1,
    num: 4
  },
  battlebond: {
    onSourceAfterFaint(length, target, source, effect) {
      if (effect?.effectType !== "Move")
        return;
      if (source.abilityState.battleBondTriggered)
        return;
      if (source.species.id === "greninjabond" && source.hp && !source.transformed && source.side.foePokemonLeft()) {
        this.boost({ atk: 1, spa: 1, spe: 1 }, source, source, this.effect);
        this.add("-activate", source, "ability: Battle Bond");
        source.abilityState.battleBondTriggered = true;
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "Battle Bond",
    rating: 3.5,
    num: 210
  },
  beadsofruin: {
    onStart(pokemon) {
      if (this.suppressingAbility(pokemon))
        return;
      this.add("-ability", pokemon, "Beads of Ruin");
    },
    onAnyModifySpD(spd, target, source, move) {
      const abilityHolder = this.effectState.target;
      if (target.hasAbility("Beads of Ruin"))
        return;
      if (!move.ruinedSpD?.hasAbility("Beads of Ruin"))
        move.ruinedSpD = abilityHolder;
      if (move.ruinedSpD !== abilityHolder)
        return;
      this.debug("Beads of Ruin SpD drop");
      return this.chainModify(0.80);
    },
    flags: {},
    name: "Beads of Ruin",
    rating: 4.5,
    num: 284
  },
  beastboost: {
    onSourceAfterFaint(length, target, source, effect) {
      if (effect && effect.effectType === "Move") {
        const bestStat = source.getBestStat(true, true);
        this.boost({ [bestStat]: length }, source);
      }
    },
    flags: {},
    name: "Beast Boost",
    rating: 3.5,
    num: 224
  },
  berserk: {
    onSourceAfterFaint(length, target, source, effect) {
      if (effect && effect.effectType === "Move") {
        this.boost({ spa: length }, source);
      }
    },
    flags: {},
    name: "Berserk",
    rating: 2,
    num: 201
  },
  bigpecks: {
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      if (boost.def && boost.def < 0) {
        delete boost.def;
        if (!effect.secondaries && effect.id !== "octolock") {
          this.add("-fail", target, "unboost", "Defense", "[from] ability: Big Pecks", "[of] " + target);
        }
      }
    },
    onBasePowerPriority: 19,
    onBasePower(basePower, attacker, defender, move) {
      if (move.flags["pecking"]) {
        this.debug("BigPecks boost");
        return this.chainModify(1.3);
      }
    },
    flags: { breakable: 1 },
    name: "Big Pecks",
    rating: 0.5,
    num: 145
  },
  blaze: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Fire" && attacker.hp <= attacker.maxhp / 2) {
        this.debug("Blaze boost");
        return this.chainModify(1.4);
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, attacker, defender, move) {
      if (move.type === "Fire" && attacker.hp <= attacker.maxhp / 2) {
        this.debug("Blaze boost");
        return this.chainModify(1.4);
      }
    },
    flags: {},
    name: "Blaze",
    rating: 2,
    num: 66
  },
  bulletproof: {
    onTryHit(pokemon, target, move) {
      if (move.flags["bullet"]) {
        this.add("-immune", pokemon, "[from] ability: Bulletproof");
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Bulletproof",
    rating: 3,
    num: 171
  },
  cheekpouch: {
    onEatItem(item, pokemon) {
      this.heal(pokemon.baseMaxhp / 3);
    },
    flags: {},
    name: "Cheek Pouch",
    rating: 2,
    num: 167
  },
  chillingneigh: {
    onSourceAfterFaint(length, target, source, effect) {
      if (effect && effect.effectType === "Move") {
        this.boost({ atk: length }, source);
      }
    },
    flags: {},
    name: "Chilling Neigh",
    rating: 3,
    num: 264
  },
  chlorophyll: {
    onModifySpe(spe, pokemon) {
      if (["sunnyday", "desolateland"].includes(pokemon.effectiveWeather())) {
        return this.chainModify(2);
      }
    },
    flags: {},
    name: "Chlorophyll",
    rating: 3,
    num: 34
  },
  clearbody: {
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      let showMsg = false;
      let i;
      for (i in boost) {
        if (boost[i] < 0) {
          delete boost[i];
          showMsg = true;
        }
      }
      if (showMsg && !effect.secondaries && effect.id !== "octolock") {
        this.add("-fail", target, "unboost", "[from] ability: Clear Body", "[of] " + target);
      }
    },
    flags: { breakable: 1 },
    name: "Clear Body",
    rating: 2,
    num: 29
  },
  cloudnine: {
    onStart(pokemon) {
            if (this.field.weather) {
                this.field.clearWeather();
                this.add('-activate', pokemon, 'ability: Cloud Nine');
            }
            for (const side of this.sides) {
                if (side.sideConditions['tailwind']) {
                    side.removeSideCondition('tailwind');
                }
            }
        },
	suppressWeather: true,
    flags: {},
    name: "Cloud Nine",
    rating: 1.5,
    num: 13
  },
  colorchange: {
    onTryHitPriority: 1,
    onTryHit(target, source, move) {
      const type = move.type;
      if (target.isActive && move.effectType === "Move" && target !== source && type !== "???" && !target.hasItem("terashard")) {
        if (!target.setType(type))
          return false;
        this.add("-start", target, "typechange", type, "[from] ability: Color Change");
        if (target.side.active.length === 2 && target.position === 1) {
          const action = this.queue.willMove(target);
          if (action && action.move.id === "curse") {
            action.targetLoc = -1;
          }
        }
      }
    },
    name: "Color Change",
    shortDesc: "This Pokemon's type changes to the type of a move it's about to be hit by, unless it has the type.",
    rating: 0,
    num: 16
  },
  comatose: {
    onStart(pokemon) {
      this.add("-ability", pokemon, "Comatose");
    },
    onSetStatus(status, target, source, effect) {
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Comatose");
      }
      return false;
    },
    // Permanent sleep "status" implemented in the relevant sleep-checking effects
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "Comatose",
    rating: 4,
    num: 213
  },
	commander: {
    onUpdate(pokemon) {
        // Ensure the ability only works in Doubles or Triples
        if (this.gameType !== 'doubles' && this.gameType !== 'triples') return;
        
        // In Doubles, there is only one ally. In Triples, we check both adjacent allies.
        const allies = pokemon.allies();

        // Handle Tatsugiri and Dondozo pairing in Doubles or Triples
        const dondozoAlly = allies.find(ally => ally.baseSpecies.baseSpecies === 'Dondozo');
        if (pokemon.baseSpecies.baseSpecies === 'Tatsugiri' && dondozoAlly) {
            if (!pokemon.getVolatile('commanding')) {
                if (dondozoAlly.getVolatile('commanded')) return;
                this.queue.cancelAction(pokemon);
                this.add('-activate', pokemon, 'ability: Commander', '[of] ' + dondozoAlly);
                pokemon.addVolatile('commanding');
                dondozoAlly.addVolatile('commanded', pokemon);
            } else {
                if (!dondozoAlly.fainted) return;
                pokemon.removeVolatile('commanding');
            }
        }

        // Handle Remoraid and Mantine pairing in Doubles or Triples
        const mantineAlly = allies.find(ally => ally.baseSpecies.baseSpecies === 'Mantine');
        if (pokemon.baseSpecies.baseSpecies === 'Remoraid' && mantineAlly) {
            if (!pokemon.getVolatile('commanding')) {
                if (mantineAlly.getVolatile('commanded')) return;
                this.queue.cancelAction(pokemon);
                this.add('-activate', pokemon, 'ability: Commander', '[of] ' + mantineAlly);
                pokemon.addVolatile('commanding');
                mantineAlly.addVolatile('commanded', pokemon);
            } else {
                if (!mantineAlly.fainted) return;
                pokemon.removeVolatile('commanding');
            }
        }

        // Edge case handling: if neither pairing applies or if ally faints
        if (pokemon.getVolatile('commanding') && (!dondozoAlly || dondozoAlly.fainted) && (!mantineAlly || mantineAlly.fainted)) {
            pokemon.removeVolatile('commanding');
        }
    },
    flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1},
    name: "Commander",
    rating: 0,
    num: 279,
},
  competitive: {
    onAfterEachBoost(boost, target, source, effect) {
      if (!source || target.isAlly(source)) {
        return;
      }
      let statsLowered = false;
      let i;
      for (i in boost) {
        if (boost[i] < 0) {
          statsLowered = true;
        }
      }
      if (statsLowered) {
        this.boost({ spa: 2 }, target, target, null, false, true);
      }
    },
    flags: {},
    name: "Competitive",
    rating: 2.5,
    num: 172
  },
  compoundeyes: {
    onSourceModifyAccuracyPriority: -1,
    onSourceModifyAccuracy(accuracy) {
      if (typeof accuracy !== "number")
        return;
      this.debug("compoundeyes - enhancing accuracy");
      return this.chainModify([5325, 4096]);
    },
    flags: {},
    name: "Compound Eyes",
    rating: 3,
    num: 14
  },
  contrary: {
    onChangeBoost(boost, target, source, effect) {
      if (effect && effect.id === "zpower")
        return;
      let i;
      for (i in boost) {
        boost[i] *= -1;
      }
    },
    flags: { breakable: 1 },
    name: "Contrary",
    rating: 4.5,
    num: 126
  },
  corrosion: {
    // Toxic effect implemented in sim/pokemon.js:Pokemon#setStatus
	  onModifyMove(move, source, target) {
		if (move.type === 'Poison') {
			if (target.hasType('Steel')) {
				move.ignoreImmunity = {'Poison': true};
			}
		}
	},
    flags: {},
    name: "Corrosion",
    rating: 2.5,
    num: 212
  },
  costar: {
    onStart(pokemon) {
      const ally = pokemon.allies()[0];
      if (!ally)
        return;
      let i;
      for (i in ally.boosts) {
        pokemon.boosts[i] = ally.boosts[i];
      }
      const volatilesToCopy = ["focusenergy", "gmaxchistrike", "laserfocus"];
      for (const volatile of volatilesToCopy) {
        if (ally.volatiles[volatile]) {
          pokemon.addVolatile(volatile);
          if (volatile === "gmaxchistrike")
            pokemon.volatiles[volatile].layers = ally.volatiles[volatile].layers;
        } else {
          pokemon.removeVolatile(volatile);
        }
      }
      this.add("-copyboost", pokemon, ally, "[from] ability: Costar");
    },
    flags: {},
    name: "Costar",
    rating: 0,
    num: 294
  },
  cottondown: {
    onDamagingHit(damage, target, source, move) {
      let activated = false;
      for (const pokemon of this.getAllActive()) {
        if (pokemon === target || pokemon.fainted)
          continue;
        if (!activated) {
          this.add("-ability", target, "Cotton Down");
          activated = true;
        }
        this.boost({ spe: -1 }, pokemon, target, null, true);
      }
    },
    flags: {},
    name: "Cotton Down",
    rating: 2,
    num: 238
  },
  cudchew: {
    onEatItem(item, pokemon) {
      if (item.isBerry && pokemon.addVolatile("cudchew")) {
        pokemon.volatiles["cudchew"].berry = item;
      }
    },
    onEnd(pokemon) {
      delete pokemon.volatiles["cudchew"];
    },
    condition: {
      noCopy: true,
      duration: 2,
      onRestart() {
        this.effectState.duration = 2;
      },
      onResidualOrder: 28,
      onResidualSubOrder: 2,
      onEnd(pokemon) {
        if (pokemon.hp) {
          const item = this.effectState.berry;
          this.add("-activate", pokemon, "ability: Cud Chew");
          this.add("-enditem", pokemon, item.name, "[eat]");
          if (this.singleEvent("Eat", item, null, pokemon, null, null)) {
            this.runEvent("EatItem", pokemon, null, null, item);
          }
          if (item.onEat)
            pokemon.ateBerry = true;
        }
      }
    },
    flags: {},
    name: "Cud Chew",
    rating: 2,
    num: 291
  },
  curiousmedicine: {
    onStart(pokemon) {
      for (const ally of pokemon.adjacentAllies()) {
        ally.clearBoosts();
        this.add("-clearboost", ally, "[from] ability: Curious Medicine", "[of] " + pokemon);
      }
    },
    flags: {},
    name: "Curious Medicine",
    rating: 0,
    num: 261
  },
  cursedbody: {
    onDamagingHit(damage, target, source, move) {
      if (source.volatiles["disable"])
        return;
      if (!move.isMax && !move.flags["futuremove"] && move.id !== "struggle") {
        if (this.randomChance(3, 10)) {
          source.addVolatile("disable", this.effectState.target);
        }
      }
    },
    flags: {},
    name: "Cursed Body",
    rating: 2,
    num: 130
  },
  cutecharm: {
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target)) {
        if (this.randomChance(3, 10)) {
          source.addVolatile("attract", this.effectState.target);
        }
      }
    },
    flags: {},
    name: "Cute Charm",
    rating: 0.5,
    num: 56
  },
  damp: {
    onAnyTryMove(target, source, effect) {
      if (["explosion", "mindblown", "mistyexplosion", "selfdestruct"].includes(effect.id)) {
        this.attrLastMove("[still]");
        this.add("cant", this.effectState.target, "ability: Damp", effect, "[of] " + target);
        return false;
      }
    },
    onAnyDamage(damage, target, source, effect) {
      if (effect && effect.name === "Aftermath") {
        return false;
      }
    },
    onSourceModifyAtkPriority: 5,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Fire") {
        return this.chainModify(0.5);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Fire") {
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Damp",
    rating: 0.5,
    num: 6
  },
  dancer: {
    flags: {},
    name: "Dancer",
    // implemented in runMove in scripts.js
    rating: 1.5,
    num: 216
  },
  darkaura: {
    onStart(pokemon) {
      if (this.suppressingAbility(pokemon))
        return;
      this.add("-ability", pokemon, "Dark Aura");
    },
    onAnyBasePowerPriority: 20,
    onAnyBasePower(basePower, source, target, move) {
      if (target === source || move.category === "Status" || move.type !== "Dark")
        return;
      if (!move.auraBooster?.hasAbility("Dark Aura"))
        move.auraBooster = this.effectState.target;
      if (move.auraBooster !== this.effectState.target)
        return;
      return this.chainModify([move.hasAuraBreak ? 3072 : 5448, 4096]);
    },
    flags: {},
    name: "Dark Aura",
    rating: 3,
    num: 186
  },
  dauntlessshield: {
    onStart(pokemon) {
      if (pokemon.shieldBoost)
        return;
      pokemon.shieldBoost = true;
      this.boost({ def: 1 }, pokemon);
    },
    flags: {},
    name: "Dauntless Shield",
    rating: 3.5,
    num: 235
  },
  dazzling: {
    onFoeTryMove(target, source, move) {
      const targetAllExceptions = ["perishsong", "flowershield", "rototiller"];
      if (move.target === "foeSide" || move.target === "all" && !targetAllExceptions.includes(move.id)) {
        return;
      }
      const dazzlingHolder = this.effectState.target;
      if ((source.isAlly(dazzlingHolder) || move.target === "all") && move.priority > 0.1) {
        this.attrLastMove("[still]");
        this.add("cant", dazzlingHolder, "ability: Dazzling", move, "[of] " + target);
        return false;
      }
    },
    flags: { breakable: 1 },
    name: "Dazzling",
    rating: 2.5,
    num: 219
  },
  defeatist: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, pokemon) {
      if (pokemon.hp <= pokemon.maxhp / 3) {
        return this.chainModify(0.5);
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, pokemon) {
      if (pokemon.hp <= pokemon.maxhp / 3) {
        return this.chainModify(0.5);
      }
    },
    onModifySpePriority: 5,
    onModifySpe(spe, pokemon) {
      if (pokemon.hp <= pokemon.maxhp / 3) {
        return this.chainModify(2.0);
      }
    },
    flags: {},
    name: "Defeatist",
    rating: -1,
    num: 129
  },
  defiant: {
    onAfterEachBoost(boost, target, source, effect) {
      if (!source || target.isAlly(source)) {
        return;
      }
      let statsLowered = false;
      let i;
      for (i in boost) {
        if (boost[i] < 0) {
          statsLowered = true;
        }
      }
      if (statsLowered) {
        this.boost({ atk: 2 }, target, target, null, false, true);
      }
    },
    flags: {},
    name: "Defiant",
    rating: 3,
    num: 128
  },
  deltastream: {
    onStart(source) {
      this.field.setWeather("deltastream");
    },
    onAnySetWeather(target, source, weather) {
      const strongWeathers = ["desolateland", "primordialsea", "deltastream"];
      if (this.field.getWeather().id === "deltastream" && !strongWeathers.includes(weather.id))
        return false;
    },
    onEnd(pokemon) {
      if (this.field.weatherState.source !== pokemon)
        return;
      for (const target of this.getAllActive()) {
        if (target === pokemon)
          continue;
        if (target.hasAbility("deltastream")) {
          this.field.weatherState.source = target;
          return;
        }
      }
      this.field.clearWeather();
    },
    flags: {},
    name: "Delta Stream",
    rating: 4,
    num: 191
  },
  desolateland: {
    onStart(source) {
      this.field.setWeather("desolateland");
    },
    onAnySetWeather(target, source, weather) {
      const strongWeathers = ["desolateland", "primordialsea", "deltastream"];
      if (this.field.getWeather().id === "desolateland" && !strongWeathers.includes(weather.id))
        return false;
    },
    onEnd(pokemon) {
      if (this.field.weatherState.source !== pokemon)
        return;
      for (const target of this.getAllActive()) {
        if (target === pokemon)
          continue;
        if (target.hasAbility("desolateland")) {
          this.field.weatherState.source = target;
          return;
        }
      }
      this.field.clearWeather();
    },
    flags: {},
    name: "Desolate Land",
    rating: 4.5,
    num: 190
  },
  disguise: {
    onDamagePriority: 1,
    onDamage(damage, target, source, effect) {
      if (effect?.effectType === "Move" && ["mimikyu", "mimikyutotem"].includes(target.species.id)) {
        this.add("-activate", target, "ability: Disguise");
        this.effectState.busted = true;
        return 0;
      }
    },
    onCriticalHit(target, source, move) {
      if (!target)
        return;
      if (!["mimikyu", "mimikyutotem"].includes(target.species.id)) {
        return;
      }
      const hitSub = target.volatiles["substitute"] && !move.flags["bypasssub"] && !(move.infiltrates && this.gen >= 6);
      if (hitSub)
        return;
      if (!target.runImmunity(move.type))
        return;
      return false;
    },
    onEffectiveness(typeMod, target, type, move) {
      if (!target || move.category === "Status")
        return;
      if (!["mimikyu", "mimikyutotem"].includes(target.species.id)) {
        return;
      }
      const hitSub = target.volatiles["substitute"] && !move.flags["bypasssub"] && !(move.infiltrates && this.gen >= 6);
      if (hitSub)
        return;
      if (!target.runImmunity(move.type))
        return;
      return 0;
    },
    onUpdate(pokemon) {
      if (["mimikyu", "mimikyutotem"].includes(pokemon.species.id) && this.effectState.busted) {
        const speciesid = pokemon.species.id === "mimikyutotem" ? "Mimikyu-Busted-Totem" : "Mimikyu-Busted";
        pokemon.formeChange(speciesid, this.effect, true);
        this.damage(pokemon.baseMaxhp / 8, pokemon, pokemon, this.dex.species.get(speciesid));
      }
    },
	onAfterMove(source, target, move) {
    if (move.id === 'letssnuggleforever' && source.hasAbility('disguise') && this.effectState.busted) {
      this.add('-activate', source, 'ability: Disguise', 'reset');
      this.effectState.busted = false; // Reset the busted state
      const speciesid = source.species.id === "mimikyutotem" ? "Mimikyu" : "Mimikyu"; // Reset to original form
      source.formeChange(speciesid, this.effect, true); // Forme change back to original
    }
  },
    flags: {
      failroleplay: 1,
      noreceiver: 1,
      noentrain: 1,
      notrace: 1,
      failskillswap: 1,
      cantsuppress: 1,
      breakable: 1,
      notransform: 1
    },
    name: "Disguise",
    rating: 3.5,
    num: 209
  },
  download: {
    onStart(pokemon) {
      let totaldef = 0;
      let totalspd = 0;
      for (const target of pokemon.foes()) {
        totaldef += target.getStat("def", false, true);
        totalspd += target.getStat("spd", false, true);
      }
      if (totaldef && totaldef >= totalspd) {
        this.boost({ spa: 1 });
      } else if (totalspd) {
        this.boost({ atk: 1 });
      }
    },
    flags: {},
    name: "Download",
    rating: 3.5,
    num: 88
  },
  dragonsmaw: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Dragon") {
        this.debug("Dragon's Maw boost");
        return this.chainModify(1.5);
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, attacker, defender, move) {
      if (move.type === "Dragon") {
        this.debug("Dragon's Maw boost");
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Dragon's Maw",
    rating: 3.5,
    num: 263
  },
  drizzle: {
    onStart(source) {
      for (const action of this.queue) {
        if (action.choice === "runPrimal" && action.pokemon === source && source.species.id === "kyogre")
          return;
        if (action.choice !== "runSwitch" && action.choice !== "runPrimal")
          break;
      }
      this.field.setWeather("raindance");
    },
    flags: {},
    name: "Drizzle",
    rating: 4,
    num: 2
  },
  drought: {
    onStart(source) {
      for (const action of this.queue) {
        if (action.choice === "runPrimal" && action.pokemon === source && source.species.id === "groudon")
          return;
        if (action.choice !== "runSwitch" && action.choice !== "runPrimal")
          break;
      }
      this.field.setWeather("sunnyday");
    },
    flags: {},
    name: "Drought",
    rating: 4,
    num: 70
  },
  dryskin: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Water") {
        if (!this.heal(target.baseMaxhp / 4)) {
          this.add("-immune", target, "[from] ability: Dry Skin");
        }
        return null;
      }
    },
    onSourceBasePowerPriority: 17,
    onSourceBasePower(basePower, attacker, defender, move) {
      if (move.type === "Fire") {
        return this.chainModify(1.25);
      }
    },
    onWeather(target, source, effect) {
      if (target.hasItem("utilityumbrella"))
        return;
      if (effect.id === "raindance" || effect.id === "primordialsea") {
        this.heal(target.baseMaxhp / 8);
      } else if (effect.id === "sunnyday" || effect.id === "desolateland") {
        this.damage(target.baseMaxhp / 8, target, target);
      }
    },
    flags: { breakable: 1 },
    name: "Dry Skin",
    rating: 3,
    num: 87
  },
  earlybird: {
    flags: {},
    name: "Early Bird",
    // Implemented in statuses.js
    rating: 1.5,
    num: 48
  },
  eartheater: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Ground") {
        if (!this.heal(target.baseMaxhp / 4)) {
          this.add("-immune", target, "[from] ability: Earth Eater");
        }
        return null;
      }
    },
    onStart(pokemon) {
      if (pokemon.side.removeSideCondition('spikes')) {
        this.add('-sideend', pokemon.side, 'Spikes', '[from] ability: Earth Eater', '[of] ' + pokemon);
      }
    },
    onDamage(damage, target, source, effect) {
      if (effect && effect.id === 'spikes') {
        this.add('-immune', target, "[from] ability: Earth Eater");
        return false;
      }
    },
    flags: { breakable: 1 },
    name: "Earth Eater",
    rating: 3.5,
    num: 297,
  },
  effectspore: {
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target) && !source.status && source.runStatusImmunity("powder")) {
        const r = this.random(100);
        if (r < 11) {
          source.setStatus("slp", target);
        } else if (r < 21) {
          source.setStatus("par", target);
        } else if (r < 30) {
          source.setStatus("psn", target);
        }
      }
    },
    flags: {},
    name: "Effect Spore",
    rating: 2,
    num: 27
  },
  electricsurge: {
    onStart(source) {
      this.field.setTerrain("electricterrain");
    },
    flags: {},
    name: "Electric Surge",
    rating: 4,
    num: 226
  },
	electromorphosis: {
    onDamagingHitOrder: 1,
    onDamagingHit(damage, target, source, move) {
      // Check if the target doesn't already have the 'charge' volatile
      if (!target.volatiles['charge']) {
        target.addVolatile('charge');
        // Boost Special Defense by 1 stage
        this.boost({spd: 1}, target);
      }
    },
    flags: {},
    name: "Electromorphosis",
    rating: 3,
    num: 280,
  },
  embodyaspectcornerstone: {
    onStart(pokemon) {
      if (pokemon.baseSpecies.name === "Ogerpon-Cornerstone-Tera" && !this.effectState.embodied) {
        this.effectState.embodied = true;
        this.boost({ def: 1 }, pokemon);
      }
    },
    onSwitchIn() {
      delete this.effectState.embodied;
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
    name: "Embody Aspect (Cornerstone)",
    rating: 3.5,
    num: 304
  },
  embodyaspecthearthflame: {
    onStart(pokemon) {
      if (pokemon.baseSpecies.name === "Ogerpon-Hearthflame-Tera" && !this.effectState.embodied) {
        this.effectState.embodied = true;
        this.boost({ atk: 1 }, pokemon);
      }
    },
    onSwitchIn() {
      delete this.effectState.embodied;
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
    name: "Embody Aspect (Hearthflame)",
    rating: 3.5,
    num: 303
  },
  embodyaspectteal: {
    onStart(pokemon) {
      if (pokemon.baseSpecies.name === "Ogerpon-Teal-Tera" && !this.effectState.embodied) {
        this.effectState.embodied = true;
        this.boost({ spe: 1 }, pokemon);
      }
    },
    onSwitchIn() {
      delete this.effectState.embodied;
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
    name: "Embody Aspect (Teal)",
    rating: 3.5,
    num: 301
  },
  embodyaspectwellspring: {
    onStart(pokemon) {
      if (pokemon.baseSpecies.name === "Ogerpon-Wellspring-Tera" && !this.effectState.embodied) {
        this.effectState.embodied = true;
        this.boost({ spd: 1 }, pokemon);
      }
    },
    onSwitchIn() {
      delete this.effectState.embodied;
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
    name: "Embody Aspect (Wellspring)",
    rating: 3.5,
    num: 302
  },
  emergencyexit: {
    onEmergencyExit(target) {
      if (!this.canSwitch(target.side) || target.forceSwitchFlag || target.switchFlag)
        return;
      for (const side of this.sides) {
        for (const active of side.active) {
          active.switchFlag = false;
        }
      }
      target.emergencyExitFlag = true;  // Set a custom flag to indicate the switch should happen later
      this.add("-activate", target, "ability: Emergency Exit");
    },
    onResidualOrder: 27, // Ensure this runs at the end of the turn
    onResidual(target) {
      if (target.emergencyExitFlag) {
        target.switchFlag = true;
        target.emergencyExitFlag = false;  // Clear the custom flag after the switch
      }
    },
    flags: {},
    name: "Emergency Exit",
    rating: 1,
    num: 194,
},
  fairyaura: {
    onStart(pokemon) {
      if (this.suppressingAbility(pokemon))
        return;
      this.add("-ability", pokemon, "Fairy Aura");
    },
    onAnyBasePowerPriority: 20,
    onAnyBasePower(basePower, source, target, move) {
      if (target === source || move.category === "Status" || move.type !== "Fairy")
        return;
      if (!move.auraBooster?.hasAbility("Fairy Aura"))
        move.auraBooster = this.effectState.target;
      if (move.auraBooster !== this.effectState.target)
        return;
      return this.chainModify([move.hasAuraBreak ? 3072 : 5448, 4096]);
    },
    flags: {},
    name: "Fairy Aura",
    rating: 3,
    num: 187
  },
  filter: {
    onSourceModifyDamage(damage, source, target, move) {
      if (target.getMoveHitData(move).typeMod > 0) {
        this.debug("Filter neutralize");
        return this.chainModify(0.75);
      }
    },
    flags: { breakable: 1 },
    name: "Filter",
    rating: 3,
    num: 111
  },
  flamebody: {
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target)) {
        if (this.randomChance(3, 10)) {
          source.trySetStatus("brn", target);
        }
      }
    },
    flags: {},
    name: "Flame Body",
    rating: 2,
    num: 49
  },
  flareboost: {
    onBasePowerPriority: 19,
    onBasePower(basePower, attacker, defender, move) {
      if (attacker.status === "brn" && move.category === "Special") {
        return this.chainModify(1.5);
      }
    },
	onDamage(damage, target, source, effect) {
    if (effect.id === 'brn') {
      return false;
    }
  },
    flags: {},
    name: "Flare Boost",
    rating: 2,
    num: 138
  },
  flashfire: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Fire") {
        move.accuracy = true;
        if (!target.addVolatile("flashfire")) {
          this.add("-immune", target, "[from] ability: Flash Fire");
        }
        return null;
      }
    },
    onEnd(pokemon) {
      pokemon.removeVolatile("flashfire");
    },
    condition: {
      noCopy: true,
      // doesn't get copied by Baton Pass
      onStart(target) {
        this.add("-start", target, "ability: Flash Fire");
      },
      onModifyAtkPriority: 5,
      onModifyAtk(atk, attacker, defender, move) {
        if (move.type === "Fire" && attacker.hasAbility("flashfire")) {
          this.debug("Flash Fire boost");
          return this.chainModify(1.5);
        }
      },
      onModifySpAPriority: 5,
      onModifySpA(atk, attacker, defender, move) {
        if (move.type === "Fire" && attacker.hasAbility("flashfire")) {
          this.debug("Flash Fire boost");
          return this.chainModify(1.5);
        }
      },
      onEnd(target) {
        this.add("-end", target, "ability: Flash Fire", "[silent]");
      }
    },
    flags: { breakable: 1 },
    name: "Flash Fire",
    rating: 3.5,
    num: 18
  },
  flowergift: {
    onStart(pokemon) {
      this.singleEvent("WeatherChange", this.effect, this.effectState, pokemon);
    },
    onWeatherChange(pokemon) {
      if (!pokemon.isActive || pokemon.baseSpecies.baseSpecies !== "Cherrim" || pokemon.transformed)
        return;
      if (!pokemon.hp)
        return;
      if (["sunnyday", "desolateland"].includes(pokemon.effectiveWeather())) {
        if (pokemon.species.id !== "cherrimsunshine") {
          pokemon.formeChange("Cherrim-Sunshine", this.effect, false, "[msg]");
        }
      } else {
        if (pokemon.species.id === "cherrimsunshine") {
          pokemon.formeChange("Cherrim", this.effect, false, "[msg]");
        }
      }
    },
    onAllyModifyAtkPriority: 3,
    onAllyModifyAtk(atk, pokemon) {
      if (this.effectState.target.baseSpecies.baseSpecies !== "Cherrim")
        return;
      if (["sunnyday", "desolateland"].includes(pokemon.effectiveWeather())) {
        return this.chainModify(1.5);
      }
    },
    onAllyModifySpDPriority: 4,
    onAllyModifySpD(spd, pokemon) {
      if (this.effectState.target.baseSpecies.baseSpecies !== "Cherrim")
        return;
      if (["sunnyday", "desolateland"].includes(pokemon.effectiveWeather())) {
        return this.chainModify(1.5);
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, breakable: 1 },
    name: "Flower Gift",
    rating: 1,
    num: 122
  },
  flowerveil: {
    onAllyTryBoost(boost, target, source, effect) {
      if (source && target === source || !target.hasType("Grass"))
        return;
      let showMsg = false;
      let i;
      for (i in boost) {
        if (boost[i] < 0) {
          delete boost[i];
          showMsg = true;
        }
      }
      if (showMsg && !effect.secondaries) {
        const effectHolder = this.effectState.target;
        this.add("-block", target, "ability: Flower Veil", "[of] " + effectHolder);
      }
    },
    onAllySetStatus(status, target, source, effect) {
      if (target.hasType("Grass") && source && target !== source && effect && effect.id !== "yawn") {
        this.debug("interrupting setStatus with Flower Veil");
        if (effect.name === "Synchronize" || effect.effectType === "Move" && !effect.secondaries) {
          const effectHolder = this.effectState.target;
          this.add("-block", target, "ability: Flower Veil", "[of] " + effectHolder);
        }
        return null;
      }
    },
    onAllyTryAddVolatile(status, target) {
      if (target.hasType("Grass") && status.id === "yawn") {
        this.debug("Flower Veil blocking yawn");
        const effectHolder = this.effectState.target;
        this.add("-block", target, "ability: Flower Veil", "[of] " + effectHolder);
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Flower Veil",
    rating: 0,
    num: 166
  },
  fluffy: {
    onSourceModifyDamage(damage, source, target, move) {
      let mod = 1;
      if (move.type === "Fire")
        mod *= 2;
      if (move.flags["contact"])
        mod /= 2;
      return this.chainModify(mod);
    },
    flags: { breakable: 1 },
    name: "Fluffy",
    rating: 3.5,
    num: 218
  },
  forecast: {
    onStart(pokemon) {
      this.singleEvent("WeatherChange", this.effect, this.effectState, pokemon);
	  if (pokemon.hasItem('damprock')) {
		this.field.setWeather('raindance');
		} else if (pokemon.hasItem('heatrock')) {
		this.field.setWeather('sunnyday');
		} else if (pokemon.hasItem('smoothrock')) {
		this.field.setWeather('sandstorm');
		} else if (pokemon.hasItem('icyrock')) {
		this.field.setWeather('snow');
		}
    },
    onWeatherChange(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Castform" || pokemon.transformed)
        return;
      let forme = null;
      switch (pokemon.effectiveWeather()) {
        case "sunnyday":
        case "desolateland":
          if (pokemon.species.id !== "castformsunny")
            forme = "Castform-Sunny";
          break;
        case "raindance":
        case "primordialsea":
          if (pokemon.species.id !== "castformrainy")
            forme = "Castform-Rainy";
          break;
        case "hail":
        case "snow":
          if (pokemon.species.id !== "castformsnowy")
            forme = "Castform-Snowy";
          break;
        default:
          if (pokemon.species.id !== "castform")
            forme = "Castform";
          break;
      }
      if (pokemon.isActive && forme) {
        pokemon.formeChange(forme, this.effect, false, "[msg]");
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
    name: "Forecast",
    rating: 2,
    num: 59
  },
	forewarn: {
    onStart(pokemon) {
      let warnMoves = [];
      let warnBp = 1;
      for (const target of pokemon.foes()) {
        for (const moveSlot of target.moveSlots) {
          const move = this.dex.moves.get(moveSlot.move);
          let bp = move.basePower;
          if (move.ohko) bp = 150;
          if (move.id === 'counter' || move.id === 'metalburst' || move.id === 'mirrorcoat') bp = 120;
          if (bp === 1) bp = 80;
          if (!bp && move.category !== 'Status') bp = 80;
          if (bp > warnBp) {
            warnMoves = [[move, target]];
            warnBp = bp;
          } else if (bp === warnBp) {
            warnMoves.push([move, target]);
          }
        }
      }
      if (!warnMoves.length) return;
      const [warnMove, warnTarget] = this.sample(warnMoves);
      this.add('-activate', pokemon, 'ability: Forewarn', warnMove, '[of] ' + warnTarget);

      // Disable the Forewarned move for 1 turn
      if (!warnTarget.volatiles['disable']) {
              warnTarget.addVolatile('forewarn', pokemon, { move: warnMove.id, duration: 1 });
            }
          },

      condition: {
        duration: 1,
        onStart(target, source, effect) {
          // Store the move to disable using the effect data
          this.effectState.move = effect.move;
          this.add('-start', target, 'Disable', this.dex.moves.get(effect.move).name, '[from] ability: Forewarn', '[of] ' + source);
        },
        onDisableMove(pokemon) {
          if (pokemon.volatiles['forewarn']?.move) {
            pokemon.disableMove(this.effectState.move);
          }
        },
        onBeforeMove(attacker, defender, move) {
          if (move.id === this.effectState.move) {
            this.add('cant', attacker, 'Disable', move);
            return false;
          }
        },
        onEnd(pokemon) {
          this.add('-end', pokemon, 'Disable');
        },
      },
    // The volatile's effect handlers must be defined elsewhere (e.g., in conditions.ts)
    flags: {},
    name: "Forewarn",
    rating: 0.5,
    num: 108,
  },
  friendguard: {
    onAnyModifyDamage(damage, source, target, move) {
      if (target !== this.effectState.target && target.isAlly(this.effectState.target)) {
        this.debug("Friend Guard weaken");
        return this.chainModify(0.75);
      }
    },
    flags: { breakable: 1 },
    name: "Friend Guard",
    rating: 0,
    num: 132
  },
	frisk: {
    onStart(pokemon) {
        for (const target of pokemon.foes()) {
            if (target.item) {
                this.add('-item', target, target.getItem().name, '[from] ability: Frisk', '[of] ' + pokemon);
            }
            // Add embargo for 1 turn to all foes
            if (!target.volatiles['embargo']) {
				    this.add("-activate", pokemon, "ability: Frisk");
                target.addVolatile('embargo');
                target.volatiles['friskEmbargo'] = true; // Mark that this is Frisk-induced embargo
            }
        }
    },
    onResidualOrder: 20,
    onResidual(pokemon) {
        // Check all opponents and end after 1 turn if it's from Frisk
        for (const target of pokemon.foes()) {
            if (target.volatiles['friskEmbargo'] && target.volatiles['friskEmbargo']) {
                target.removeVolatile('embargo');
                delete target.volatiles['friskEmbargo'];
                this.add('-end', target, 'Embargo', '[from] ability: Frisk', '[silent]');
            }
        }
    },
    flags: {},
    name: "Frisk",
    rating: 1.5,
    num: 119,
},
  fullmetalbody: {
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      let showMsg = false;
      let i;
      for (i in boost) {
        if (boost[i] < 0) {
          delete boost[i];
          showMsg = true;
        }
      }
      if (showMsg && !effect.secondaries && effect.id !== "octolock") {
        this.add("-fail", target, "unboost", "[from] ability: Full Metal Body", "[of] " + target);
      }
    },
    flags: {},
    name: "Full Metal Body",
    rating: 2,
    num: 230
  },
  furcoat: {
    onModifyDefPriority: 6,
    onModifyDef(def) {
      return this.chainModify(2);
    },
    flags: { breakable: 1 },
    name: "Fur Coat",
    rating: 4,
    num: 169
  },
  galewings: {
    onModifyPriority(priority, pokemon, target, move) {
      if (move?.type === "Flying" && pokemon.hp >= pokemon.maxhp / 2)
        return priority + 1;
    },
    flags: {},
    name: "Gale Wings",
    rating: 1.5,
    num: 177
  },
  galvanize: {
    onModifyTypePriority: -1,
    onModifyType(move, pokemon) {
      const noModifyType = [
        "judgment",
        "multiattack",
        "naturalgift",
        "revelationdance",
        "technoblast",
        "terrainpulse",
        "weatherball"
      ];
      if (move.type === "Normal" && !noModifyType.includes(move.id) && !(move.isZ && move.category !== "Status") && !(move.name === "Tera Blast" && pokemon.terastallized)) {
        move.type = "Electric";
        move.typeChangerBoosted = this.effect;
      }
    },
    onBasePowerPriority: 23,
    onBasePower(basePower, pokemon, target, move) {
      if (move.typeChangerBoosted === this.effect)
        return this.chainModify([4915, 4096]);
    },
    flags: {},
    name: "Galvanize",
    rating: 4,
    num: 206
  },
  gluttony: {
    onStart(pokemon) {
      pokemon.abilityState.gluttony = true;
    },
    onDamage(item, pokemon) {
      pokemon.abilityState.gluttony = true;
    },
    flags: {},
    name: "Gluttony",
    rating: 1.5,
    num: 82
  },
  goodasgold: {
    onTryHit(target, source, move) {
      if (move.category === "Status" && target !== source) {
        this.add("-immune", target, "[from] ability: Good as Gold");
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Good as Gold",
    rating: 5,
    num: 283
  },
  gooey: {
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target, true)) {
        this.add("-ability", target, "Gooey");
        this.boost({ spe: -1 }, source, target, null, true);
      }
    },
    flags: {},
    name: "Gooey",
    rating: 2,
    num: 183
  },
  gorillatactics: {
    onStart(pokemon) {
      pokemon.abilityState.choiceLock = "";
    },
    onBeforeMove(pokemon, target, move) {
      if (move.isZOrMaxPowered || move.id === "struggle")
        return;
      if (pokemon.abilityState.choiceLock && pokemon.abilityState.choiceLock !== move.id) {
        this.addMove("move", pokemon, move.name);
        this.attrLastMove("[still]");
        this.debug("Disabled by Gorilla Tactics");
        this.add("-fail", pokemon);
        return false;
      }
    },
    onModifyMove(move, pokemon) {
      if (pokemon.abilityState.choiceLock || move.isZOrMaxPowered || move.id === "struggle")
        return;
      pokemon.abilityState.choiceLock = move.id;
    },
    onModifyAtkPriority: 1,
    onModifyAtk(atk, pokemon) {
      if (pokemon.volatiles["dynamax"])
        return;
      this.debug("Gorilla Tactics Atk Boost");
      return this.chainModify(1.5);
    },
    onDisableMove(pokemon) {
      if (!pokemon.abilityState.choiceLock)
        return;
      if (pokemon.volatiles["dynamax"])
        return;
      for (const moveSlot of pokemon.moveSlots) {
        if (moveSlot.id !== pokemon.abilityState.choiceLock) {
          pokemon.disableMove(moveSlot.id, false, this.effectState.sourceEffect);
        }
      }
    },
    onEnd(pokemon) {
      pokemon.abilityState.choiceLock = "";
    },
    flags: {},
    name: "Gorilla Tactics",
    rating: 4.5,
    num: 255
  },
  grasspelt: {
    onModifyDefPriority: 6,
    onModifyDef(pokemon) {
      if (this.field.isTerrain("grassyterrain"))
        return this.chainModify(2);
    },
    flags: { breakable: 1 },
    name: "Grass Pelt",
    rating: 0.5,
    num: 179
  },
  grassysurge: {
    onStart(source) {
      this.field.setTerrain("grassyterrain");
    },
    flags: {},
    name: "Grassy Surge",
    rating: 4,
    num: 229
  },
  grimneigh: {
    onSourceAfterFaint(length, target, source, effect) {
      if (effect && effect.effectType === "Move") {
        this.boost({ spa: length }, source);
      }
    },
    flags: {},
    name: "Grim Neigh",
    rating: 3,
    num: 265
  },
  guarddog: {
    onDragOutPriority: 1,
    onDragOut(pokemon) {
      this.add("-activate", pokemon, "ability: Guard Dog");
      return null;
    },
    onTryBoost(boost, target, source, effect) {
      if (effect.name === "Intimidate" && boost.atk) {
        delete boost.atk;
        this.boost({ atk: 1 }, target, target, null, false, true);
      }
    },
    flags: { breakable: 1 },
    name: "Guard Dog",
    rating: 2,
    num: 275
  },
  gulpmissile: {
    onDamagingHit(damage, target, source, move) {
      if (!source.hp || !source.isActive || target.isSemiInvulnerable()) return;
      if (["cramorantgulping", "cramorantgorging"].includes(target.species.id)) {
        this.damage(source.baseMaxhp / 4, source, target);
        if (target.species.id === "cramorantgulping") {
          this.boost({ def: -1 }, source, target, null, true);
        } else {
          source.trySetStatus("par", target, move);
        }
        target.formeChange("cramorant", move);
      }
    },
    onSourceTryPrimaryHit(target, source, effect) {
      if (
        source.hasAbility("gulpmissile") &&
        source.species.name === "Cramorant" &&
        ["surf", "waterfall", "wavecrash"].includes(effect?.id)
      ) {
        const forme = source.hp <= source.maxhp / 2 ? "cramorantgorging" : "cramorantgulping";
        source.formeChange(forme, effect);
      }
    },
    flags: { cantsuppress: 1, notransform: 1 },
    name: "Gulp Missile",
    rating: 2.5,
    num: 241
  },
  guts: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, pokemon) {
      if (pokemon.status) {
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Guts",
    rating: 3.5,
    num: 62
  },
  hadronengine: {
    onStart(pokemon) {
      if (!this.field.setTerrain("electricterrain") && this.field.isTerrain("electricterrain")) {
        this.add("-activate", pokemon, "ability: Hadron Engine");
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, attacker, defender, move) {
      if (this.field.isTerrain("electricterrain")) {
        this.debug("Hadron Engine boost");
        return this.chainModify([5461, 4096]);
      }
    },
    flags: {},
    name: "Hadron Engine",
    rating: 4.5,
    num: 289
  },
  harvest: {
    onResidualOrder: 28,
    onResidualSubOrder: 2,
    onResidual(pokemon) {
      if (this.field.isWeather(["sunnyday", "desolateland"]) || this.randomChance(1, 2)) {
        if (pokemon.hp && !pokemon.item && this.dex.items.get(pokemon.lastItem).isBerry) {
          pokemon.setItem(pokemon.lastItem);
          pokemon.lastItem = "";
          this.add("-item", pokemon, pokemon.getItem(), "[from] ability: Harvest");
        }
      }
    },
    flags: {},
    name: "Harvest",
    rating: 2.5,
    num: 139
  },
  healer: {
    onResidualOrder: 5,
    onResidualSubOrder: 3,
    onResidual(pokemon) {
      for (const allyActive of pokemon.adjacentAllies()) {
        if (allyActive.status && this.randomChance(3, 10)) {
          this.add("-activate", pokemon, "ability: Healer");
          allyActive.cureStatus();
        }
      }
    },
	onAfterMoveSecondarySelf(pokemon, target, move) {
            if (move.flags['heal'] && move.id !== 'rest') {
                if (pokemon.status) {
                    pokemon.cureStatus();
                    this.add('-curestatus', pokemon, pokemon.status, '[from] ability: Healer');
                }
            }
        },
    flags: {},
    name: "Healer",
    rating: 0,
    num: 131
  },
  heatproof: {
    onSourceModifyAtkPriority: 6,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Fire") {
        this.debug("Heatproof Atk weaken");
        return this.chainModify(0.5);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Fire") {
        this.debug("Heatproof SpA weaken");
        return this.chainModify(0.5);
      }
    },
    onUpdate(pokemon) {
      if (pokemon.status === "brn") {
        this.add("-activate", pokemon, "ability: Heatproof");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (status.id !== "brn")
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Heatproof");
      }
      return false;
    },
    flags: { breakable: 1 },
    name: "Heatproof",
    rating: 2,
    num: 85
  },
  heavymetal: {
    onModifyWeightPriority: 1,
    onModifyWeight(weighthg) {
      return weighthg * 2;
    },
	onSourceModifyAtkPriority: 5,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Ground") {
        return this.chainModify(0.75);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Ground") {
        return this.chainModify(0.75);
      }
    },
    flags: { breakable: 1 },
    name: "Heavy Metal",
    rating: 0,
    num: 134
  },
  honeygather: {
	  onTryHit(target, source, move) {
		  if (target !== source && move.flags["powder"]) {
			  if (!this.heal(target.baseMaxhp / 4)) {
				  this.add("-immune", target, "[from] ability: Honey Gather");
			  }
			  return null;
		  }
	  },
	  onImmunity(type, pokemon) {
		  if (type === "powder")
			  return false;
	  },
    flags: {},
    name: "Honey Gather",
    rating: 0,
    num: 118
  },
  hospitality: {
    onStart(pokemon) {
      for (const ally of pokemon.adjacentAllies()) {
        this.heal(ally.baseMaxhp / 4, ally, pokemon);
      }
    },
    flags: {},
    name: "Hospitality",
    rating: 0,
    num: 299
  },
  hugepower: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk) {
      return this.chainModify(2);
    },
    flags: {},
    name: "Huge Power",
    rating: 5,
    num: 37
  },
  hungerswitch: {
    onResidualOrder: 29,
    onResidual(pokemon) {
      if (pokemon.species.baseSpecies !== "Morpeko" || pokemon.terastallized)
        return;
      const targetForme = pokemon.species.name === "Morpeko" ? "Morpeko-Hangry" : "Morpeko";
      pokemon.formeChange(targetForme);
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
    name: "Hunger Switch",
    rating: 1,
    num: 258
  },
  hustle: {
    // This should be applied directly to the stat as opposed to chaining with the others
    onModifyAtkPriority: 5,
    onModifyAtk(atk) {
      return this.modify(atk, 1.5);
    },
    onSourceModifyAccuracyPriority: -1,
    onSourceModifyAccuracy(accuracy, target, source, move) {
      if (move.category === "Physical" && typeof accuracy === "number") {
        return this.chainModify([3277, 4096]);
      }
    },
    flags: {},
    name: "Hustle",
    rating: 3.5,
    num: 55
  },
  hydration: {
    onResidualOrder: 5,
    onResidualSubOrder: 3,
    onResidual(pokemon) {
      if (pokemon.status && ["raindance", "primordialsea"].includes(pokemon.effectiveWeather())) {
        this.debug("hydration");
        this.add("-activate", pokemon, "ability: Hydration");
        pokemon.cureStatus();
      }
    },
    onDamage(damage, target, source, effect) {
      if (effect.effectType !== "Move" && this.field.isWeather(["raindance", "primordialsea"])) {
        if (effect.effectType === "Ability" && this.field.isWeather(["raindance", "primordialsea"]))
          this.add("-activate", source, "ability: " + effect.name);
        return false;
      }
    },
    flags: {},
    name: "Hydration",
    rating: 1.5,
    num: 93
  },
  hypercutter: {
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      if (boost.atk && boost.atk < 0) {
        delete boost.atk;
        if (!effect.secondaries) {
          this.add("-fail", target, "unboost", "Attack", "[from] ability: Hyper Cutter", "[of] " + target);
        }
      }
    },
    onModifyCritRatio(critRatio, attacker, defender, move) {
      if (move.flags["slicing"])
        return 2;
    },
    flags: { breakable: 1 },
    name: "Hyper Cutter",
    rating: 1.5,
    num: 52
  },
  icebody: {
    onWeather(target, source, effect) {
      if (effect.id === "hail" || effect.id === "snow") {
        this.heal(target.baseMaxhp / 8);
      }
    },
    onImmunity(type, pokemon) {
      if (type === "hail")
        return false;
    },
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target)) {
        if (this.randomChance(3, 10)) {
          source.trySetStatus("frz", target);
        }
      }
    },
    flags: {},
    name: "Ice Body",
    rating: 1,
    num: 115
  },
  iceface: {
    onStart(pokemon) {
      if (this.field.isWeather(["hail", "snow"]) && pokemon.species.id === "eiscuenoice") {
        this.add("-activate", pokemon, "ability: Ice Face");
        this.effectState.busted = false;
        pokemon.formeChange("Eiscue", this.effect, true);
      }
    },
    onDamagePriority: 1,
    onDamage(damage, target, source, effect) {
      if (effect?.effectType === "Move" && effect.category === "Physical" && target.species.id === "eiscue") {
        this.add("-activate", target, "ability: Ice Face");
        this.effectState.busted = true;
        return 0;
      }
    },
    onCriticalHit(target, type, move) {
      if (!target)
        return;
      if (move.category !== "Physical" || target.species.id !== "eiscue")
        return;
      if (target.volatiles["substitute"] && !(move.flags["bypasssub"] || move.infiltrates))
        return;
      if (!target.runImmunity(move.type))
        return;
      return false;
    },
    onEffectiveness(typeMod, target, type, move) {
      if (!target)
        return;
      if (move.category !== "Physical" || target.species.id !== "eiscue")
        return;
      const hitSub = target.volatiles["substitute"] && !move.flags["bypasssub"] && !(move.infiltrates && this.gen >= 6);
      if (hitSub)
        return;
      if (!target.runImmunity(move.type))
        return;
      return 0;
    },
    onUpdate(pokemon) {
      if (pokemon.species.id === "eiscue" && this.effectState.busted) {
        pokemon.formeChange("Eiscue-Noice", this.effect, true);
      }
    },
    onWeatherChange(pokemon, source, sourceEffect) {
      if (sourceEffect?.suppressWeather)
        return;
      if (!pokemon.hp)
        return;
      if (this.field.isWeather(["hail", "snow"]) && pokemon.species.id === "eiscuenoice") {
        this.add("-activate", pokemon, "ability: Ice Face");
        this.effectState.busted = false;
        pokemon.formeChange("Eiscue", this.effect, true);
      }
    },
    flags: {
      failroleplay: 1,
      noreceiver: 1,
      noentrain: 1,
      notrace: 1,
      failskillswap: 1,
      cantsuppress: 1,
      breakable: 1,
      notransform: 1
    },
    name: "Ice Face",
    rating: 3,
    num: 248
  },
  icescales: {
    onSourceModifyDamage(damage, source, target, move) {
      if (move.category === "Special") {
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Ice Scales",
    rating: 4,
    num: 246
  },
  illuminate: {
    onStart(pokemon) {
      // note: the resource pack for this is temporary, there's some targeting screwery that's going on so unfortunately it's a little vague for now
      for (const target of pokemon.adjacentFoes()) {
        if (!target.volatiles['illuminate']) {
          target.addVolatile('illuminate', pokemon);
        }
      }
    },
    onUpdate(pokemon) {
      const affectedFoes = new Set(pokemon.adjacentFoes());
      for (const foe of pokemon.side.foe.active) {
        if (affectedFoes.has(foe)) {
          if (!foe.volatiles['illuminate']) {
            foe.addVolatile('illuminate', pokemon);
          }
        } else if (foe.volatiles['illuminate']) {
          foe.removeVolatile('illuminate');
        }
      }
    },
    onEnd(pokemon) {
      for (const foe of pokemon.side.foe.active) {
        if (foe.volatiles['illuminate']) {
          foe.removeVolatile('illuminate');
        }
      }
    },
    condition: {
      onStart(target, source) {
        this.add('-start', target, 'Illuminate', '[from] ability: Illuminate', '[of] ' + source);
      },
      onModifyEvasion(evasion) {
        return this.chainModify(0.75);
      },
      onEnd(target) {
        this.add('-end', target, 'Illuminate');
      },
    },
    flags: {breakable: 1},
    name: "Illuminate",
    rating: 0.5,
    num: 35,
  },
  illusion: {
  onBeforeSwitchIn(pokemon) {
    pokemon.illusion = null;
    for (let i = pokemon.side.pokemon.length - 1; i > pokemon.position; i--) {
      const possibleTarget = pokemon.side.pokemon[i];
      if (!possibleTarget.fainted) {
        if (!pokemon.terastallized || possibleTarget.species.baseSpecies !== "Ogerpon") {
          pokemon.illusion = possibleTarget;
        }
        break;
      }
    }
  },
  onModifyDamage(damage, source, target, move) {
    if (source.illusion) {
      return this.chainModify(1.3);
    }
  },
  onDamagingHit(damage, target, source, move) {
    if (target.illusion) {
      this.singleEvent("End", this.dex.abilities.get("Illusion"), target.abilityState, target, source, move);
    }
  },
  onEnd(pokemon) {
    if (pokemon.illusion) {
      this.debug("illusion cleared");
      pokemon.illusion = null;
      const details = pokemon.species.name + (pokemon.level === 100 ? "" : ", L" + pokemon.level) + (pokemon.gender === "" ? "" : ", " + pokemon.gender) + (pokemon.set.shiny ? ", shiny" : "");
      this.add("replace", pokemon, details);
      this.add("-end", pokemon, "Illusion");
      if (this.ruleTable.has("illusionlevelmod")) {
        this.hint("Illusion Level Mod is active, so this Pok\xE9mon's true level was hidden.", true);
      }
    }
  },
  onFaint(pokemon) {
    pokemon.illusion = null;
  },
  flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1 },
  name: "Illusion",
  rating: 4.5,
  num: 149
},
  immunity: {
    onUpdate(pokemon) {
      if (pokemon.status === "psn" || pokemon.status === "tox") {
        this.add("-activate", pokemon, "ability: Immunity");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (status.id !== "psn" && status.id !== "tox")
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Immunity");
      }
      return false;
    },
    onSourceModifyAtkPriority: 5,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Poison") {
        return this.chainModify(0.5);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Poison") {
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Immunity",
    rating: 2,
    num: 17
  },
  imposter: {
    onSwitchIn(pokemon) {
      this.effectState.switchingIn = true;
    },
    onStart(pokemon) {
      if (!this.effectState.switchingIn)
        return;
      const target = pokemon.side.foe.active[pokemon.side.foe.active.length - 1 - pokemon.position];
      if (target) {
        pokemon.transformInto(target, this.dex.abilities.get("imposter"));
      }
      this.effectState.switchingIn = false;
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
    name: "Imposter",
    rating: 5,
    num: 150
  },
  infiltrator: {
    onModifyMove(move) {
      move.infiltrates = true;
    },
    flags: {},
    name: "Infiltrator",
    rating: 2.5,
    num: 151
  },
  innardsout: {
    onDamagingHitOrder: 1,
    onDamagingHit(damage, target, source, move) {
      if (!target.hp) {
        this.damage(target.getUndynamaxedHP(damage), source, target);
      }
    },
    flags: {},
    name: "Innards Out",
    rating: 4,
    num: 215
  },
  innerfocus: {
    onTryAddVolatile(status, pokemon) {
      if (status.id === "flinch")
        return null;
    },
    onTryBoost(boost, target, source, effect) {
      if (effect.name === "Intimidate" && boost.atk) {
        delete boost.atk;
        this.add("-fail", target, "unboost", "Attack", "[from] ability: Inner Focus", "[of] " + target);
      }
    },
    flags: { breakable: 1 },
    name: "Inner Focus",
    rating: 1,
    num: 39
  },
  insomnia: {
    onUpdate(pokemon) {
      if (pokemon.status === "slp") {
        this.add("-activate", pokemon, "ability: Insomnia");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (status.id !== "slp")
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Insomnia");
      }
      return false;
    },
    onTryAddVolatile(status, target) {
      if (status.id === "yawn") {
        this.add("-immune", target, "[from] ability: Insomnia");
        return null;
      }
    },
    onSourceModifyAtkPriority: 6,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Dark" || move.type === "Ghost") {
        this.debug("Insomnia weaken");
        return this.chainModify(0.5);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Dark" || move.type === "Ghost") {
        this.debug("Insomnia weaken");
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Insomnia",
    rating: 1.5,
    num: 15
  },
  intimidate: {
    onStart(pokemon) {
      let activated = false;
      for (const target of pokemon.adjacentFoes()) {
        if (!activated) {
          this.add("-ability", pokemon, "Intimidate", "boost");
          activated = true;
        }
        if (target.volatiles["substitute"]) {
          this.add("-immune", target);
        } else {
          this.boost({ atk: -1 }, target, pokemon, null, true);
        }
      }
    },
    flags: {},
    name: "Intimidate",
    rating: 3.5,
    num: 22
  },
  intrepidsword: {
    onStart(pokemon) {
      if (pokemon.swordBoost)
        return;
      pokemon.swordBoost = true;
      this.boost({ atk: 1 }, pokemon);
    },
    flags: {},
    name: "Intrepid Sword",
    rating: 4,
    num: 234
  },
  ironbarbs: {
    onDamagingHitOrder: 1,
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target, true)) {
        this.damage(source.baseMaxhp / 8, source, target);
      }
    },
    flags: {},
    name: "Iron Barbs",
    rating: 2.5,
    num: 160
  },
  ironfist: {
    onBasePower(basePower, attacker, defender, move) {
      if (move.flags["punch"]) {
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Iron Fist",
    rating: 3,
    num: 89
  },
  justified: {
    onDamagingHit(damage, target, source, move) {
      if (move.type === "Dark") {
        const betterStat = target.storedStats.atk >= target.storedStats.spa ? 'atk' : 'spa';
        const boost = {};
        boost[betterStat] = 1;
        this.boost(boost);
      }
    },
    onSourceModifyAtkPriority: 5,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Dark") {
        return this.chainModify(0.5);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(spa, attacker, defender, move) {
      if (move.type === "Dark") {
        return this.chainModify(0.5);
      }
    },
    flags: {},
    name: "Justified",
    rating: 2.5,
    num: 154
  },
	keeneye: {
		onTryBoost(boost, target, source, effect) {
			if (source && target === source) return;
			if (boost.accuracy && boost.accuracy < 0) {
				delete boost.accuracy;
				if (!effect.secondaries) {
					this.add('-fail', target, 'unboost', 'accuracy', '[from] ability: Keen Eye', '[of] ' + target);
				}
			}
		},
		onModifyMove(move, source) {
			move.ignoreEvasion = true;

			if (move.type === source.types[0] || (source.types[1] && move.type === source.types[1])) {
				if (typeof move.accuracy === 'number') {
					move.accuracy *= 1.2;
				}
			}
		},
		flags: {breakable: 1},
		name: "Keen Eye",
		rating: 0.5,
		num: 51,
	},
  klutz: {
    // Item suppression implemented in Pokemon.ignoringItem() within sim/pokemon.js
    onStart(pokemon) {
      this.singleEvent("End", pokemon.getItem(), pokemon.itemState, pokemon);
    },
    flags: {},
    name: "Klutz",
    rating: -1,
    num: 103
  },
  leafguard: {
    onSetStatus(status, target, source, effect) {
      if (["sunnyday", "desolateland"].includes(target.effectiveWeather())) {
        if (effect?.status) {
          this.add("-immune", target, "[from] ability: Leaf Guard");
        }
        return false;
      }
    },
    onTryAddVolatile(status, target) {
      if (status.id === "yawn" && ["sunnyday", "desolateland"].includes(target.effectiveWeather())) {
        this.add("-immune", target, "[from] ability: Leaf Guard");
        return null;
      }
    },
    onDamage(damage, target, source, effect) {
      if (effect.effectType !== "Move" && this.field.isWeather(["sunnyday", "desolateland"])) {
        if (effect.effectType === "Ability" && this.field.isWeather(["sunnyday", "desolateland"]))
          this.add("-activate", source, "ability: " + effect.name);
        return false;
      }
    },
    flags: { breakable: 1 },
    name: "Leaf Guard",
    rating: 0.5,
    num: 102
  },
  levitate: {
    // airborneness implemented in sim/pokemon.js:Pokemon#isGrounded
    flags: { breakable: 1 },
    name: "Levitate",
    rating: 3.5,
    num: 26
  },
  libero: {
    onPrepareHit(source, target, move) {
      if (this.effectState.libero)
        return;
      if (move.hasBounced || move.flags["futuremove"] || move.sourceEffect === "snatch")
        return;
      const type = move.type;
      if (type && type !== "???" && source.getTypes().join() !== type) {
        if (!source.setType(type))
          return;
        this.effectState.libero = true;
        this.add("-start", source, "typechange", type, "[from] ability: Libero");
      }
    },
    onSwitchIn() {
      delete this.effectState.libero;
    },
    flags: {},
    name: "Libero",
    rating: 4,
    num: 236
  },
  lightmetal: {
    onModifyWeight(weighthg) {
      return this.trunc(weighthg / 2);
    },
	onSourceModifyAtkPriority: 5,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Fighting") {
        return this.chainModify(0.75);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Fighting") {
        return this.chainModify(0.75);
      }
    },
    flags: { breakable: 1 },
    name: "Light Metal",
    rating: 1,
    num: 135
  },
  lightningrod: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Electric") {
        if (!this.boost({ spa: 1 })) {
          this.add("-immune", target, "[from] ability: Lightning Rod");
        }
        return null;
      }
    },
    onAnyRedirectTarget(target, source, source2, move) {
      if (move.type !== "Electric" || move.flags["pledgecombo"])
        return;
      const redirectTarget = ["randomNormal", "adjacentFoe"].includes(move.target) ? "normal" : move.target;
      if (this.validTarget(this.effectState.target, source, redirectTarget)) {
        if (move.smartTarget)
          move.smartTarget = false;
        if (this.effectState.target !== target) {
          this.add("-activate", this.effectState.target, "ability: Lightning Rod");
        }
        return this.effectState.target;
      }
    },
    flags: { breakable: 1 },
    name: "Lightning Rod",
    rating: 3,
    num: 31
  },
  limber: {
    onUpdate(pokemon) {
      if (pokemon.status === "par") {
        this.add("-activate", pokemon, "ability: Limber");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (status.id !== "par")
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Limber");
      }
      this.boost({ spe: 2 });
      return false;
    },
    flags: { breakable: 1 },
    name: "Limber",
    rating: 2,
    num: 7
  },
  lingeringaroma: {
    onDamagingHit(damage, target, source, move) {
      const sourceAbility = source.getAbility();
      if (sourceAbility.flags["cantsuppress"] || sourceAbility.id === "lingeringaroma") {
        return;
      }
      if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) {
        const oldAbility = source.setAbility("lingeringaroma", target);
        if (oldAbility) {
          this.add("-activate", target, "ability: Lingering Aroma", this.dex.abilities.get(oldAbility).name, "[of] " + source);
        }
      }
    },
	onAnyTryAddVolatile(status, target, source, effect) {
      if (["attract", "disable", "encore", "healblock", "taunt", "torment"].includes(status.id)) {
        if (effect.effectType === "Move") {
          const effectHolder = this.effectState.target;
          this.add("-block", target, "ability: Lingering Aroma", "[of] " + effectHolder);
        }
        return null;
      }
    },
    flags: {},
    name: "Lingering Aroma",
    rating: 2,
    num: 268
  },
  liquidooze: {
    onSourceTryHeal(damage, target, source, effect) {
        this.debug("Heal is occurring: " + target + " <- " + source + " :: " + effect.id);
        const canOoze = ["drain", "leechseed", "strengthsap"];
        if (canOoze.includes(effect.id)) {
          this.damage(damage);
          if (target.status === '' || target.status === 'slp') {
            target.trySetStatus('tox', source);
          }
          return 0;
        }
      },
    flags: {},
    name: "Liquid Ooze",
    rating: 2.5,
    num: 64
  },
  liquidvoice: {
    onModifyTypePriority: -1,
    onModifyType(move, pokemon) {
      if (move.flags["sound"] && !pokemon.volatiles["dynamax"]) {
        move.type = "Water";
      }
    },
    flags: {},
    name: "Liquid Voice",
    rating: 1.5,
    num: 204
  },
	longreach: {
    onModifyMove(move, pokemon) {
    //move.target = 'any';
    //move.flags['distance'] = 1;
    //neither of these actually work even for onStart, it'd require messing with Pokemon#getMoves to get it working which might be a tad out of scope, but leaving it here for now nonetheless
      if (!move.flags['contact']) {
        move.flags['bypasssub'] = 1;
      }
      delete move.flags['contact'];
    },
    flags: {},
    name: "Long Reach",
    rating: 1,
    num: 203,
  },
  magicbounce: {
    onTryHitPriority: 1,
    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 = false;
      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;
    },
    condition: {
      duration: 1
    },
    flags: { breakable: 1 },
    name: "Magic Bounce",
    rating: 4,
    num: 156
  },
  magicguard: {
    onDamage(damage, target, source, effect) {
      if (effect.effectType !== "Move") {
        if (effect.effectType === "Ability")
          this.add("-activate", source, "ability: " + effect.name);
        return false;
      }
    },
    flags: {},
    name: "Magic Guard",
    rating: 4,
    num: 98
  },
	magician: {
    onAfterMoveSecondarySelf(source, target, move) {
   if (source.hasType(move.type) && move.category !== 'Status') {
     if (target.hasAbility('stickyhold')) {
       this.add('-activate', target, 'ability: Sticky Hold');
       return;
     }
   if (source.item && target.item) {
       if (source.hp) {
     const item = target.takeItem();
     if (item) {
       this.add('-enditem', target, item.name, '[from] ability: Magician', '[of] ' + source);
     }
   }
   }
     else if (!source.item && target.item) {
       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; // bypass setItem so we don't break choicelock or anything
       return;
     }
     this.add('-item', source, yourItem, '[from] ability: Magician', '[of] ' + target);
 }
 }
 },
   flags: {},
   name: "Magician",
   rating: 1,
   num: 170
},
  magmaarmor: {
    onUpdate(pokemon) {
      if (pokemon.status === "frz") {
        this.add("-activate", pokemon, "ability: Magma Armor");
        pokemon.cureStatus();
      }
    },
    onImmunity(type, pokemon) {
      if (type === "frz")
        return false;
    },
    onSourceModifyDamage(damage, source, target, move) {
      if (move.type === 'Water' || move.type === 'Ice') {
        return this.chainModify(0.5);
      }
    },
    onHit(target, source, move) {
      if (move.type === 'Water' || move.type === 'Ice') {
        this.boost({ def: 1, spd: 1, spe: -2 }, target);
      }
    },
    flags: { breakable: 1 },
    name: "Magma Armor",
    rating: 0.5,
    num: 40
  },
  magnetpull: {
    onFoeTrapPokemon(pokemon) {
      if (pokemon.hasType("Steel") && pokemon.isAdjacent(this.effectState.target)) {
        pokemon.tryTrap(true);
      }
    },
    onFoeMaybeTrapPokemon(pokemon, source) {
      if (!source)
        source = this.effectState.target;
      if (!source || !pokemon.isAdjacent(source))
        return;
      if (!pokemon.knownType || pokemon.hasType("Steel")) {
        pokemon.maybeTrapped = true;
      }
    },
    flags: {},
    name: "Magnet Pull",
    rating: 4,
    num: 42
  },
  marvelscale: {
    onModifyDefPriority: 6,
    onModifyDef(def, pokemon) {
      if (pokemon.status) {
        return this.chainModify(1.5);
      }
    },
    flags: { breakable: 1 },
    name: "Marvel Scale",
    rating: 2.5,
    num: 63
  },
	megalauncher: {
		onBasePowerPriority: 19,
		onBasePower(basePower, attacker, defender, move) {
			if (move.flags['pulse'] || move.flags['bullet']) {
				return this.chainModify(1.5);
			}
		},
		flags: {},
		name: "Mega Launcher",
		rating: 3,
		num: 178,
	},
  merciless: {
    onModifyCritRatio(critRatio, source, target) {
      if (target && ["psn", "tox"].includes(target.status))
        return 5;
    },
    flags: {},
    name: "Merciless",
    rating: 1.5,
    num: 196
  },
  mimicry: {
    onStart(pokemon) {
      this.singleEvent("TerrainChange", this.effect, this.effectState, pokemon);
    },
    onTerrainChange(pokemon) {
      let types;
      switch (this.field.terrain) {
        case "electricterrain":
          types = ["Electric"];
          break;
        case "grassyterrain":
          types = ["Grass"];
          break;
        case "mistyterrain":
          types = ["Fairy"];
          break;
        case "psychicterrain":
          types = ["Psychic"];
          break;
        default:
          types = pokemon.baseSpecies.types;
      }
      const oldTypes = pokemon.getTypes();
      if (oldTypes.join() === types.join() || !pokemon.setType(types))
        return;
      if (this.field.terrain || pokemon.transformed) {
        this.add("-start", pokemon, "typechange", types.join("/"), "[from] ability: Mimicry");
        if (!this.field.terrain)
          this.hint("Transform Mimicry changes you to your original un-transformed types.");
      } else {
        this.add("-activate", pokemon, "ability: Mimicry");
        this.add("-end", pokemon, "typechange", "[silent]");
      }
    },
    flags: {},
    name: "Mimicry",
    rating: 0,
    num: 250
  },
  mindseye: {
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      if (boost.accuracy && boost.accuracy < 0) {
        delete boost.accuracy;
        if (!effect.secondaries) {
          this.add("-fail", target, "unboost", "accuracy", "[from] ability: Mind's Eye", "[of] " + target);
        }
      }
    },
    onModifyMovePriority: -5,
    onModifyMove(move) {
      move.ignoreEvasion = true;
      if (!move.ignoreImmunity)
        move.ignoreImmunity = {};
      if (move.ignoreImmunity !== true) {
        move.ignoreImmunity["Fighting"] = true;
        move.ignoreImmunity["Normal"] = true;
      }
    },
    flags: { breakable: 1 },
    name: "Mind's Eye",
    rating: 0,
    num: 300
  },
  minus: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    onModifyDefPriority: 5,
    onModifyDef(def, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(spa, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    onModifySpDPriority: 5,
    onModifySpD(spd, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    onModifySpePriority: 5,
    onModifySpe(spe, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    flags: {},
    name: "Minus",
    rating: 0,
    num: 58
  },
  mirrorarmor: {
    onTryBoost(boost, target, source, effect) {
      if (!source || target === source || !boost || effect.name === "Mirror Armor" || effect.name === "Prism Scale")
        return;
      let b;
      for (b in boost) {
        if (boost[b] < 0) {
          if (target.boosts[b] === -6)
            continue;
          const negativeBoost = {};
          negativeBoost[b] = boost[b];
          delete boost[b];
          if (source.hp) {
            this.add("-ability", target, "Mirror Armor");
            this.boost(negativeBoost, source, target, null, true);
          }
        }
      }
    },
    flags: { breakable: 1 },
    name: "Mirror Armor",
    rating: 2,
    num: 240
  },
  mistysurge: {
    onStart(source) {
      this.field.setTerrain("mistyterrain");
    },
    flags: {},
    name: "Misty Surge",
    rating: 3.5,
    num: 228
  },
  moldbreaker: {
    onStart(pokemon) {
      this.add("-ability", pokemon, "Mold Breaker");
    },
    onModifyMove(move) {
      move.ignoreAbility = true;
    },
    flags: {},
    name: "Mold Breaker",
    rating: 3,
    num: 104
  },
  moody: {
    onResidualOrder: 28,
    onResidualSubOrder: 2,
    onResidual(pokemon) {
      let stats = [];
      const boost = {};
      let statPlus;
      for (statPlus in pokemon.boosts) {
        if (statPlus === "accuracy" || statPlus === "evasion")
          continue;
        if (pokemon.boosts[statPlus] < 6) {
          stats.push(statPlus);
        }
      }
      let randomStat = stats.length ? this.sample(stats) : void 0;
      if (randomStat)
        boost[randomStat] = 2;
      stats = [];
      let statMinus;
      for (statMinus in pokemon.boosts) {
        if (statMinus === "accuracy" || statMinus === "evasion")
          continue;
        if (pokemon.boosts[statMinus] > -6 && statMinus !== randomStat) {
          stats.push(statMinus);
        }
      }
      randomStat = stats.length ? this.sample(stats) : void 0;
      if (randomStat)
        boost[randomStat] = -1;
      this.boost(boost, pokemon, pokemon);
    },
    flags: {},
    name: "Moody",
    rating: 5,
    num: 141
  },
  motordrive: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Electric") {
        if (!this.boost({ spe: 1 })) {
          this.add("-immune", target, "[from] ability: Motor Drive");
        }
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Motor Drive",
    rating: 3,
    num: 78
  },
  moxie: {
    onSourceAfterFaint(length, target, source, effect) {
      if (effect && effect.effectType === "Move") {
        this.boost({ atk: length }, source);
      }
    },
    flags: {},
    name: "Moxie",
    rating: 3,
    num: 153
  },
  multiscale: {
    onSourceModifyDamage(damage, source, target, move) {
      if (target.hp >= target.maxhp) {
        this.debug("Multiscale weaken");
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Multiscale",
    rating: 3.5,
    num: 136
  },
  multitype: {
    // Multitype's type-changing itself is implemented in statuses.js
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "Multitype",
    rating: 4,
    num: 121
  },
  mummy: {
    onDamagingHit(damage, target, source, move) {
      const sourceAbility = source.getAbility();
      if (sourceAbility.flags["cantsuppress"] || sourceAbility.id === "mummy") {
        return;
      }
      if (this.checkMoveMakesContact(move, source, target, !source.isAlly(target))) {
        const oldAbility = source.setAbility("mummy", target);
        if (oldAbility) {
          this.add("-activate", target, "ability: Mummy", this.dex.abilities.get(oldAbility).name, "[of] " + source);
        }
      }
    },
    flags: {},
    name: "Mummy",
    rating: 2,
    num: 152
  },
	myceliummight: {
		onFractionalPriorityPriority: -1,
		onFractionalPriority(priority, pokemon, target, move) {
			if (move.category === 'Status' && move.type === 'Grass') {
				return -0.1;
			}
		},
		onModifyMove(move) {
			if (move.category === 'Status' && move.type === 'Grass') {
				move.ignoreAbility = true;
			}
		},
		flags: {},
		name: "Mycelium Might",
		rating: 2,
		num: 298,
	},
  naturalcure: {
    onCheckShow(pokemon) {
      if (pokemon.side.active.length === 1)
        return;
      if (pokemon.showCure === true || pokemon.showCure === false)
        return;
      const cureList = [];
      let noCureCount = 0;
      for (const curPoke of pokemon.side.active) {
        if (!curPoke?.status) {
          continue;
        }
        if (curPoke.showCure) {
          continue;
        }
        const species = curPoke.species;
        if (!Object.values(species.abilities).includes("Natural Cure")) {
          continue;
        }
        if (!species.abilities["1"] && !species.abilities["H"]) {
          continue;
        }
        if (curPoke !== pokemon && !this.queue.willSwitch(curPoke)) {
          continue;
        }
        if (curPoke.hasAbility("naturalcure")) {
          cureList.push(curPoke);
        } else {
          noCureCount++;
        }
      }
      if (!cureList.length || !noCureCount) {
        for (const pkmn of cureList) {
          pkmn.showCure = true;
        }
      } else {
        this.add("-message", "(" + cureList.length + " of " + pokemon.side.name + "'s pokemon " + (cureList.length === 1 ? "was" : "were") + " cured by Natural Cure.)");
        for (const pkmn of cureList) {
          pkmn.showCure = false;
        }
      }
    },
    onSwitchOut(pokemon) {
      if (!pokemon.status)
        return;
      if (pokemon.showCure === void 0)
        pokemon.showCure = true;
      if (pokemon.showCure)
        this.add("-curestatus", pokemon, pokemon.status, "[from] ability: Natural Cure");
      pokemon.clearStatus();
      if (!pokemon.showCure)
        pokemon.showCure = void 0;
    },
    flags: {},
    name: "Natural Cure",
    rating: 2.5,
    num: 30
  },
  neuroforce: {
    onModifyDamage(damage, source, target, move) {
      if (move && target.getMoveHitData(move).typeMod > 0) {
        return this.chainModify([5120, 4096]);
      }
    },
    flags: {},
    name: "Neuroforce",
    rating: 2.5,
    num: 233
  },
  neutralizinggas: {
    // Ability suppression implemented in sim/pokemon.ts:Pokemon#ignoringAbility
    onPreStart(pokemon) {
      this.add("-ability", pokemon, "Neutralizing Gas");
      pokemon.abilityState.ending = false;
      const strongWeathers = ["desolateland", "primordialsea", "deltastream"];
      for (const target of this.getAllActive()) {
        if (target.hasItem("Ability Shield")) {
          this.add("-block", target, "item: Ability Shield");
          continue;
        }
        if (target.volatiles["commanding"]) {
          continue;
        }
        if (target.illusion) {
          this.singleEvent("End", this.dex.abilities.get("Illusion"), target.abilityState, target, pokemon, "neutralizinggas");
        }
        if (target.volatiles["slowstart"]) {
          delete target.volatiles["slowstart"];
          this.add("-end", target, "Slow Start", "[silent]");
        }
        if (strongWeathers.includes(target.getAbility().id)) {
          this.singleEvent("End", this.dex.abilities.get(target.getAbility().id), target.abilityState, target, pokemon, "neutralizinggas");
        }
      }
    },
    onEnd(source) {
      if (source.transformed)
        return;
      for (const pokemon of this.getAllActive()) {
        if (pokemon !== source && pokemon.hasAbility("Neutralizing Gas")) {
          return;
        }
      }
      this.add("-end", source, "ability: Neutralizing Gas");
      if (source.abilityState.ending)
        return;
      source.abilityState.ending = true;
      const sortedActive = this.getAllActive();
      this.speedSort(sortedActive);
      for (const pokemon of sortedActive) {
        if (pokemon !== source) {
          if (pokemon.getAbility().flags["cantsuppress"])
            continue;
          if (pokemon.hasItem("abilityshield"))
            continue;
          this.singleEvent("Start", pokemon.getAbility(), pokemon.abilityState, pokemon);
          if (pokemon.ability === "gluttony") {
            pokemon.abilityState.gluttony = false;
          }
        }
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
    name: "Neutralizing Gas",
    rating: 3.5,
    num: 256
  },
  noguard: {
    onAnyInvulnerabilityPriority: 1,
    onAnyInvulnerability(target, source, move) {
      if (move && (source === this.effectState.target || target === this.effectState.target))
        return 0;
    },
    onAnyAccuracy(accuracy, target, source, move) {
      if (move && (source === this.effectState.target || target === this.effectState.target)) {
        return true;
      }
      return accuracy;
    },
    flags: {},
    name: "No Guard",
    rating: 4,
    num: 99
  },
  normalize: {
    onModifyTypePriority: 1,
    onModifyType(move, pokemon) {
      const noModifyType = [
        "hiddenpower",
        "judgment",
        "multiattack",
        "naturalgift",
        "revelationdance",
        "struggle",
        "technoblast",
        "terrainpulse",
        "weatherball"
      ];
      if (
        !(move.isZ && move.category !== "Status") &&
        !noModifyType.includes(move.id) &&
        !(move.name === "Tera Blast" && pokemon.terastallized) &&
        move.type !== "Normal"
      ) {
        move.type = "Normal";
        move.typeChangerBoosted = this.effect;
      }
    },
    onModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Normal" && !move.typeChangerBoosted) {
        return this.chainModify(2);
      }
    },
    onModifySpA(spa, attacker, defender, move) {
      if (move.type === "Normal" && !move.typeChangerBoosted) {
        return this.chainModify(2);
      }
    },
    onBasePowerPriority: 23,
    onBasePower(basePower, pokemon, target, move) {
      if (move.type === "Normal" && move.typeChangerBoosted === this.effect) {
        return this.chainModify([5324, 4096]);
      }
    },
    flags: {},
    name: "Normalize",
    rating: 0,
    num: 96,
  },
  oblivious: {
    onUpdate(pokemon) {
      if (pokemon.volatiles["attract"]) {
        this.add("-activate", pokemon, "ability: Oblivious");
        pokemon.removeVolatile("attract");
        this.add("-end", pokemon, "move: Attract", "[from] ability: Oblivious");
      }
      if (pokemon.volatiles["taunt"]) {
        this.add("-activate", pokemon, "ability: Oblivious");
        pokemon.removeVolatile("taunt");
      }
    },
    onImmunity(type, pokemon) {
      if (type === "attract")
        return false;
    },
    onTryHit(pokemon, target, move) {
      if (move.id === "attract" || move.id === "captivate" || move.id === "taunt" || move.id === "partingshot") {
        this.add("-immune", pokemon, "[from] ability: Oblivious");
        return null;
      }
    },
    onTryBoost(boost, target, source, effect) {
      if (effect.name === "Intimidate" && boost.atk) {
        delete boost.atk;
        this.add("-fail", target, "unboost", "Attack", "[from] ability: Oblivious", "[of] " + target);
      }
    },
    flags: { breakable: 1 },
    name: "Oblivious",
    rating: 1.5,
    num: 12
  },
  opportunist: {
    onFoeAfterBoost(boost, target, source, effect) {
      if (effect?.name === "Opportunist" || effect?.name === "Mirror Herb")
        return;
      const pokemon = this.effectState.target;
      const positiveBoosts = {};
      let i;
      for (i in boost) {
        if (boost[i] > 0) {
          positiveBoosts[i] = boost[i];
        }
      }
      if (Object.keys(positiveBoosts).length < 1)
        return;
      this.boost(positiveBoosts, pokemon);
    },
    flags: {},
    name: "Opportunist",
    rating: 3,
    num: 290
  },
  orichalcumpulse: {
    onStart(pokemon) {
      if (this.field.setWeather("sunnyday")) {
        this.add("-activate", pokemon, "Orichalcum Pulse", "[source]");
      } else if (this.field.isWeather("sunnyday")) {
        this.add("-activate", pokemon, "ability: Orichalcum Pulse");
      }
    },
    onModifyAtkPriority: 5,
    onModifyAtk(atk, pokemon) {
      if (["sunnyday", "desolateland"].includes(pokemon.effectiveWeather())) {
        this.debug("Orichalcum boost");
        return this.chainModify([5461, 4096]);
      }
    },
    flags: {},
    name: "Orichalcum Pulse",
    rating: 4.5,
    num: 288
  },
  overcoat: {
    onImmunity(type, pokemon) {
      if (type === "sandstorm" || type === "hail" || type === "powder")
        return false;
    },
    onTryHitPriority: 1,
    onTryHit(target, source, move) {
      if (move.flags["powder"] && target !== source && this.dex.getImmunity("powder", target)) {
        this.add("-immune", target, "[from] ability: Overcoat");
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Overcoat",
    rating: 2,
    num: 142
  },
  overgrow: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Grass" && attacker.hp <= attacker.maxhp / 2) {
        this.debug("Overgrow boost");
        return this.chainModify(1.4);
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, attacker, defender, move) {
      if (move.type === "Grass" && attacker.hp <= attacker.maxhp / 2) {
        this.debug("Overgrow boost");
        return this.chainModify(1.4);
      }
    },
    flags: {},
    name: "Overgrow",
    rating: 2,
    num: 65
  },
  owntempo: {
    onUpdate(pokemon) {
      if (pokemon.volatiles["confusion"]) {
        this.add("-activate", pokemon, "ability: Own Tempo");
        pokemon.removeVolatile("confusion");
      }
    },
    onTryAddVolatile(status, pokemon) {
      if (status.id === "confusion")
        return null;
    },
    onHit(target, source, move) {
      if (move?.volatileStatus === "confusion") {
        this.add("-immune", target, "confusion", "[from] ability: Own Tempo");
      }
    },
    onTryBoost(boost, target, source, effect) {
      if (effect.name === "Intimidate" && boost.atk) {
        delete boost.atk;
        this.add("-fail", target, "unboost", "Attack", "[from] ability: Own Tempo", "[of] " + target);
      }
    },
    flags: { breakable: 1 },
    name: "Own Tempo",
    rating: 1.5,
    num: 20
  },
  parentalbond: {
    onPrepareHit(source, target, move) {
      if (source.hp <= source.maxhp / 4) return;
      
      if (
        move.category === "Status" ||
        move.multihit ||
        move.flags["noparentalbond"] ||
        move.flags["charge"] ||
        move.flags["futuremove"] ||
        move.spreadHit ||
        move.isZ ||
        move.isMax
      ) return;
      
      move.multihit = 2;
      move.multihitType = "parentalbond";
    },
    // Damage modifier implemented in BattleActions#modifyDamage()
    onSourceModifySecondaries(secondaries, target, source, move) {
      if (move.multihitType === "parentalbond" && move.id === "secretpower" && move.hit < 2) {
        return secondaries.filter((effect) => effect.volatileStatus === "flinch");
      }
    },
    flags: {},
    name: "Parental Bond",
    rating: 4.5,
    num: 185
  },
  pastelveil: {
    onStart(pokemon) {
      for (const ally of pokemon.alliesAndSelf()) {
        if (["psn", "tox"].includes(ally.status)) {
          this.add("-activate", pokemon, "ability: Pastel Veil");
          ally.cureStatus();
        }
      }
    },
    onUpdate(pokemon) {
      if (["psn", "tox"].includes(pokemon.status)) {
        this.add("-activate", pokemon, "ability: Pastel Veil");
        pokemon.cureStatus();
      }
    },
    onAllySwitchIn(pokemon) {
      if (["psn", "tox"].includes(pokemon.status)) {
        this.add("-activate", this.effectState.target, "ability: Pastel Veil");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (!["psn", "tox"].includes(status.id))
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Pastel Veil");
      }
      return false;
    },
    onAllySetStatus(status, target, source, effect) {
      if (!["psn", "tox"].includes(status.id))
        return;
      if (effect?.status) {
        const effectHolder = this.effectState.target;
        this.add("-block", target, "ability: Pastel Veil", "[of] " + effectHolder);
      }
      return false;
    },
	onSourceModifyDamage(damage, source, target, move) {
      if (move.type === 'Poison' && (target === this.effectState.target || target.side === this.effectState.target.side)) {
        this.debug('Pastel Veil - reducing damage');
        return this.chainModify(0.8);
      }
    },
    flags: { breakable: 1 },
    name: "Pastel Veil",
    rating: 2,
    num: 257
  },
  perishbody: {
    onDamagingHit(damage, target, source, move) {
      if (!this.checkMoveMakesContact(move, source, target))
        return;
      let announced = false;
      for (const pokemon of [target, source]) {
        if (pokemon.volatiles["perishsong"])
          continue;
        if (!announced) {
          this.add("-ability", target, "Perish Body");
          announced = true;
        }
        pokemon.addVolatile("perishsong");
      }
    },
    flags: {},
    name: "Perish Body",
    rating: 1,
    num: 253
  },
	pickpocket: {
    onAfterMoveSecondarySelf(source, target, move) {
      if (move.flags['contact']) {
      if (target.hasAbility('stickyhold')) {
        this.add('-activate', target, 'ability: Sticky Hold');
        return;
      }
	  if (source.item && target.item) {
        if (source.hp) {
			const item = target.takeItem();
			if (item) {
				this.add('-enditem', target, item.name, '[from] ability: Magician', '[of] ' + source);
			}
		}
    }
      else if (!source.item && target.item) {
        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; // bypass setItem so we don't break choicelock or anything
				return;
			}
			this.add('-item', source, yourItem, '[from] ability: Magician', '[of] ' + target);
	}
	}
  },
    flags: {},
    name: "Pickpocket",
    rating: 1,
    num: 124,
  },
  pickup: {
    onResidualOrder: 28,
    onResidualSubOrder: 2,
    onResidual(pokemon) {
      if (pokemon.item)
        return;
      const pickupTargets = this.getAllActive().filter((target) => target.lastItem && target.usedItemThisTurn && pokemon.isAdjacent(target));
      if (!pickupTargets.length)
        return;
      const randomTarget = this.sample(pickupTargets);
      const item = randomTarget.lastItem;
      randomTarget.lastItem = "";
      this.add("-item", pokemon, this.dex.items.get(item), "[from] ability: Pickup");
      pokemon.setItem(item);
    },
    flags: {},
    name: "Pickup",
    rating: 0.5,
    num: 53
  },
  pixilate: {
    onModifyTypePriority: -1,
    onModifyType(move, pokemon) {
      const noModifyType = [
        "judgment",
        "multiattack",
        "naturalgift",
        "revelationdance",
        "technoblast",
        "terrainpulse",
        "weatherball"
      ];
      if (move.type === "Normal" && !noModifyType.includes(move.id) && !(move.isZ && move.category !== "Status") && !(move.name === "Tera Blast" && pokemon.terastallized)) {
        move.type = "Fairy";
        move.typeChangerBoosted = this.effect;
      }
    },
    onBasePowerPriority: 23,
    onBasePower(basePower, pokemon, target, move) {
      if (move.typeChangerBoosted === this.effect)
        return this.chainModify([4915, 4096]);
    },
    flags: {},
    name: "Pixilate",
    rating: 4,
    num: 182
  },
	plus: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    onModifyDefPriority: 5,
    onModifyDef(def, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(spa, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    onModifySpDPriority: 5,
    onModifySpD(spd, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    onModifySpePriority: 5,
    onModifySpe(spe, pokemon) {
      for (const allyActive of pokemon.allies()) {
        if (
          (allyActive.species.id === 'plusle' && allyActive.hasAbility('plus')) ||
          (allyActive.species.id === 'minun' && allyActive.hasAbility('minus'))
        ) {
          return this.chainModify(1.5);
        }
      }
    },
    flags: {},
    name: "Plus",
    rating: 0,
    num: 57,
  },
  poisonheal: {
    onDamagePriority: 1,
    onDamage(damage, target, source, effect) {
      if (effect.id === "psn" || effect.id === "tox") {
        this.heal(target.baseMaxhp / 8);
        return false;
      }
    },
    flags: {},
    name: "Poison Heal",
    rating: 4,
    num: 90
  },
  poisonpoint: {
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target)) {
        if (this.randomChance(3, 10)) {
          source.trySetStatus("psn", target);
        }
      }
    },
    onBasePower(basePower, attacker, defender, move) {
      if (move.type === 'Poison' && move.flags['contact']) {
        return this.chainModify(1.3);
      }
    },
    flags: {},
    name: "Poison Point",
    rating: 1.5,
    num: 38
  },
  poisonpuppeteer: {
    onAnyAfterSetStatus(status, target, source, effect) {
      if (source.baseSpecies.name !== "Pecharunt")
        return;
      if (source !== this.effectState.target || target === source || effect.effectType !== "Move")
        return;
      if (status.id === "psn" || status.id === "tox") {
        target.addVolatile("confusion");
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1 },
    name: "Poison Puppeteer",
    rating: 3,
    num: 310
  },
	poisontouch: {
    onSourceDamagingHit(damage, target, source, move) {
      // Despite not being a secondary, Shield Dust / Covert Cloak block Poison Touch's effect
      if (target.hasAbility('shielddust') || target.hasItem('covertcloak')) return;
      if (this.checkMoveMakesContact(move, target, source)) {
        if (this.randomChance(3, 10)) {
          if (target.status === 'psn') {
            target.setStatus('tox', source);
          } else {
            target.trySetStatus('psn', source);
          }
        }
      }
    },
    flags: {},
    name: "Poison Touch",
    rating: 2,
    num: 143,
  },
  powerconstruct: {
    onResidualOrder: 29,
    onResidual(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Zygarde" || pokemon.transformed || !pokemon.hp)
        return;
      if (pokemon.species.id === "zygardecomplete" || pokemon.hp > pokemon.maxhp / 2)
        return;
      this.add("-activate", pokemon, "ability: Power Construct");
      pokemon.formeChange("Zygarde-Complete", this.effect, true);
      pokemon.baseMaxhp = Math.floor(Math.floor(
        2 * pokemon.species.baseStats["hp"] + pokemon.set.ivs["hp"] + Math.floor(pokemon.set.evs["hp"] / 4) + 100
      ) * pokemon.level / 100 + 10);
      const newMaxHP = pokemon.volatiles["dynamax"] ? 2 * pokemon.baseMaxhp : pokemon.baseMaxhp;
      pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp);
      pokemon.maxhp = newMaxHP;
      this.add("-heal", pokemon, pokemon.getHealth, "[silent]");
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "Power Construct",
    rating: 5,
    num: 211
  },
  powerofalchemy: {
    onSwitchIn() {
      const side = this.effectState.target.side;
      const faintedAllies = side.pokemon.filter(pokemon => pokemon.fainted && pokemon.ability !== 'noability');
      
      if (faintedAllies.length) {
        const lastFaintedAlly = faintedAllies[faintedAllies.length - 1];
        const ability = lastFaintedAlly.getAbility();
        
        if (!ability.flags["noreceiver"] && this.effectState.target.setAbility(ability)) {
          this.add("-ability", this.effectState.target, ability, "[from] ability: Power of Alchemy", "[of] " + lastFaintedAlly);
        }
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
    name: "Power of Alchemy",
    rating: 0,
    num: 223,
  },
  powerspot: {
    onAllyBasePowerPriority: 22,
    onAllyBasePower(basePower, attacker, defender, move) {
      if (attacker !== this.effectState.target) {
        this.debug("Power Spot boost");
        return this.chainModify([5734, 4096]);
      }
    },
     onBasePowerPriority: 22,
     OnBasePower(basepower, attacker, defender, move){
      if (attacker !==this.effectState.target) {
        this.debug("Power Spot [user] Boost");
        return this.chainModify([5734, 4096]);
      }
    },
    flags: {},
    name: "Power Spot",
    rating: 0,
    num: 249
  },
  prankster: {
    onModifyPriority(priority, pokemon, target, move) {
      if (move?.category === "Status") {
        move.pranksterBoosted = true;
        return priority + 1;
      }
    },
    flags: {},
    name: "Prankster",
    rating: 4,
    num: 158
  },
  pressure: {
    onStart(pokemon) {
      this.add("-ability", pokemon, "Pressure");
    },
    onDeductPP(target, source) {
      if (target.isAlly(source))
        return;
      return 1;
    },
    flags: {},
    name: "Pressure",
    rating: 2.5,
    num: 46
  },
  primordialsea: {
    onStart(source) {
      this.field.setWeather("primordialsea");
    },
    onAnySetWeather(target, source, weather) {
      const strongWeathers = ["desolateland", "primordialsea", "deltastream"];
      if (this.field.getWeather().id === "primordialsea" && !strongWeathers.includes(weather.id))
        return false;
    },
    onEnd(pokemon) {
      if (this.field.weatherState.source !== pokemon)
        return;
      for (const target of this.getAllActive()) {
        if (target === pokemon)
          continue;
        if (target.hasAbility("primordialsea")) {
          this.field.weatherState.source = target;
          return;
        }
      }
      this.field.clearWeather();
    },
    flags: {},
    name: "Primordial Sea",
    rating: 4.5,
    num: 189
  },
  prismarmor: {
    onSourceModifyDamage(damage, source, target, move) {
      if (target.getMoveHitData(move).typeMod > 0) {
        this.debug("Prism Armor neutralize");
        return this.chainModify(0.75);
      }
    },
    flags: {},
    name: "Prism Armor",
    rating: 3,
    num: 232
  },
  propellertail: {
    onModifyMovePriority: 1,
    onModifyMove(move) {
      move.tracksTarget = move.target !== "scripted";
    },
    flags: {},
    name: "Propeller Tail",
    rating: 0,
    num: 239
  },
  protean: {
    onPrepareHit(source, target, move) {
      if (move.hasBounced || move.flags["futuremove"] || move.sourceEffect === "snatch")
        return;
      const type = move.type;
      if (type && type !== "???" && source.getTypes().join() !== type) {
        if (!source.setType(type))
          return;
        this.add("-start", source, "typechange", type, "[from] ability: Protean");
      }
    },
	    onModifyMove(move, pokemon) {
        move.stab = 1.3;
    },
    flags: {},
    name: "Protean",
    rating: 4,
    num: 168
  },
  protosynthesis: {
    onStart(pokemon) {
      this.singleEvent("WeatherChange", this.effect, this.effectState, pokemon);
    },
    onWeatherChange(pokemon) {
      if (this.field.isWeather("sunnyday")) {
        pokemon.addVolatile("protosynthesis");
      } else if (!pokemon.volatiles["protosynthesis"]?.fromBooster) {
        pokemon.removeVolatile("protosynthesis");
      }
    },
    onEnd(pokemon) {
      delete pokemon.volatiles["protosynthesis"];
      this.add("-end", pokemon, "Protosynthesis", "[silent]");
    },
    condition: {
      noCopy: true,
      onStart(pokemon, source, effect) {
        if (effect?.name === "Booster Energy") {
          this.effectState.fromBooster = true;
          this.add("-activate", pokemon, "ability: Protosynthesis", "[fromitem]");
        } else {
          this.add("-activate", pokemon, "ability: Protosynthesis");
        }
        this.effectState.bestStat = pokemon.getBestStat(false, true);
        this.add("-start", pokemon, "protosynthesis" + this.effectState.bestStat);
      },
      onModifyAtkPriority: 5,
      onModifyAtk(atk, pokemon) {
        if (this.effectState.bestStat !== "atk" || pokemon.ignoringAbility())
          return;
        this.debug("Protosynthesis atk boost");
        return this.chainModify([5325, 4096]);
      },
      onModifyDefPriority: 6,
      onModifyDef(def, pokemon) {
        if (this.effectState.bestStat !== "def" || pokemon.ignoringAbility())
          return;
        this.debug("Protosynthesis def boost");
        return this.chainModify([5325, 4096]);
      },
      onModifySpAPriority: 5,
      onModifySpA(spa, pokemon) {
        if (this.effectState.bestStat !== "spa" || pokemon.ignoringAbility())
          return;
        this.debug("Protosynthesis spa boost");
        return this.chainModify([5325, 4096]);
      },
      onModifySpDPriority: 6,
      onModifySpD(spd, pokemon) {
        if (this.effectState.bestStat !== "spd" || pokemon.ignoringAbility())
          return;
        this.debug("Protosynthesis spd boost");
        return this.chainModify([5325, 4096]);
      },
      onModifySpe(spe, pokemon) {
        if (this.effectState.bestStat !== "spe" || pokemon.ignoringAbility())
          return;
        this.debug("Protosynthesis spe boost");
        return this.chainModify(1.3);
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Protosynthesis");
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
    name: "Protosynthesis",
    rating: 3,
    num: 281
  },
  psychicsurge: {
    onStart(source) {
      this.field.setTerrain("psychicterrain");
    },
    flags: {},
    name: "Psychic Surge",
    rating: 4,
    num: 227
  },
  punkrock: {
    onBasePowerPriority: 7,
    onBasePower(basePower, attacker, defender, move) {
      if (move.flags["sound"]) {
        this.debug("Punk Rock boost");
        return this.chainModify([5325, 4096]);
      }
    },
    onSourceModifyDamage(damage, source, target, move) {
      if (move.flags["sound"]) {
        this.debug("Punk Rock weaken");
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Punk Rock",
    rating: 3.5,
    num: 244
  },
  purepower: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk) {
      return this.chainModify(2);
    },
    flags: {},
    name: "Pure Power",
    rating: 5,
    num: 74
  },
  purifyingsalt: {
    onSetStatus(status, target, source, effect) {
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Purifying Salt");
      }
      return false;
    },
    onTryAddVolatile(status, target) {
      if (status.id === "yawn") {
        this.add("-immune", target, "[from] ability: Purifying Salt");
        return null;
      }
    },
    onSourceModifyAtkPriority: 6,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Ghost") {
        this.debug("Purifying Salt weaken");
        return this.chainModify(0.5);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(spa, attacker, defender, move) {
      if (move.type === "Ghost") {
        this.debug("Purifying Salt weaken");
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Purifying Salt",
    rating: 4,
    num: 272
  },
  quarkdrive: {
    onStart(pokemon) {
      this.singleEvent("TerrainChange", this.effect, this.effectState, pokemon);
    },
    onTerrainChange(pokemon) {
      if (this.field.isTerrain("electricterrain")) {
        pokemon.addVolatile("quarkdrive");
      } else if (!pokemon.volatiles["quarkdrive"]?.fromBooster) {
        pokemon.removeVolatile("quarkdrive");
      }
    },
    onEnd(pokemon) {
      delete pokemon.volatiles["quarkdrive"];
      this.add("-end", pokemon, "Quark Drive", "[silent]");
    },
    condition: {
      noCopy: true,
      onStart(pokemon, source, effect) {
        if (effect?.name === "Booster Energy") {
          this.effectState.fromBooster = true;
          this.add("-activate", pokemon, "ability: Quark Drive", "[fromitem]");
        } else {
          this.add("-activate", pokemon, "ability: Quark Drive");
        }
        this.effectState.bestStat = pokemon.getBestStat(false, true);
        this.add("-start", pokemon, "quarkdrive" + this.effectState.bestStat);
      },
      onModifyAtkPriority: 5,
      onModifyAtk(atk, pokemon) {
        if (this.effectState.bestStat !== "atk" || pokemon.ignoringAbility())
          return;
        this.debug("Quark Drive atk boost");
        return this.chainModify([5325, 4096]);
      },
      onModifyDefPriority: 6,
      onModifyDef(def, pokemon) {
        if (this.effectState.bestStat !== "def" || pokemon.ignoringAbility())
          return;
        this.debug("Quark Drive def boost");
        return this.chainModify([5325, 4096]);
      },
      onModifySpAPriority: 5,
      onModifySpA(spa, pokemon) {
        if (this.effectState.bestStat !== "spa" || pokemon.ignoringAbility())
          return;
        this.debug("Quark Drive spa boost");
        return this.chainModify([5325, 4096]);
      },
      onModifySpDPriority: 6,
      onModifySpD(spd, pokemon) {
        if (this.effectState.bestStat !== "spd" || pokemon.ignoringAbility())
          return;
        this.debug("Quark Drive spd boost");
        return this.chainModify([5325, 4096]);
      },
      onModifySpe(spe, pokemon) {
        if (this.effectState.bestStat !== "spe" || pokemon.ignoringAbility())
          return;
        this.debug("Quark Drive spe boost");
        return this.chainModify(1.3);
      },
      onEnd(pokemon) {
        this.add("-end", pokemon, "Quark Drive");
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, notransform: 1 },
    name: "Quark Drive",
    rating: 3,
    num: 282
  },
  queenlymajesty: {
    onFoeTryMove(target, source, move) {
      const targetAllExceptions = ["perishsong", "flowershield", "rototiller"];
      if (move.target === "foeSide" || move.target === "all" && !targetAllExceptions.includes(move.id)) {
        return;
      }
      const dazzlingHolder = this.effectState.target;
      if ((source.isAlly(dazzlingHolder) || move.target === "all") && move.priority > 0.1) {
        this.attrLastMove("[still]");
        this.add("cant", dazzlingHolder, "ability: Queenly Majesty", move, "[of] " + target);
        return false;
      }
    },
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      if (boost.spd && boost.spd < 0) {
        delete boost.spd;
        if (!effect.secondaries && effect.id !== "octolock") {
          this.add("-fail", target, "unboost", "Special Defense", "[from] ability: Queenly Majesty", "[of] " + target);
        }
      }
    },
    flags: { breakable: 1 },
    name: "Queenly Majesty",
    rating: 2.5,
    num: 214
  },
  quickdraw: {
    onFractionalPriorityPriority: -1,
    onFractionalPriority(priority, pokemon, target, move) {
      if (move.category !== "Status" && this.randomChance(3, 10)) {
        this.add("-activate", pokemon, "ability: Quick Draw");
        return 0.1;
      }
    },
    flags: {},
    name: "Quick Draw",
    rating: 2.5,
    num: 259
  },
  quickfeet: {
    onModifySpe(spe, pokemon) {
      if (pokemon.status) {
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Quick Feet",
    rating: 2.5,
    num: 95
  },
  raindish: {
    onWeather(target, source, effect) {
      if (target.hasItem("utilityumbrella"))
        return;
      if (effect.id === "raindance" || effect.id === "primordialsea") {
        this.heal(target.baseMaxhp / 8);
      }
    },
    flags: {},
    name: "Rain Dish",
    rating: 1.5,
    num: 44
  },
  rattled: {
    onDamagingHit(damage, target, source, move) {
      if (["Dark", "Bug", "Ghost"].includes(move.type)) {
        this.boost({ spe: 1 });
      }
    },
    onAfterBoost(boost, target, source, effect) {
      if (effect?.name === "Intimidate") {
        this.boost({ spe: 1 });
      }
    },
    flags: {},
    name: "Rattled",
    rating: 1,
    num: 155
  },
  receiver: {
    onAnyFaint(target) { 
      if (!this.effectState.target.hp) return;
      
      const ability = target.getAbility();
      if (ability.flags["noreceiver"] || ability.id === "noability") return;
  
      if (this.effectState.target.setAbility(ability)) {
        this.add("-ability", this.effectState.target, ability, "[from] ability: Receiver", "[of] " + target);
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
    name: "Receiver",
    rating: 0,
    num: 222
  },
  reckless: {
    onBasePowerPriority: 23,
    onBasePower(basePower, attacker, defender, move) {
      if (move.recoil || move.hasCrashDamage || move.id === 'chloroblast' || move.id === 'steelbeam' || move.id === 'struggle') {
        this.debug("Reckless boost");
        return this.chainModify([5734, 4096]);
      }
    },
    flags: {},
    name: "Reckless",
    rating: 3,
    num: 120
  },
  refrigerate: {
    onModifyTypePriority: -1,
    onModifyType(move, pokemon) {
      const noModifyType = [
        "judgment",
        "multiattack",
        "naturalgift",
        "revelationdance",
        "technoblast",
        "terrainpulse",
        "weatherball"
      ];
      if (move.type === "Normal" && !noModifyType.includes(move.id) && !(move.isZ && move.category !== "Status") && !(move.name === "Tera Blast" && pokemon.terastallized)) {
        move.type = "Ice";
        move.typeChangerBoosted = this.effect;
      }
    },
    onBasePowerPriority: 23,
    onBasePower(basePower, pokemon, target, move) {
      if (move.typeChangerBoosted === this.effect)
        return this.chainModify([4915, 4096]);
    },
    flags: {},
    name: "Refrigerate",
    rating: 4,
    num: 174
  },
  regenerator: {
    onSwitchOut(pokemon) {
      pokemon.heal(pokemon.baseMaxhp / 4);
    },
    flags: {},
    name: "Regenerator",
    rating: 4.5,
    num: 144
  },
  ripen: {
    onTryHeal(damage, target, source, effect) {
      if (!effect)
        return;
      if (effect.name === "Berry Juice" || effect.name === "Leftovers") {
        this.add("-activate", target, "ability: Ripen");
      }
      if (effect.isBerry)
        return this.chainModify(2);
    },
    onChangeBoost(boost, target, source, effect) {
      if (effect && effect.isBerry) {
        let b;
        for (b in boost) {
          boost[b] *= 2;
        }
      }
    },
    onSourceModifyDamagePriority: -1,
    onSourceModifyDamage(damage, source, target, move) {
      if (target.abilityState.berryWeaken) {
        target.abilityState.berryWeaken = false;
        return this.chainModify(0.5);
      }
    },
    onTryEatItemPriority: -1,
    onTryEatItem(item, pokemon) {
      this.add("-activate", pokemon, "ability: Ripen");
    },
    onEatItem(item, pokemon) {
      const weakenBerries = [
        "Babiri Berry",
        "Charti Berry",
        "Chilan Berry",
        "Chople Berry",
        "Coba Berry",
        "Colbur Berry",
        "Haban Berry",
        "Kasib Berry",
        "Kebia Berry",
        "Occa Berry",
        "Passho Berry",
        "Payapa Berry",
        "Rindo Berry",
        "Roseli Berry",
        "Shuca Berry",
        "Tanga Berry",
        "Wacan Berry",
        "Yache Berry"
      ];
      pokemon.abilityState.berryWeaken = weakenBerries.includes(item.name);
    },
    flags: {},
    name: "Ripen",
    rating: 2,
    num: 247
  },
  rivalry: {
    onBasePowerPriority: 24,
    onBasePower(basePower, attacker, defender, move) {
      if (attacker.gender && defender.gender) {
        if (attacker.gender === defender.gender) {
          this.debug("Rivalry boost");
          return this.chainModify(1.5);
        } else {
          this.debug("Rivalry weaken");
          return this.chainModify(0.75);
        }
      }
    },
    flags: {},
    name: "Rivalry",
    rating: 0,
    num: 79
  },
  rkssystem: {
    // RKS System's type-changing itself is implemented in statuses.js
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "RKS System",
    rating: 4,
    num: 225
  },
  rockhead: {
    onDamage(damage, target, source, effect) {
      if (effect.id === "recoil") {
        if (!this.activeMove)
          throw new Error("Battle.activeMove is null");
        if (this.activeMove.id !== "struggle")
          return null;
      }
    },
	onHit(target, source, move) {
            if (target.volatiles['lockedmove'] && target.volatiles['confusion']) {
                this.add('-activate', target, 'ability: Rock Head');
                target.removeVolatile('confusion');
            }
        },
        onTryAddVolatile(status, target) {
            // Prevent confusion from self-induced moves
            if (status.id === 'confusion' && target.volatiles['lockedmove']) {
                this.add('-activate', target, 'ability: Rock head');
                return null;
            }
        },
    flags: {},
    name: "Rock Head",
    rating: 3,
    num: 69
  },
  rockypayload: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Rock") {
        this.debug("Rocky Payload boost");
        return this.chainModify(1.5);
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, attacker, defender, move) {
      if (move.type === "Rock") {
        this.debug("Rocky Payload boost");
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Rocky Payload",
    rating: 3.5,
    num: 276
  },
  roughskin: {
    onDamagingHitOrder: 1,
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target, true)) {
        this.damage(source.baseMaxhp / 8, source, target);
      }
    },
    flags: {},
    name: "Rough Skin",
    rating: 2.5,
    num: 24
  },
  runaway: {
    onTrapPokemonPriority: -10,
    onTrapPokemon(pokemon) {
      pokemon.trapped = pokemon.maybeTrapped = false;
    },
    flags: {},
    name: "Run Away",
    rating: 0,
    num: 50
  },
  sandforce: {
    onBasePowerPriority: 21,
    onBasePower(basePower, attacker, defender, move) {
      if (this.field.isWeather("sandstorm")) {
        if (move.type === "Rock" || move.type === "Ground" || move.type === "Steel") {
          this.debug("Sand Force boost");
          return this.chainModify([5325, 4096]);
        }
      }
    },
    onImmunity(type, pokemon) {
      if (type === "sandstorm")
        return false;
    },
    flags: {},
    name: "Sand Force",
    rating: 2,
    num: 159
  },
  sandrush: {
    onModifySpe(spe, pokemon) {
      if (this.field.isWeather("sandstorm")) {
        return this.chainModify(2);
      }
    },
    onImmunity(type, pokemon) {
      if (type === "sandstorm")
        return false;
    },
    flags: {},
    name: "Sand Rush",
    rating: 3,
    num: 146
  },
  sandspit: {
    onDamagingHit(damage, target, source, move) {
      this.field.setWeather("sandstorm");
    },
    onImmunity(type, pokemon) {
      if (type === "sandstorm")
        return false;
    },
    flags: {},
    name: "Sand Spit",
    rating: 1,
    num: 245
  },
  sandstream: {
    onStart(source) {
      this.field.setWeather("sandstorm");
    },
    onImmunity(type, pokemon) {
      if (type === "sandstorm")
        return false;
    },
    flags: {},
    name: "Sand Stream",
    rating: 4,
    num: 45
  },
  sandveil: {
    onImmunity(type, pokemon) {
      if (type === "sandstorm")
        return false;
    },
    onModifyAccuracyPriority: 10,
    onModifyAccuracy(accuracy, target, source, move) {
      if (move.category === "Status" && typeof accuracy === "number" && this.field.isWeather(["sandstorm"])) {
        this.debug("sandveil's wonderskin fx - setting accuracy to 50");
        return 50;
      }
    },
    onDamage(damage, target, source, effect) {
      if (effect.effectType !== "Move" && this.field.isWeather(["sandstorm"])) {
        if (effect.effectType === "Ability" && this.field.isWeather(["sandstorm"]))
          this.add("-activate", source, "ability: " + effect.name);
        return false;
      }
    },
    flags: { breakable: 1 },
    name: "Sand Veil",
    rating: 1.5,
    num: 8
  },
  sapsipper: {
    onTryHitPriority: 1,
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Grass") {
        if (!this.boost({ atk: 1 })) {
          this.add("-immune", target, "[from] ability: Sap Sipper");
        }
        return null;
      }
    },
    onAllyTryHitSide(target, source, move) {
      if (source === this.effectState.target || !target.isAlly(source))
        return;
      if (move.type === "Grass") {
        this.boost({ atk: 1 }, this.effectState.target);
      }
    },
    flags: { breakable: 1 },
    name: "Sap Sipper",
    rating: 3,
    num: 157
  },
  schooling: {
    onStart(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Wishiwashi" || pokemon.level < 20 || pokemon.transformed)
        return;
      if (pokemon.hp > pokemon.maxhp / 4) {
        if (pokemon.species.id === "wishiwashi") {
          pokemon.formeChange("Wishiwashi-School");
        }
      } else {
        if (pokemon.species.id === "wishiwashischool") {
          pokemon.formeChange("Wishiwashi");
        }
      }
    },
    onResidualOrder: 29,
    onResidual(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Wishiwashi" || pokemon.level < 20 || pokemon.transformed || !pokemon.hp)
        return;
  
      if (pokemon.species.id === "wishiwashischool") {
        this.heal(pokemon.baseMaxhp / 16);
      }
  
      if (pokemon.hp > pokemon.maxhp / 4) {
        if (pokemon.species.id === "wishiwashi") {
          pokemon.formeChange("Wishiwashi-School");
        }
      } else {
        if (pokemon.species.id === "wishiwashischool") {
          pokemon.formeChange("Wishiwashi");
        }
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "Schooling",
    rating: 3,
    num: 208
  },
  scrappy: {
    onModifyMovePriority: -5,
    onModifyMove(move) {
      if (!move.ignoreImmunity)
        move.ignoreImmunity = {};
      if (move.ignoreImmunity !== true) {
        move.ignoreImmunity["Fighting"] = true;
        move.ignoreImmunity["Normal"] = true;
      }
    },
    onTryBoost(boost, target, source, effect) {
      if (effect.name === "Intimidate" && boost.atk) {
        delete boost.atk;
        this.add("-fail", target, "unboost", "Attack", "[from] ability: Scrappy", "[of] " + target);
      }
    },
    flags: {},
    name: "Scrappy",
    rating: 3,
    num: 113
  },
  screencleaner: {
    onStart(pokemon) {
      let activated = false;
      let removedCount = 0; 
      for (const sideCondition of ["reflect", "lightscreen", "auroraveil"]) {
        for (const side of [pokemon.side, ...pokemon.side.foeSidesWithConditions()]) {
          if (side.getSideCondition(sideCondition)) {
            if (!activated) {
              this.add("-activate", pokemon, "ability: Screen Cleaner");
              activated = true;
            }
            side.removeSideCondition(sideCondition);
            removedCount++; 
          }
        }
      }
      if (removedCount > 0) {
        this.boost({spe: removedCount}, pokemon, pokemon, null, true);
      }
    },
    flags: {},
    name: "Screen Cleaner",
    rating: 2,
    num: 251
  },
  seedsower: {
    onDamagingHit(damage, target, source, move) {
      this.field.setTerrain("grassyterrain");
    },
    flags: {},
    name: "Seed Sower",
    rating: 2.5,
    num: 269
  },
  serenegrace: {
    onModifyMovePriority: -2,
    onModifyMove(move) {
      if (move.secondaries) {
        this.debug("doubling secondary chance");
        for (const secondary of move.secondaries) {
          if (secondary.chance)
            secondary.chance *= 2;
        }
      }
      if (move.self?.chance)
        move.self.chance *= 2;
    },
    flags: {},
    name: "Serene Grace",
    rating: 3.5,
    num: 32
  },
  shadowshield: {
    onSourceModifyDamage(damage, source, target, move) {
      if (target.hp >= target.maxhp) {
        this.debug("Shadow Shield weaken");
        return this.chainModify(0.5);
      }
    },
    flags: {},
    name: "Shadow Shield",
    rating: 3.5,
    num: 231
  },
  shadowtag: {
    onBeforeTurn(pokemon) {
      for (const side of this.sides) {
        if (side.hasAlly(pokemon)) continue;
        side.addSideCondition('shadowtag', pokemon);
        const data = side.getSideConditionData('shadowtag');
        if (!data.sources) {
          data.sources = [];
        }
        data.sources.push(pokemon);
      }
    },
    onBasePower(relayVar, source, target, move) {
      // You can't get here unless the pursuit succeeds
      if (target.beingCalledBack || target.switchFlag) {
        this.debug('Pursuit damage boost');
        return move.basePower * 2;
      }
      return move.basePower;
    },
    condition: {
      duration: 1,
      onBeforeSwitchOut(pokemon) {
        const move = this.queue.willMove(pokemon.foes()[0]);
        const moveName = move && move.moveid ? move.moveid.toString() : "";
        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.foes()[0], 'ability: Shadow Tag');
            alreadyAdded = true;
          }
          // Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn.
          // If it is, then Mega Evolve before moving.
          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(moveName, source, source.getLocOf(pokemon));
        }
      },
    },
    flags: {},
    name: "Shadow Tag",
    rating: 5,
    num: 23
  },
  sharpness: {
    onBasePowerPriority: 19,
    onBasePower(basePower, attacker, defender, move) {
      if (move.flags["slicing"]) {
        this.debug("Shapness boost");
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Sharpness",
    rating: 3.5,
    num: 292
  },
  shedskin: {
    onResidualOrder: 5,
    onResidualSubOrder: 3,
    onResidual(pokemon) {
      if (pokemon.hp && pokemon.status && this.randomChance(33, 100)) {
        this.debug("shed skin");
        this.add("-activate", pokemon, "ability: Shed Skin");
        pokemon.cureStatus();
      }
    },
    flags: {},
    name: "Shed Skin",
    rating: 3,
    num: 61
  },
  sheerforce: {
    onModifyMove(move, pokemon) {
      if (move.secondaries) {
        delete move.secondaries;
        delete move.self;
        if (move.id === "clangoroussoulblaze")
          delete move.selfBoost;
        move.hasSheerForce = true;
      }
    },
    onBasePowerPriority: 21,
    onBasePower(basePower, pokemon, target, move) {
      if (move.hasSheerForce)
        return this.chainModify([5325, 4096]);
    },
    flags: {},
    name: "Sheer Force",
    rating: 3.5,
    num: 125
  },
  shellarmor: {
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      if (boost.def && boost.def < 0) {
        delete boost.def;
        if (!effect.secondaries && effect.id !== "octolock") {
          this.add("-fail", target, "unboost", "Defense", "[from] ability: Shell Armor", "[of] " + target);
        }
      }
    },
    onCriticalHit: false,
    flags: { breakable: 1 },
    name: "Shell Armor",
    rating: 1,
    num: 75
  },
  shielddust: {
    onModifySecondaries(secondaries) {
      this.debug("Shield Dust prevent secondary");
      return secondaries.filter((effect) => !!(effect.self || effect.dustproof));
    },
    flags: { breakable: 1 },
    name: "Shield Dust",
    rating: 2,
    num: 19
  },
  shieldsdown: {
    onStart(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Minior" || pokemon.transformed)
        return;
      if (pokemon.hp > pokemon.maxhp / 2) {
        if (pokemon.species.forme !== "Meteor") {
          pokemon.formeChange("Minior-Meteor");
        }
      } else {
        if (pokemon.species.forme === "Meteor") {
          pokemon.formeChange(pokemon.set.species);
        }
      }
    },
    onResidualOrder: 29,
    onResidual(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Minior" || pokemon.transformed || !pokemon.hp)
        return;
      if (pokemon.hp > pokemon.maxhp / 2) {
        if (pokemon.species.forme !== "Meteor") {
          pokemon.formeChange("Minior-Meteor");
        }
      } else {
        if (pokemon.species.forme === "Meteor") {
          pokemon.formeChange(pokemon.set.species);
        }
      }
    },
    onSetStatus(status, target, source, effect) {
      if (target.species.id !== "miniormeteor" || target.transformed)
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Shields Down");
      }
      return false;
    },
    onTryAddVolatile(status, target) {
      if (target.species.id !== "miniormeteor" || target.transformed)
        return;
      if (status.id !== "yawn")
        return;
      this.add("-immune", target, "[from] ability: Shields Down");
      return null;
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "Shields Down",
    rating: 3,
    num: 197
  },
  simple: {
    onChangeBoost(boost, target, source, effect) {
      if (effect && effect.id === "zpower")
        return;
      let i;
      for (i in boost) {
        boost[i] *= 2;
      }
    },
    flags: { breakable: 1 },
    name: "Simple",
    rating: 4,
    num: 86
  },
  skilllink: {
    onModifyMove(move) {
      if (move.multihit && Array.isArray(move.multihit) && move.multihit.length) {
        move.multihit = move.multihit[1];
      }
      if (move.multiaccuracy) {
        delete move.multiaccuracy;
      }
    },
    flags: {},
    name: "Skill Link",
    rating: 3,
    num: 92
  },
  slowstart: {
    onStart(pokemon) {
      // Check if the Pokemon has already completed Slow Start
      if (pokemon.side.persistentState && pokemon.side.persistentState[pokemon.species.id] === 'slowstartended') {
        return;
      }
      
      this.add('-ability', pokemon, 'Slow Start');
      if (!pokemon.side.persistentState) {
        pokemon.side.persistentState = {};
      }
  
      // If this is the first time activating Slow Start, initialize the turn counter
      if (!pokemon.side.persistentState[pokemon.species.id]) {
        pokemon.side.persistentState[pokemon.species.id] = 5;
      }
  
      // Apply Slow Start stat reduction
      pokemon.addVolatile('slowstart');
    },
    onResidualOrder: 27,
    onResidual(pokemon) {
      if (pokemon.side.persistentState[pokemon.species.id]) {
        pokemon.side.persistentState[pokemon.species.id]--;
        
        // Check if Slow Start should end
        if (pokemon.side.persistentState[pokemon.species.id] <= 0) {
          pokemon.removeVolatile('slowstart');
          pokemon.side.persistentState[pokemon.species.id] = 'slowstartended';
          this.add('-end', pokemon, 'Slow Start');
        }
      }
    },
    condition: {
      onModifyAtk(atk, pokemon) {
        return this.chainModify(0.5);
      },
      onModifySpe(spe, pokemon) {
        return this.chainModify(0.5);
      },
    },
    name: "Slow Start",
    rating: -1,
    num: 112,
  },
  slushrush: {
    onModifySpe(spe, pokemon) {
      if (this.field.isWeather(["hail", "snow"])) {
        return this.chainModify(2);
      }
    },
    flags: {},
    name: "Slush Rush",
    rating: 3,
    num: 202
  },
  sniper: {
    onModifyDamage(damage, source, target, move) {
      if (target.getMoveHitData(move).crit) {
        this.debug("Sniper boost");
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Sniper",
    rating: 2,
    num: 97
  },
  snowcloak: {
    onImmunity(type, pokemon) {
      if (type === "hail")
        return false;
    },
    onModifyAccuracyPriority: 10,
    onModifyAccuracy(accuracy, target, source, move) {
      if (move.category === "Status" && typeof accuracy === "number" && this.field.isWeather(["hail", "snow"])) {
        this.debug("snowcloak's wonderskin fx - setting accuracy to 50");
        return 50;
      }
    },
    onDamage(damage, target, source, effect) {
      if (effect.effectType !== "Move" && this.field.isWeather(["hail", "snow"])) {
        if (effect.effectType === "Ability" && this.field.isWeather(["hail", "snow"]))
          this.add("-activate", source, "ability: " + effect.name);
        return false;
      }
    },
    flags: { breakable: 1 },
    name: "Snow Cloak",
    rating: 1.5,
    num: 81
  },
  snowwarning: {
    onStart(source) {
      this.field.setWeather("snow");
    },
    onImmunity(type, pokemon) {
      if (type === "hail" && type === "snow")
        return false;
    },
    flags: {},
    name: "Snow Warning",
    rating: 4,
    num: 117
  },
  solarpower: {
    onModifySpAPriority: 5,
    onModifySpA(atk, pokemon) {
      if (["sunnyday", "desolateland"].includes(pokemon.effectiveWeather())) {
        return this.chainModify(1.5);
      }
    },
    onModifyAtkPriority: 5,
    onModifyAtk(atk, pokemon) {
      if (["sunnyday", "desolateland"].includes(pokemon.effectiveWeather())) {
        return this.chainModify(1.5);
      }
    },
    onAfterMove(pokemon, target, move) {
      if (
        ["sunnyday", "desolateland"].includes(pokemon.effectiveWeather()) &&
        move.category !== 'Status'
      ) {
        this.damage(pokemon.baseMaxhp / 16, pokemon, pokemon, this.dex.abilities.get('solarpower'));
      }
    },
    flags: {},
    name: "Solar Power",
    rating: 2,
    num: 94
  },
  solidrock: {
    onSourceModifyDamage(damage, source, target, move) {
      if (target.getMoveHitData(move).typeMod > 0) {
        this.debug("Solid Rock neutralize");
        return this.chainModify(0.75);
      }
    },
    flags: { breakable: 1 },
    name: "Solid Rock",
    rating: 3,
    num: 116
  },
  soulheart: {
    onAnyFaintPriority: 1,
    onAnyFaint() {
      this.boost({ spa: 1 }, this.effectState.target);
    },
    flags: {},
    name: "Soul-Heart",
    rating: 3.5,
    num: 220
  },
  soundproof: {
    onTryHit(target, source, move) {
      if (target !== source && move.flags["sound"]) {
        this.add("-immune", target, "[from] ability: Soundproof");
        return null;
      }
    },
    onAllyTryHitSide(target, source, move) {
      if (move.flags["sound"]) {
        this.add("-immune", this.effectState.target, "[from] ability: Soundproof");
      }
    },
    flags: { breakable: 1 },
    name: "Soundproof",
    rating: 2,
    num: 43
  },
  speedboost: {
    onResidualOrder: 28,
    onResidualSubOrder: 2,
    onResidual(pokemon) {
      if (pokemon.activeTurns) {
        this.boost({ spe: 1 });
      }
    },
    flags: {},
    name: "Speed Boost",
    rating: 4.5,
    num: 3
  },
  stakeout: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, attacker, defender) {
      if (!defender.activeTurns) {
        this.debug("Stakeout boost");
        return this.chainModify(2);
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, attacker, defender) {
      if (!defender.activeTurns) {
        this.debug("Stakeout boost");
        return this.chainModify(2);
      }
    },
    flags: {},
    name: "Stakeout",
    rating: 4.5,
    num: 198
  },
	stall: {
    onFractionalPriority: -0.1,
    flags: {},
    name: "Stall",
    rating: -1,
    num: 100,

    onStart(pokemon) {
        this.add('-ability', pokemon, 'Stall');
    },
    onModifyMovePriority: -5,
    onModifyMove(move) {
        move.ignoreAbility = true;
    },

    onModifyAccuracy(accuracy) {
        if (typeof accuracy === 'number') {
            return this.chainModify([4915, 4096]); 
        }
    },
},
  stalwart: {
    onModifyMovePriority: 1,
    onModifyMove(move) {
      move.tracksTarget = move.target !== "scripted";
    },
    flags: {},
    name: "Stalwart",
    rating: 0,
    num: 242
  },
  stamina: {
    onDamagingHit(damage, target, source, effect) {
      this.boost({ def: 1 });
    },
    flags: {},
    name: "Stamina",
    rating: 4,
    num: 192
  },
  stancechange: {
    onModifyMovePriority: 1,
    onModifyMove(move, attacker, defender) {
      if (attacker.species.baseSpecies !== "Aegislash" || attacker.transformed)
        return;
      if (move.category === "Status" && move.id !== "kingsshield")
        return;
      const targetForme = move.id === "kingsshield" ? "Aegislash" : "Aegislash-Blade";
      if (attacker.species.name !== targetForme)
        attacker.formeChange(targetForme);
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "Stance Change",
    rating: 4,
    num: 176
  },
  static: {
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target)) {
        if (this.randomChance(3, 10)) {
          source.trySetStatus("par", target);
        }
      }
    },
	onBasePower(basePower, attacker, defender, move) {
      if (move.type === 'Electric' && move.flags['contact']) {
        return this.chainModify(1.3);
      }
    },
    flags: {},
    name: "Static",
    rating: 2,
    num: 9
  },
  steadfast: {
    onTryAddVolatile(status, pokemon) {
      if (status.id === "flinch")
      this.boost({ spe: 1 });
        return null;
    },
    flags: {},
    name: "Steadfast",
    rating: 1,
    num: 80
  },
  steamengine: {
    onDamagingHit(damage, target, source, move) {
      if (["Water", "Fire"].includes(move.type)) {
        this.boost({ spe: 6 });
      }
    },
    flags: {},
    name: "Steam Engine",
    rating: 2,
    num: 243
  },
  steelworker: {
    onStart(pokemon) {
            if (!pokemon.hasType('Steel')) {
                this.add('-start', pokemon, 'typeadd', 'Steel', '[from] ability: Steelworker');
                pokemon.addType('Steel');
            }
        },
        onEnd(pokemon) {
                this.add('-end', pokemon, 'typeadd', 'Steel', "[silent]");
				pokemon.addType('');
        },
    flags: {},
    name: "Steelworker",
    rating: 3.5,
    num: 200
  },
  steelyspirit: {
    onAllyBasePowerPriority: 22,
    onAllyBasePower(basePower, attacker, defender, move) {
      if (move.type === "Steel") {
        this.debug("Steely Spirit boost");
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Steely Spirit",
    rating: 3.5,
    num: 252
  },
  stench: {
    onResidualOrder: 28,
    onResidualSubOrder: 2,
    onResidual(pokemon) {
      if (!pokemon.hp) return;
  
      const adjacentPokemon = [
        ...pokemon.side.active,
        ...pokemon.side.foe.active
      ].filter(target => target && target !== pokemon && pokemon.isAdjacent(target));
  
      for (const target of adjacentPokemon) {
        if (target.ability === 'stench') continue;
  
        const effectiveness = this.dex.getEffectiveness('Poison', target);
  
        if (effectiveness < 0) {
          continue;
        } else if (effectiveness === 0) {
          this.damage(target.baseMaxhp / 16, target, pokemon);
        } else if (effectiveness > 0) {
          this.damage(target.baseMaxhp / 8, target, pokemon);
        }
      }
    },
    flags: {},
    name: "Stench",
    rating: 0.5,
    num: 1,
  },
  stickyhold: {
    onTakeItem(item, pokemon, source) {
      if (!this.activeMove)
        throw new Error("Battle.activeMove is null");
      if (!pokemon.hp || pokemon.item === "stickybarb")
        return;
      if (source && source !== pokemon || this.activeMove.id === "knockoff") {
        this.add("-activate", pokemon, "ability: Sticky Hold");
        return false;
      }
    },
    flags: { breakable: 1 },
    name: "Sticky Hold",
    rating: 1.5,
    num: 60
  },
  stormdrain: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Water") {
        if (!this.boost({ spa: 1 })) {
          this.add("-immune", target, "[from] ability: Storm Drain");
        }
        return null;
      }
    },
    onAnyRedirectTarget(target, source, source2, move) {
      if (move.type !== "Water" || move.flags["pledgecombo"])
        return;
      const redirectTarget = ["randomNormal", "adjacentFoe"].includes(move.target) ? "normal" : move.target;
      if (this.validTarget(this.effectState.target, source, redirectTarget)) {
        if (move.smartTarget)
          move.smartTarget = false;
        if (this.effectState.target !== target) {
          this.add("-activate", this.effectState.target, "ability: Storm Drain");
        }
        return this.effectState.target;
      }
    },
    flags: { breakable: 1 },
    name: "Storm Drain",
    rating: 3,
    num: 114
  },
  strongjaw: {
    onBasePowerPriority: 19,
    onBasePower(basePower, attacker, defender, move) {
      if (move.flags["bite"]) {
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Strong Jaw",
    rating: 3.5,
    num: 173
  },
  sturdy: {
    onTryHit(pokemon, target, move) {
      if (move.ohko) {
        this.add("-immune", pokemon, "[from] ability: Sturdy");
        return null;
      }
    },
    onDamagePriority: -30,
    onDamage(damage, target, source, effect) {
      if (target.hp === target.maxhp && damage >= target.hp && effect && effect.effectType === "Move") {
        this.add("-ability", target, "Sturdy");
        return target.hp - 1;
      }
    },
    flags: { breakable: 1 },
    name: "Sturdy",
    rating: 3,
    num: 5
  },
  suctioncups: {
    onDragOutPriority: 1,
    onDragOut(pokemon) {
      this.add("-activate", pokemon, "ability: Suction Cups");
      return null;
    },
    flags: { breakable: 1 },
    name: "Suction Cups",
    rating: 1,
    num: 21
  },
  superluck: {
    onModifyCritRatio(critRatio) {
      return critRatio + 1;
    },
    flags: {},
    name: "Super Luck",
    rating: 1.5,
    num: 105
  },
  supersweetsyrup: {
    onStart(pokemon) {
      if (pokemon.syrupTriggered)
        return;
      pokemon.syrupTriggered = true;
      this.add("-ability", pokemon, "Supersweet Syrup");
      let activated = false;
      for (const target of pokemon.adjacentFoes()) {
        if (!activated) {
          this.add("-ability", pokemon, "Supersweet Syrup", "boost");
          activated = true;
        }
        if (target.volatiles["substitute"]) {
          this.add("-immune", target);
        } else {
          this.boost({ evasion: -1 }, target, pokemon, null, true);
        }
      }
    },
    flags: {},
    name: "Supersweet Syrup",
    rating: 1.5,
    num: 306
  },
  supremeoverlord: {
    onStart(pokemon) {
      if (pokemon.side.totalFainted) {
        this.add("-activate", pokemon, "ability: Supreme Overlord");
        const fallen = Math.min(pokemon.side.totalFainted, 5);
        this.add("-start", pokemon, `fallen${fallen}`, "[silent]");
        this.effectState.fallen = fallen;
      }
    },
    onEnd(pokemon) {
      this.add("-end", pokemon, `fallen${this.effectState.fallen}`, "[silent]");
    },
    onBasePowerPriority: 21,
    onBasePower(basePower, attacker, defender, move) {
      if (this.effectState.fallen) {
        const powMod = [4096, 4301, 4506, 4710, 4915, 5120];
        this.debug(`Supreme Overlord boost: ${powMod[this.effectState.fallen]}/4096`);
        return this.chainModify([powMod[this.effectState.fallen], 4096]);
      }
    },
    flags: {},
    name: "Supreme Overlord",
    rating: 4,
    num: 293
  },
  surgesurfer: {
    onModifySpe(spe) {
      if (this.field.isTerrain("electricterrain")) {
        return this.chainModify(2);
      }
    },
    flags: {},
    name: "Surge Surfer",
    rating: 3,
    num: 207
  },
  swarm: {
	  onPrepareHit(source, target, move) {
		  if (
			  move.category === 'Status' || move.multihit || move.flags['noparentalbond'] || move.flags['charge'] ||
			  move.flags['futuremove'] || move.spreadHit || move.isZ || move.isMax
		  ) return;
		  if (source.hp > source.maxhp / 2) return;
		  if (move.type !== 'Bug') return;

		  move.multihit = 2;
		  move.multihitType = 'parentalbond';
	  },
	  // Damage modifier implemented in BattleActions#modifyDamage()
	  onSourceModifySecondaries(secondaries, target, source, move) {
		  if (move.multihitType === 'parentalbond' && move.id === 'secretpower' && move.hit < 2) {
			  // Hack to prevent accidentally suppressing King's Rock/Razor Fang
			  return secondaries.filter(effect => effect.volatileStatus === 'flinch');
		  }
	  },
    flags: {},
    name: "Swarm",
    rating: 2,
    num: 68
  },
  sweetveil: {
    onAllySetStatus(status, target, source, effect) {
      if (status.id === "slp") {
        this.debug("Sweet Veil interrupts sleep");
        const effectHolder = this.effectState.target;
        this.add("-block", target, "ability: Sweet Veil", "[of] " + effectHolder);
        return null;
      }
    },
    onAllyTryAddVolatile(status, target) {
      if (status.id === "yawn") {
        this.debug("Sweet Veil blocking yawn");
        const effectHolder = this.effectState.target;
        this.add("-block", target, "ability: Sweet Veil", "[of] " + effectHolder);
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Sweet Veil",
    rating: 2,
    num: 175
  },
  swiftswim: {
    onModifySpe(spe, pokemon) {
      if (["raindance", "primordialsea"].includes(pokemon.effectiveWeather())) {
        return this.chainModify(2);
      }
    },
    flags: {},
    name: "Swift Swim",
    rating: 3,
    num: 33
  },
  symbiosis: {
    onAllyAfterUseItem(item, pokemon) {
      if (pokemon.switchFlag)
        return;
      const source = this.effectState.target;
      const myItem = source.takeItem();
      if (!myItem)
        return;
      if (!this.singleEvent("TakeItem", myItem, source.itemState, pokemon, source, this.effect, myItem) || !pokemon.setItem(myItem)) {
        source.item = myItem.id;
        return;
      }
      this.add("-activate", source, "ability: Symbiosis", myItem, "[of] " + pokemon);
    },
    flags: {},
    name: "Symbiosis",
    rating: 0,
    num: 180
  },
  synchronize: {
  onDamage(damage, target, source, effect) {
    if (effect && effect.effectType !== "Move" && effect.id !== "synchronize") {
      const halfDamage = Math.floor(damage / 2);
      for (const foes of target.adjacentFoes()) {
        this.damage(halfDamage, foes, target);
      }
    }
  },
  flags: {},
  name: "Synchronize",
  rating: 2,
  num: 28
},
  swordofruin: {
    onStart(pokemon) {
      if (this.suppressingAbility(pokemon))
        return;
      this.add("-ability", pokemon, "Sword of Ruin");
    },
    onAnyModifyDef(def, target, source, move) {
      const abilityHolder = this.effectState.target;
      if (target.hasAbility("Sword of Ruin"))
        return;
      if (!move.ruinedDef?.hasAbility("Sword of Ruin"))
        move.ruinedDef = abilityHolder;
      if (move.ruinedDef !== abilityHolder)
        return;
      this.debug("Sword of Ruin Def drop");
      return this.chainModify(0.80);
    },
    flags: {},
    name: "Sword of Ruin",
    rating: 4.5,
    num: 285
  },
  tabletsofruin: {
    onStart(pokemon) {
      if (this.suppressingAbility(pokemon))
        return;
      this.add("-ability", pokemon, "Tablets of Ruin");
    },
    onAnyModifyAtk(atk, source, target, move) {
      const abilityHolder = this.effectState.target;
      if (source.hasAbility("Tablets of Ruin"))
        return;
      if (!move.ruinedAtk)
        move.ruinedAtk = abilityHolder;
      if (move.ruinedAtk !== abilityHolder)
        return;
      this.debug("Tablets of Ruin Atk drop");
      return this.chainModify(0.80);
    },
    flags: {},
    name: "Tablets of Ruin",
    rating: 4.5,
    num: 284
  },
  tangledfeet: {
    onModifyAccuracyPriority: -1,
    onModifyAccuracy(accuracy, target) {
      if (typeof accuracy !== "number")
        return;
      if (target?.volatiles["confusion"]) {
        this.debug("Tangled Feet - decreasing accuracy");
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Tangled Feet",
    rating: 1,
    num: 77
  },
  tanglinghair: {
    onDamagingHit(damage, target, source, move) {
      if (this.checkMoveMakesContact(move, source, target, true)) {
        this.add("-ability", target, "Tangling Hair");
        this.boost({ spe: -1 }, source, target, null, true);
      }
    },
    flags: {},
    name: "Tangling Hair",
    rating: 2,
    num: 221
  },
  technician: {
    onBasePowerPriority: 30,
    onBasePower(basePower, attacker, defender, move) {
      const basePowerAfterMultiplier = this.modify(basePower, this.event.modifier);
      this.debug("Base Power: " + basePowerAfterMultiplier);
      if (basePowerAfterMultiplier <= 60) {
        this.debug("Technician boost");
        return this.chainModify(1.5);
      }
    },
    flags: {},
    name: "Technician",
    rating: 3.5,
    num: 101
  },
  telepathy: {
    onTryHit(target, source, move) {
      if (target !== source && target.isAlly(source) && move.category !== "Status") {
        this.add("-activate", target, "ability: Telepathy");
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Telepathy",
    rating: 0,
    num: 140
  },
  teraformzero: {
    onAfterTerastallization(pokemon) {
      if (pokemon.baseSpecies.name !== "Terapagos-Stellar")
        return;
      if (this.field.weather || this.field.terrain) {
        this.add("-ability", pokemon, "Teraform Zero");
        this.field.clearWeather();
        this.field.clearTerrain();
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1 },
    name: "Teraform Zero",
    rating: 3,
    num: 309
  },
  terashell: {
    onEffectiveness(typeMod, target, type, move) {
      if (!target || target.species.name !== "Terapagos-Terastal")
        return;
      if (this.effectState.resisted)
        return -1;
      if (move.category === "Status")
        return;
      if (!target.runImmunity(move.type))
        return;
      if (target.hp < target.maxhp)
        return;
      this.add("-activate", target, "ability: Tera Shell");
      this.effectState.resisted = true;
      return -1;
    },
    onAnyAfterMove() {
      this.effectState.resisted = false;
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, breakable: 1 },
    name: "Tera Shell",
    rating: 3.5,
    num: 308
  },
  terashift: {
    onPreStart(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Terapagos")
        return;
      if (pokemon.species.forme !== "Terastal") {
        this.add("-activate", pokemon, "ability: Tera Shift");
        pokemon.formeChange("Terapagos-Terastal", this.effect, true);
        pokemon.baseMaxhp = Math.floor(Math.floor(
          2 * pokemon.species.baseStats["hp"] + pokemon.set.ivs["hp"] + Math.floor(pokemon.set.evs["hp"] / 4) + 100
        ) * pokemon.level / 100 + 10);
        const newMaxHP = pokemon.baseMaxhp;
        pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp);
        pokemon.maxhp = newMaxHP;
        this.add("-heal", pokemon, pokemon.getHealth, "[silent]");
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1 },
    name: "Tera Shift",
    rating: 3,
    num: 307
  },
  teravolt: {
    onStart(pokemon) {
      this.add("-ability", pokemon, "Teravolt");
    },
    onModifyMove(move) {
      move.ignoreAbility = true;
    },
    flags: {},
    name: "Teravolt",
    rating: 3,
    num: 164
  },
  thermalexchange: {
    onDamagingHit(damage, target, source, move) {
      if (move.type === "Fire") {
        this.boost({ atk: 1 });
      }
    },
    onUpdate(pokemon) {
      if (pokemon.status === "brn") {
        this.add("-activate", pokemon, "ability: Thermal Exchange");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (status.id !== "brn")
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Thermal Exchange");
      }
      return false;
    },
    flags: { breakable: 1 },
    name: "Thermal Exchange",
    rating: 2.5,
    num: 270
  },
  thickfat: {
    onSourceModifyAtkPriority: 6,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Ice" || move.type === "Fire") {
        this.debug("Thick Fat weaken");
        return this.chainModify(0.5);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Ice" || move.type === "Fire") {
        this.debug("Thick Fat weaken");
        return this.chainModify(0.5);
      }
    },
    flags: { breakable: 1 },
    name: "Thick Fat",
    rating: 3.5,
    num: 47
  },
  tintedlens: {
    onModifyDamage(damage, source, target, move) {
      if (target.getMoveHitData(move).typeMod < 0) {
        this.debug("Tinted Lens boost");
        return this.chainModify(2);
      }
    },
    flags: {},
    name: "Tinted Lens",
    rating: 4,
    num: 110
  },
  torrent: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Water" && attacker.hp <= attacker.maxhp / 2) {
        this.debug("Torrent boost");
        return this.chainModify(1.4);
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, attacker, defender, move) {
      if (move.type === "Water" && attacker.hp <= attacker.maxhp / 2) {
        this.debug("Torrent boost");
        return this.chainModify(1.4);
      }
    },
    flags: {},
    name: "Torrent",
    rating: 2,
    num: 67
  },
  toughclaws: {
    onBasePowerPriority: 21,
    onBasePower(basePower, attacker, defender, move) {
      if (move.flags["contact"]) {
        return this.chainModify([5325, 4096]);
      }
    },
    flags: {},
    name: "Tough Claws",
    rating: 3.5,
    num: 181
  },
  toxicboost: {
    onBasePowerPriority: 19,
    onBasePower(basePower, attacker, defender, move) {
      if ((attacker.status === "psn" || attacker.status === "tox") && move.category === "Physical") {
        return this.chainModify(1.5);
      }
    },
	onDamage(damage, target, source, effect) {
    if (effect.id === 'psn' || effect.id === 'tox') {
      return false;
    }
  },
    flags: {},
    name: "Toxic Boost",
    rating: 3,
    num: 137
  },
  toxicchain: {
    onSourceDamagingHit(damage, target, source, move) {
      if (target.hasAbility("shielddust") || target.hasItem("covertcloak"))
        return;
      if (this.randomChance(3, 10)) {
        target.trySetStatus("tox", source);
      }
    },
    flags: {},
    name: "Toxic Chain",
    rating: 4.5,
    num: 305
  },
  toxicdebris: {
    onDamagingHit(damage, target, source, move) {
      const side = source.isAlly(target) ? source.side.foe : source.side;
      const toxicSpikes = side.sideConditions["toxicspikes"];
      if (move.category === "Physical" && (!toxicSpikes || toxicSpikes.layers < 2)) {
        this.add("-activate", target, "ability: Toxic Debris");
        side.addSideCondition("toxicspikes", target);
      }
    },
    flags: {},
    name: "Toxic Debris",
    rating: 3.5,
    num: 295
  },
  trace: {
    onStart(pokemon) {
      if (pokemon.adjacentFoes().some((foeActive) => foeActive.ability === "noability")) {
        this.effectState.gaveUp = true;
      }
      if (pokemon.hasItem("Ability Shield")) {
        this.add("-block", pokemon, "item: Ability Shield");
        this.effectState.gaveUp = true;
      }
    },
    onUpdate(pokemon) {
      if (!pokemon.isStarted || this.effectState.gaveUp)
        return;
      const possibleTargets = pokemon.adjacentFoes().filter(
        (target2) => !target2.getAbility().flags["notrace"] && target2.ability !== "noability"
      );
      if (!possibleTargets.length)
        return;
      const target = this.sample(possibleTargets);
      const ability = target.getAbility();
      if (pokemon.setAbility(ability)) {
        this.add("-ability", pokemon, ability, "[from] ability: Trace", "[of] " + target);
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1 },
    name: "Trace",
    rating: 2.5,
    num: 36
  },
  transistor: {
    onModifyAtkPriority: 5,
    onModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Electric") {
        this.debug("Transistor boost");
        return this.chainModify([5325, 4096]);
      }
    },
    onModifySpAPriority: 5,
    onModifySpA(atk, attacker, defender, move) {
      if (move.type === "Electric") {
        this.debug("Transistor boost");
        return this.chainModify([5325, 4096]);
      }
    },
    flags: {},
    name: "Transistor",
    rating: 3.5,
    num: 262
  },
  triage: {
    onModifyPriority(priority, pokemon, target, move) {
      if (move?.flags["heal"])
        return priority + 3;
    },
    flags: {},
    name: "Triage",
    rating: 3.5,
    num: 205
  },
  truant: {
    onStart(pokemon) {
      pokemon.removeVolatile("truant");
      if (pokemon.activeTurns && (pokemon.moveThisTurnResult !== void 0 || !this.queue.willMove(pokemon))) {
        pokemon.addVolatile("truant");
      }
    },
    onBeforeMovePriority: 9,
    onBeforeMove(pokemon, target, move) {
      if (move && move.category === 'Status' && move.flags['heal']) {
        return;
      }
      if (pokemon.removeVolatile("truant")) {
        this.add("cant", pokemon, "ability: Truant");
        return false;
      }
      pokemon.addVolatile("truant");
    },
	onUpdate(pokemon) {
      if (pokemon.volatiles["attract"]) {
        this.add("-activate", pokemon, "ability: Truant");
        pokemon.removeVolatile("attract");
        this.add("-end", pokemon, "move: Attract", "[from] ability: Truant");
      }
      if (pokemon.volatiles["taunt"]) {
        this.add("-activate", pokemon, "ability: Truant");
        pokemon.removeVolatile("taunt");
      }
    },
    onImmunity(type, pokemon) {
      if (type === "attract")
        return false;
    },
    onTryHit(pokemon, target, move) {
      if (move.id === "attract" || move.id === "captivate" || move.id === "taunt" || move.id === "partingshot") {
        this.add("-immune", pokemon, "[from] ability: Truant");
        return null;
      }
    },
    onTryBoost(boost, target, source, effect) {
      if (effect.name === "Intimidate" && boost.atk) {
        delete boost.atk;
        this.add("-fail", target, "unboost", "Attack", "[from] ability: Truant", "[of] " + target);
      }
    },
    condition: {},
    flags: {},
    name: "Truant",
    rating: -1,
    num: 54
  },
  turboblaze: {
    onStart(pokemon) {
      this.add("-ability", pokemon, "Turboblaze");
    },
    onModifyMove(move) {
      move.ignoreAbility = true;
    },
    flags: {},
    name: "Turboblaze",
    rating: 3,
    num: 163
  },
  unaware: {
    onAnyModifyBoost(boosts, pokemon) {
      const unawareUser = this.effectState.target;
      if (unawareUser === pokemon)
        return;
      if (unawareUser === this.activePokemon && pokemon === this.activeTarget) {
        boosts["def"] = 0;
        boosts["spd"] = 0;
        boosts["evasion"] = 0;
      }
      if (pokemon === this.activePokemon && unawareUser === this.activeTarget) {
        boosts["atk"] = 0;
        boosts["def"] = 0;
        boosts["spa"] = 0;
        boosts["accuracy"] = 0;
      }
    },
    flags: { breakable: 1 },
    name: "Unaware",
    rating: 4,
    num: 109
  },
  unburden: {
    onAfterUseItem(item, pokemon) {
      if (pokemon !== this.effectState.target)
        return;
      pokemon.addVolatile("unburden");
    },
    onTakeItem(item, pokemon) {
      pokemon.addVolatile("unburden");
    },
    onEnd(pokemon) {
      pokemon.removeVolatile("unburden");
    },
    condition: {
      onModifySpe(spe, pokemon) {
        if (!pokemon.item && !pokemon.ignoringAbility()) {
          return this.chainModify(2);
        }
      }
    },
    flags: {},
    name: "Unburden",
    rating: 3.5,
    num: 84
  },
	unnerve: {
    onPreStart(pokemon) {
      this.add('-ability', pokemon, 'Unnerve');
      this.effectState.unnerved = true;
    },
    onStart(pokemon) {
      if (this.effectState.unnerved) return;
      this.add('-ability', pokemon, 'Unnerve');
      this.effectState.unnerved = true;
    },
    onEnd() {
      this.effectState.unnerved = false;
    },
    onFoeTryEatItem() {
      return !this.effectState.unnerved;
    },
    flags: {},
    name: "Unnerve",
    rating: 1,
    num: 127,
  },
	unseenfist: {
    onModifyMove(move, source, target) {
        if (move.flags['contact']) {
            delete move.flags['protect'];
        }
    },
    onBeforeMove(source, target, move) {
        if (target.volatiles['stall'] && move.id !== 'gmaxoneblow' && move.id !== 'gmaxrapidflow') {
            source.addVolatile('unseenfistnerf');
        }
    },
    onModifyDamage(damage, source, target, move) {
        if (source.volatiles['unseenfistnerf'] && target.volatiles['stall']) {
            return this.chainModify(1/3);
        }
    },
    onResidual(source) {
        if (source.volatiles['unseenfistnerf']) {
            source.removeVolatile('unseenfistnerf');
        }
    },
    flags: {},
    name: "Unseen Fist",
    rating: 2,
    num: 260,
},
  vesselofruin: {
    onStart(pokemon) {
      if (this.suppressingAbility(pokemon))
        return;
      this.add("-ability", pokemon, "Vessel of Ruin");
    },
    onAnyModifySpA(spa, source, target, move) {
      const abilityHolder = this.effectState.target;
      if (source.hasAbility("Vessel of Ruin"))
        return;
      if (!move.ruinedSpA)
        move.ruinedSpA = abilityHolder;
      if (move.ruinedSpA !== abilityHolder)
        return;
      this.debug("Vessel of Ruin SpA drop");
      return this.chainModify(0.80);
    },
    flags: {},
    name: "Vessel of Ruin",
    rating: 4.5,
    num: 284
  },
  victorystar: {
    onAnyModifyAccuracyPriority: -1,
    onAnyModifyAccuracy(accuracy, target, source) {
      if (source.isAlly(this.effectState.target) && typeof accuracy === "number") {
        return this.chainModify([4506, 4096]);
      }
    },
    flags: {},
    name: "Victory Star",
    rating: 2,
    num: 162
  },
  vitalspirit: {
    onUpdate(pokemon) {
      if (pokemon.status === "slp") {
        this.add("-activate", pokemon, "ability: Vital Spirit");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (status.id !== "slp")
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Vital Spirit");
      }
      return false;
    },
    onTryAddVolatile(status, target) {
      if (status.id === "yawn") {
        this.add("-immune", target, "[from] ability: Vital Spirit");
        return null;
      }
    },
	onStart(pokemon) {
            this.add('-ability', pokemon, 'Vital Spirit');
        },
        onModifyAtkPriority: 5,
        onModifyAtk(atk, pokemon) {
            if (pokemon.status === 'brn') {
                return this.chainModify(1.5);
            }
        },
        onModifySpe(spe, pokemon) {
            if (pokemon.status === 'par') {
                return this.chainModify(1.5);
            }
        },
        onDamage(damage, target, source, effect) {
            if (effect.id === 'tox' || effect.id === 'psn') {
                return damage / 2;
            }
        },
		onModifySpAPriority: 5,
        onModifySpA(spa, pokemon) {
            if (pokemon.status === 'frz') {
                return this.chainModify(1.5);
            }
        },
    flags: { breakable: 1 },
    name: "Vital Spirit",
    rating: 1.5,
    num: 72
  },
  voltabsorb: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Electric") {
        if (!this.heal(target.baseMaxhp / 4)) {
          this.add("-immune", target, "[from] ability: Volt Absorb");
        }
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Volt Absorb",
    rating: 3.5,
    num: 10
  },
  wanderingspirit: {
    onDamagingHit(damage, target, source, move) {
      if (source.getAbility().flags["failskillswap"] || target.volatiles["dynamax"])
        return;
      if (this.checkMoveMakesContact(move, source, target)) {
        const targetCanBeSet = this.runEvent("SetAbility", target, source, this.effect, source.ability);
        if (!targetCanBeSet)
          return targetCanBeSet;
        const sourceAbility = source.setAbility("wanderingspirit", target);
        if (!sourceAbility)
          return;
        if (target.isAlly(source)) {
          this.add("-activate", target, "Skill Swap", "", "", "[of] " + source);
        } else {
          this.add("-activate", target, "ability: Wandering Spirit", this.dex.abilities.get(sourceAbility).name, "Wandering Spirit", "[of] " + source);
        }
        target.setAbility(sourceAbility);
      }
    },
    flags: {},
    name: "Wandering Spirit",
    rating: 2.5,
    num: 254
  },
  waterabsorb: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Water") {
        if (!this.heal(target.baseMaxhp / 4)) {
          this.add("-immune", target, "[from] ability: Water Absorb");
        }
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Water Absorb",
    rating: 3.5,
    num: 11
  },
  waterbubble: {
    onSourceModifyAtkPriority: 5,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Fire") {
        return this.chainModify(0.5);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Fire") {
        return this.chainModify(0.5);
      }
    },
    onModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Water") {
        return this.chainModify(2);
      }
    },
    onModifySpA(atk, attacker, defender, move) {
      if (move.type === "Water") {
        return this.chainModify(2);
      }
    },
    onUpdate(pokemon) {
      if (pokemon.status === "brn") {
        this.add("-activate", pokemon, "ability: Water Bubble");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (status.id !== "brn")
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Water Bubble");
      }
      return false;
    },
    flags: { breakable: 1 },
    name: "Water Bubble",
    rating: 4.5,
    num: 199
  },
  watercompaction: {
    onDamagingHit(damage, target, source, move) {
      if (move.type === "Water") {
        this.boost({ def: 2 });
      }
    },
    onSourceModifyAtkPriority: 5,
    onSourceModifyAtk(atk, attacker, defender, move) {
      if (move.type === "Water") {
        return this.chainModify(0);
      }
    },
    onSourceModifySpAPriority: 5,
    onSourceModifySpA(atk, attacker, defender, move) {
      if (move.type === "Water") {
        return this.chainModify(0);
      }
    },
    flags: {},
    name: "Water Compaction",
    rating: 1.5,
    num: 195
  },
	waterveil: {
		onStart(pokemon) {
      for (const ally of pokemon.alliesAndSelf()) {
        if (["brn"].includes(ally.status)) {
          this.add("-activate", pokemon, "ability: Water Veil");
          ally.cureStatus();
        }
      }
    },
    onUpdate(pokemon) {
      if (["brn"].includes(pokemon.status)) {
        this.add("-activate", pokemon, "ability: Water Veil");
        pokemon.cureStatus();
      }
    },
    onAllySwitchIn(pokemon) {
      if (["brn"].includes(pokemon.status)) {
        this.add("-activate", this.effectState.target, "ability: Water Veil");
        pokemon.cureStatus();
      }
    },
    onSetStatus(status, target, source, effect) {
      if (!["brn"].includes(status.id))
        return;
      if (effect?.status) {
        this.add("-immune", target, "[from] ability: Water Veil");
      }
      return false;
    },
    onAllySetStatus(status, target, source, effect) {
      if (!["brn"].includes(status.id))
        return;
      if (effect?.status) {
        const effectHolder = this.effectState.target;
        this.add("-block", target, "ability: Water Veil", "[of] " + effectHolder);
      }
      return false;
    },
	onSourceModifyDamage(damage, source, target, move) {
      if (move.type === 'Fire' && (target === this.effectState.target || target.side === this.effectState.target.side)) {
        this.debug('Water Veil - reducing damage');
        return this.chainModify(0.8);
      }
    },
		flags: {breakable: 1},
		name: "Water Veil",
		rating: 2,
		num: 41,
	},
  weakarmor: {
    onDamagingHit(damage, target, source, move) {
      if (move.category === "Physical") {
        this.boost({ def: -1, spe: 2 }, target, target);
      }
    },
    flags: {},
    name: "Weak Armor",
    rating: 1,
    num: 133
  },
  wellbakedbody: {
    onTryHit(target, source, move) {
      if (target !== source && move.type === "Fire") {
        if (!this.boost({ def: 2 })) {
          this.add("-immune", target, "[from] ability: Well-Baked Body");
        }
        return null;
      }
    },
    flags: { breakable: 1 },
    name: "Well-Baked Body",
    rating: 3.5,
    num: 273
  },
  whitesmoke: {
    onTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      let showMsg = false;
      let i;
      for (i in boost) {
        if (boost[i] < 0) {
          delete boost[i];
          showMsg = true;
        }
      }
      if (showMsg && !effect.secondaries && effect.id !== "octolock") {
        this.add("-fail", target, "unboost", "[from] ability: White Smoke", "[of] " + target);
      }
    },
    onAllyTryBoost(boost, target, source, effect) {
      if (source && target === source)
        return;
      let showMsg = false;
      let i;
      for (i in boost) {
        if (boost[i] < 0) {
          delete boost[i];
          showMsg = true;
        }
      }
      if (showMsg && !effect.secondaries && effect.id !== "octolock") {
        this.add("-fail", target, "unboost", "[from] ability: White Smoke", "[of] " + target);
      }
    },
    flags: { breakable: 1 },
    name: "White Smoke",
    rating: 2,
    num: 73
  },
  wimpout: {
    onEmergencyExit(target) {
      if (!this.canSwitch(target.side) || target.forceSwitchFlag || target.switchFlag)
        return;
      for (const side of this.sides) {
        for (const active of side.active) {
          active.switchFlag = false;
        }
      }
      target.switchFlag = true;
      this.add("-activate", target, "ability: Wimp Out");
    },
    flags: {},
    name: "Wimp Out",
    rating: 1,
    num: 193
  },
  windpower: {
    onDamagingHitOrder: 1,
    onDamagingHit(damage, target, source, move) {
      if (move.flags['wind'] && !target.volatiles['charge']) {
        target.addVolatile('charge');
        this.boost({spd: 1}, target);
      }
    },
    onAllySideConditionStart(target, source, sideCondition) {
      const pokemon = this.effectState.target;
      if (sideCondition.id === 'tailwind' && !pokemon.volatiles['charge']) {
        pokemon.addVolatile('charge');
        this.boost({spd: 1}, pokemon);
      }
    },
    onSourceModifyDamage(damage, source, target, move) {
      if (move.flags['wind']) {
        this.debug('Wind Power - reducing wind move damage');
        return this.chainModify(0.75);
      }
    },
    flags: {},
    name: "Wind Power",
    rating: 1,
    num: 277,
  },
  windrider: {
    onStart(pokemon) {
      if (pokemon.side.sideConditions["tailwind"]) {
        this.boost({ atk: 1 }, pokemon, pokemon);
      }
    },
    onTryHit(target, source, move) {
      if (target !== source && move.flags["wind"]) {
        if (!this.boost({ atk: 1 }, target, target)) {
          this.add("-immune", target, "[from] ability: Wind Rider");
        }
        return null;
      }
    },
    onAllySideConditionStart(target, source, sideCondition) {
      const pokemon = this.effectState.target;
      if (sideCondition.id === "tailwind") {
        this.boost({ atk: 1 }, pokemon, pokemon);
      }
    },
    flags: { breakable: 1 },
    name: "Wind Rider",
    rating: 3.5,
    // We do not want Brambleghast to get Infiltrator in Randbats
    num: 274
  },
	wonderguard: {
    onTryHit(target, source, move) {
        if (target === source || move.category === 'Status' || move.type === '???' || move.id === 'struggle') return;
        if (move.id === 'skydrop' && !source.volatiles['skydrop']) return;
        this.debug('Wonder Guard immunity: ' + move.id);
        if (target.runEffectiveness(move) <= 0) {
            if (move.smartTarget) {
                move.smartTarget = false;
            } else {
                this.add('-immune', target, '[from] ability: Wonder Guard');
            }
            return null;
        }
    },
    onAfterTerastallization(pokemon) {
        if (pokemon.terastallized) {
            const teraType = pokemon.teraType || pokemon.types[0];
            this.add('-start', pokemon, 'Tera Type', teraType);
            pokemon.addVolatile('teratypeboost');
        }

        pokemon.addVolatile('wogteraremove');
        if (pokemon.volatiles['wogteraremove']) {
            this.add('custom', '-endterastallize', pokemon);
            delete pokemon.terastallized;
            pokemon.removeVolatile('wogteraremove');
        }
    },
    onSwitchOut(pokemon) {
        if (pokemon.volatiles['teratypeboost']) {
            pokemon.teraBoostKept = true;
        }
    },
    onSwitchIn(pokemon) {
        if (pokemon.teraBoostKept) {
            this.add('-start', pokemon, 'Tera Type');
            pokemon.addVolatile('teratypeboost');
        }
    },
   onModifyDamage(damage, source, target, move) {
        if (source.volatiles['teratypeboost']) {
            const teraType = source.teraType || source.types[0];

            if (target.hasType(teraType)) {
                this.debug('Tera type boost for Wonder Guard: matching types');
                return this.chainModify(2);
            }
        }
    },
    flags: {failroleplay: 1, noreceiver: 1, noentrain: 1, failskillswap: 1, breakable: 1},
    name: "Wonder Guard",
    rating: 5,
    num: 25,
},
  wonderskin: {
    onModifyAccuracyPriority: 10,
    onModifyAccuracy(accuracy, target, source, move) {
      if (move.category === "Status" && typeof accuracy === "number") {
        this.debug("Wonder Skin - setting accuracy to 50");
        return 50;
      }
    },
    flags: { breakable: 1 },
    name: "Wonder Skin",
    rating: 2,
    num: 147
  },
  zenmode: {
    onStart(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Darmanitan" || pokemon.transformed) {
        return;
      }
      if (!["Zen", "Galar-Zen"].includes(pokemon.species.forme)) {
        pokemon.addVolatile("zenmode");
      }
      if (pokemon.species.name === "Darmanitan-Galar-Zen") {
        pokemon.abilityState.choiceLock = "";
      }
    },
    onResidualOrder: 29,
    onResidual(pokemon) {
      if (pokemon.species.name === "Darmanitan-Zen") {
        this.heal(pokemon.maxhp / 16);
      }
    },
    onBeforeMove(pokemon, target, move) {
      if (pokemon.species.name === "Darmanitan-Galar-Zen") {
        if (move.isZOrMaxPowered || move.id === "struggle") return;
        if (pokemon.abilityState.choiceLock && pokemon.abilityState.choiceLock !== move.id) {
          this.addMove("move", pokemon, move.name);
          this.attrLastMove("[still]");
          this.debug("Disabled by Zen Mode's choice lock");
          this.add("-fail", pokemon);
          return false;
        }
      }
    },
    onModifyMove(move, pokemon) {
      if (pokemon.species.name === "Darmanitan-Galar-Zen") {
        if (pokemon.abilityState.choiceLock || move.isZOrMaxPowered || move.id === "struggle") return;
        pokemon.abilityState.choiceLock = move.id;
      }
    },
    onDisableMove(pokemon) {
      if (pokemon.species.name === "Darmanitan-Galar-Zen") {
        if (!pokemon.abilityState.choiceLock) return;
        if (pokemon.volatiles["dynamax"]) return;
        for (const moveSlot of pokemon.moveSlots) {
          if (moveSlot.id !== pokemon.abilityState.choiceLock) {
            pokemon.disableMove(moveSlot.id, false, this.effectState.sourceEffect);
          }
        }
      }
    },
    onEnd(pokemon) {
      if (!pokemon.volatiles["zenmode"] || !pokemon.hp) return;
      pokemon.transformed = false;
      delete pokemon.volatiles["zenmode"];
      if (pokemon.species.baseSpecies === "Darmanitan" && pokemon.species.battleOnly) {
        pokemon.formeChange(pokemon.species.battleOnly, this.effect, false, "[silent]");
      }
  
      if (pokemon.species.name === "Darmanitan-Galar-Zen") {
        pokemon.abilityState.choiceLock = "";
      }
    },
    condition: {
      onStart(pokemon) {
        if (!pokemon.species.name.includes("Galar")) {
          if (pokemon.species.id !== "darmanitanzen") {
            pokemon.formeChange("Darmanitan-Zen");
          }
        } else {
          if (pokemon.species.id !== "darmanitangalarzen") {
            pokemon.formeChange("Darmanitan-Galar-Zen");
          }
        }
      },
      onEnd(pokemon) {
        if (["Zen", "Galar-Zen"].includes(pokemon.species.forme)) {
          return;
        }
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1 },
    name: "Zen Mode",
    rating: 0,
    num: 161
  },
  zerotohero: {
    onSwitchOut(pokemon) {
      if (pokemon.baseSpecies.baseSpecies !== "Palafin")
        return;
      if (pokemon.species.forme !== "Hero") {
        pokemon.formeChange("Palafin-Hero", this.effect, true);
      }
    },
    onSwitchIn() {
      this.effectState.switchingIn = true;
    },
    onStart(pokemon) {
      if (!this.effectState.switchingIn)
        return;
      this.effectState.switchingIn = false;
      if (pokemon.baseSpecies.baseSpecies !== "Palafin")
        return;
      if (!this.effectState.heroMessageDisplayed && pokemon.species.forme === "Hero") {
        this.add("-activate", pokemon, "ability: Zero to Hero");
        this.effectState.heroMessageDisplayed = true;
      }
    },
    flags: { failroleplay: 1, noreceiver: 1, noentrain: 1, notrace: 1, failskillswap: 1, cantsuppress: 1, notransform: 1 },
    name: "Zero to Hero",
    rating: 5,
    num: 278
  },
  // CAP
  mountaineer: {
    onDamage(damage, target, source, effect) {
      if (effect && effect.id === "stealthrock") {
        return false;
      }
    },
    onTryHit(target, source, move) {
      if (move.type === "Rock" && !target.activeTurns) {
        this.add("-immune", target, "[from] ability: Mountaineer");
        return null;
      }
    },
    isNonstandard: "CAP",
    flags: { breakable: 1 },
    name: "Mountaineer",
    rating: 3,
    num: -2
  },
  rebound: {
    isNonstandard: "CAP",
    onTryHitPriority: 1,
    onTryHit(target, source, move) {
      if (this.effectState.target.activeTurns)
        return;
      if (target === source || move.hasBounced || !move.flags["reflectable"]) {
        return;
      }
      const newMove = this.dex.getActiveMove(move.id);
      newMove.hasBounced = true;
      this.actions.useMove(newMove, target, source);
      return null;
    },
    onAllyTryHitSide(target, source, move) {
      if (this.effectState.target.activeTurns)
        return;
      if (target.isAlly(source) || move.hasBounced || !move.flags["reflectable"]) {
        return;
      }
      const newMove = this.dex.getActiveMove(move.id);
      newMove.hasBounced = true;
      this.actions.useMove(newMove, this.effectState.target, source);
      return null;
    },
    condition: {
      duration: 1
    },
    flags: { breakable: 1 },
    name: "Rebound",
    rating: 3,
    num: -3
  },
  persistent: {
    isNonstandard: "CAP",
    // implemented in the corresponding move
    flags: {},
    name: "Persistent",
    rating: 3,
    num: -4
  }
};
//# sourceMappingURL=abilities.js.map
