text: 'use strict';
  
  /**@type {{[k: string]: ModdedPureEffectData}} */
  let BattleStatuses = {
  	brn: {
  		name: 'brn',
  		id: 'brn',
  		num: 0,
  		effectType: 'Status',
  		onStart(target) {
  			this.add('-status', target, 'brn');
  		},
  		onAfterMoveSelfPriority: 3,
  		onAfterMoveSelf(pokemon) {
  			residualdmg(this, pokemon);
  		},
  		onAfterSwitchInSelf(pokemon) {
  			residualdmg(this, pokemon);
  		},
  	},
  	par: {
  		name: 'par',
  		id: 'par',
  		num: 0,
  		inherit: true,
  		onBeforeMovePriority: 2,
  		onBeforeMove(pokemon) {
  			if (this.randomChance(1, 4)) {
  				this.add('cant', pokemon, 'par');
  				return false;
  			}
  		},
  	},
  	slp: {
  		name: 'slp',
  		id: 'slp',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.effectType === 'Move') {
  				this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
  			} else {
  				this.add('-status', target, 'slp');
  			}
  			// 1-6 turns
  			this.effectData.time = this.random(2, 8);
  		},
  		onBeforeMovePriority: 10,
  		onBeforeMove(pokemon, target, move) {
  			pokemon.statusData.time--;
  			if (pokemon.statusData.time <= 0) {
  				pokemon.cureStatus();
  				return;
  			}
  			this.add('cant', pokemon, 'slp');
  			if (move.sleepUsable) {
  				return;
  			}
  			return false;
  		},
  	},
  	frz: {
  		name: 'frz',
  		id: 'frz',
  		num: 0,
  		inherit: true,
  		onBeforeMove(pokemon, target, move) {
  			if (move.flags['defrost']) return;
  			this.add('cant', pokemon, 'frz');
  			return false;
  		},
  		onModifyMove() {},
  		onHit() {},
  		onAfterMoveSecondary(target, source, move) {
  			if ((move.secondary && move.secondary.status === 'brn') || move.statusRoll === 'brn') {
  				target.cureStatus();
  			}
  		},
  		onAfterMoveSecondarySelf(pokemon, target, move) {
  			if (move.flags['defrost']) pokemon.cureStatus();
  		},
  		onResidual(pokemon) {
  			if (this.randomChance(25, 256)) pokemon.cureStatus();
  		},
  	},
  	psn: {
  		name: 'psn',
  		id: 'psn',
  		num: 0,
  		effectType: 'Status',
  		onStart(target) {
  			this.add('-status', target, 'psn');
  		},
  		onAfterMoveSelfPriority: 3,
  		onAfterMoveSelf(pokemon) {
  			residualdmg(this, pokemon);
  		},
  		onAfterSwitchInSelf(pokemon) {
  			residualdmg(this, pokemon);
  		},
  	},
  	tox: {
  		name: 'tox',
  		id: 'tox',
  		num: 0,
  		effectType: 'Status',
  		onStart(target) {
  			this.add('-status', target, 'tox');
  			if (!target.volatiles['residualdmg']) target.addVolatile('residualdmg');
  			target.volatiles['residualdmg'].counter = 0;
  		},
  		onAfterMoveSelfPriority: 3,
  		onAfterMoveSelf(pokemon) {
  			this.damage(this.dex.clampIntRange(Math.floor(pokemon.maxhp / 16), 1) * pokemon.volatiles['residualdmg'].counter, pokemon, pokemon);
  		},
  		onSwitchIn(pokemon) {
  			// Regular poison status and damage after a switchout -> switchin.
  			pokemon.status = /** @type {ID} */('psn');
  			this.add('-status', pokemon, 'psn', '[silent]');
  		},
  		onAfterSwitchInSelf(pokemon) {
  			this.damage(this.dex.clampIntRange(Math.floor(pokemon.maxhp / 16), 1));
  		},
  	},
  	confusion: {
  		inherit: true,
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.id === 'lockedmove') {
  				this.add('-start', target, 'confusion', '[silent]');
  			} else {
  				this.add('-start', target, 'confusion');
  			}
  			if (sourceEffect && sourceEffect.id === 'berserkgene') {
  				this.effectData.time = 256;
  			} else {
  				this.effectData.time = this.random(2, 6);
  			}
  		},
  		onBeforeMove(pokemon, target, move) {
  			pokemon.volatiles.confusion.time--;
  			if (!pokemon.volatiles.confusion.time) {
  				pokemon.removeVolatile('confusion');
  				return;
  			}
  			this.add('-activate', pokemon, 'confusion');
  			if (this.randomChance(1, 2)) {
  				return;
  			}
  			move = /** @type {ActiveMove} */ ({
  				basePower: 40,
  				type: '???',
  				baseMoveType: move.type,
  				category: 'Physical',
  				willCrit: false,
  				isSelfHit: true,
  				noDamageVariance: true,
  				flags: {},
  				selfdestruct: move.selfdestruct,
  			});
  			let damage = this.getDamage(pokemon, pokemon, move);
  			if (typeof damage !== 'number') throw new Error("Confusion damage not dealt");
  			this.directDamage(damage);
  			return false;
  		},
  	},
  	partiallytrapped: {
  		name: 'partiallytrapped',
  		id: 'partiallytrapped',
  		num: 0,
  		duration: 2,
  		onBeforeMovePriority: 4,
  		onBeforeMove(pokemon) {
  			this.add('cant', pokemon, 'partiallytrapped');
  			return false;
  		},
  	},
  	partialtrappinglock: {
  		name: 'partialtrappinglock',
  		id: 'partialtrappinglock',
  		num: 0,
  		durationCallback() {
  			let duration = this.sample([2, 2, 2, 3, 3, 3, 4, 5]);
  			return duration;
  		},
  		onResidual(target) {
  			if (target.lastMove && target.lastMove.id === 'struggle' || target.status === 'slp') {
  				delete target.volatiles['partialtrappinglock'];
  			}
  		},
  		onStart(target, source, effect) {
  			this.effectData.move = effect.id;
  		},
  		onDisableMove(pokemon) {
  			if (!pokemon.hasMove(this.effectData.move)) {
  				return;
  			}
  			for (const moveSlot of pokemon.moveSlots) {
  				if (moveSlot.id !== this.effectData.move) {
  					pokemon.disableMove(moveSlot.id);
  				}
  			}
  		},
  	},
  	lockedmove: {
  		name: 'lockedmove',
  		id: 'lockedmove',
  		num: 0,
  		// Outrage, Thrash, Petal Dance...
  		durationCallback() {
  			return this.random(2, 4);
  		},
  		onResidual(target) {
  			if ((target.lastMove && target.lastMove.id === 'struggle') || target.status === 'slp') {
  				// don't lock, and bypass confusion for calming
  				delete target.volatiles['lockedmove'];
  			}
  		},
  		onStart(target, source, effect) {
  			this.effectData.move = effect.id;
  		},
  		onEnd(target) {
  			// Confusion begins even if already confused
  			delete target.volatiles['confusion'];
  			target.addVolatile('confusion');
  		},
  		onLockMove(pokemon) {
  			return this.effectData.move;
  		},
  		onMoveAborted(pokemon) {
  			delete pokemon.volatiles['lockedmove'];
  		},
  		onBeforeTurn(pokemon) {
  			let move = this.dex.getMove(this.effectData.move);
  			if (move.id) {
  				this.debug('Forcing into ' + move.id);
  				this.queue.changeAction(pokemon, {choice: 'move', moveid: move.id});
  			}
  		},
  	},
  	sandstorm: {
  		inherit: true,
  		onWeather(target) {
  			this.damage(target.baseMaxhp / 8);
  		},
  	},
  	stall: {
  		name: 'stall',
  		id: 'stall',
  		num: 0,
  		duration: 2,
  		onStart() {
  			this.effectData.counter = 127;
  		},
  		onStallMove() {
  			let counter = Math.floor(this.effectData.counter) || 127;
  			this.debug("Success chance: " + Math.round(counter * 1000 / 255) / 10 + "% (" + counter + "/255)");
  			return this.randomChance(counter, 255);
  		},
  		onRestart() {
  			this.effectData.counter /= 2;
  			this.effectData.duration = 2;
  		},
  	},
  	residualdmg: {
  		name: 'residualdmg',
  		id: 'residualdmg',
  		num: 0,
  		onStart(target) {
  			target.volatiles['residualdmg'].counter = 0;
  		},
  		onAfterMoveSelfPriority: 100,
  		onAfterMoveSelf(pokemon) {
  			if (['brn', 'psn', 'tox'].includes(pokemon.status)) pokemon.volatiles['residualdmg'].counter++;
  		},
  		onAfterSwitchInSelf(pokemon) {
  			if (['brn', 'psn', 'tox'].includes(pokemon.status)) pokemon.volatiles['residualdmg'].counter++;
  		},
  	},
  };
  
  /**
   * @param {Battle} battle
   * @param {Pokemon} pokemon
   */
  function residualdmg(battle, pokemon) {
  	if (pokemon.volatiles['residualdmg']) {
  		battle.damage(battle.dex.clampIntRange(Math.floor(pokemon.maxhp / 16) * pokemon.volatiles['residualdmg'].counter, 1), pokemon);
  		battle.hint("In Gen 2, Toxic's counter is retained through Baton Pass/Heal Bell and applies to PSN/BRN.", true);
  	} else {
  		battle.damage(battle.dex.clampIntRange(Math.floor(pokemon.maxhp / 8), 1), pokemon);
  	}
  }
  
  exports.BattleStatuses = BattleStatuses;
  
  