text: 'use strict';
  
  /**@type {{[k: string]: PureEffectData}} */
  let BattleStatuses = {
  	brn: {
  		name: 'brn',
  		id: 'brn',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.id === 'flameorb') {
  				this.add('-status', target, 'brn', '[from] item: Flame Orb');
  			} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
  				this.add('-status', target, 'brn', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else {
  				this.add('-status', target, 'brn');
  			}
  		},
  		// Damage reduction is handled directly in the sim/battle.js damage function
  		onResidualOrder: 9,
  		onResidual(pokemon) {
  			this.damage(pokemon.baseMaxhp / 8);
  		},
  	},
  	par: {
  		name: 'par',
  		id: 'par',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.effectType === 'Ability') {
  				this.add('-status', target, 'par', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else {
  				this.add('-status', target, 'par');
  			}
  		},
  		onModifySpe(spe, pokemon) {
  			if (!pokemon.hasAbility('quickfeet')) {
  				return this.chainModify(0.5);
  			}
  		},
  		onBeforeMovePriority: 1,
  		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 === 'Ability') {
  				this.add('-status', target, 'slp', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else if (sourceEffect && sourceEffect.effectType === 'Move') {
  				this.add('-status', target, 'slp', '[from] move: ' + sourceEffect.name);
  			} else {
  				this.add('-status', target, 'slp');
  			}
  			// 1-3 turns
  			this.effectData.startTime = this.random(2, 5);
  			this.effectData.time = this.effectData.startTime;
  		},
  		onBeforeMovePriority: 10,
  		onBeforeMove(pokemon, target, move) {
  			if (pokemon.hasAbility('earlybird')) {
  				pokemon.statusData.time--;
  			}
  			pokemon.statusData.time--;
  			if (pokemon.statusData.time <= 0) {
  				pokemon.cureStatus();
  				return;
  			}
  			this.add('cant', pokemon, 'slp');
  			if (move.sleepUsable) {
  				return;
  			}
  			return false;
  		},
  	},
  	fsb: {
  		name: 'fsb',
  		id: 'fsb',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.id === 'frostorb') {
  				this.add('-status', target, 'fsb', '[from] item: Frost Orb');
  			} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
  				this.add('-status', target, 'fsb', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else {
  				this.add('-status', target, 'fsb');
  			}
  			this.add('-message', 'The target became Frostbitten!');
  		},
  		onBeforeMovePriority: 10,
  		onModifyMove(move, pokemon) {
  			if (move.flags['defrost']) {
  				this.add('-curestatus', pokemon, 'fsb', '[from] move: ' + move);
  				pokemon.setStatus('');
  			}
  		},
  		onHit(target, source, move) {
  			if (move.thawsTarget || move.type === 'Fire' && move.category !== 'Status') {
  				target.cureStatus();
  			}
  		},
  		// Damage reduction is handled directly in the sim/battle.js damage function
  		onResidualOrder: 9,
  		onResidual(pokemon) {
  			this.damage(pokemon.baseMaxhp / 8);
  			this.add('-message', 'The target is chilled by frostbite!');
  		},
  	},
  	psn: {
  		name: 'psn',
  		id: 'psn',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.effectType === 'Ability') {
  				this.add('-status', target, 'psn', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else {
  				this.add('-status', target, 'psn');
  			}
  		},
  		// Damage reduction is handled directly in the sim/battle.js damage function
  		onResidualOrder: 9,
  		onResidual(pokemon) {
  			this.damage(pokemon.baseMaxhp / 8);
  		},
  	},
  	tox: {
  		name: 'tox',
  		id: 'tox',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			this.effectData.stage = 0;
  			if (sourceEffect && sourceEffect.id === 'toxicorb') {
  				this.add('-status', target, 'tox', '[from] item: Toxic Orb');
  			} else if (sourceEffect && sourceEffect.effectType === 'Ability') {
  				this.add('-status', target, 'tox', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else {
  				this.add('-status', target, 'tox');
  			}
  		},
  		onSwitchIn() {
  			this.effectData.stage = 0;
  		},
  		onResidualOrder: 9,
  		onResidual(pokemon) {
  			if (this.effectData.stage < 15) {
  				this.effectData.stage++;
  			}
  			this.damage(this.dex.clampIntRange(pokemon.maxhp / 16, 1) * this.effectData.stage);
  		},
  	},
  	bewitchment: {
  		name: 'bewitchment',
  		id: 'bewitchment',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.effectType === 'Ability') {
  				this.add('-status', target, 'bewitchment', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else {
  				this.add('-status', target, 'bewitchment');
  			}
  			this.add('-message', 'The target became Bewitched!');
  		},
  		onModifySpD(spd, pokemon) {
  			return this.chainModify(0.33);
  		},
  		onBeforeMovePriority: 1,
  		onBeforeMove(pokemon) {
  			if (this.randomChance(1, 4)) {
  				this.add('cant', pokemon, 'bewitchment');
  				return false;
  			}
  		},
  	},
  	whiplash: {
  		name: 'whiplash',
  		id: 'whiplash',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.effectType === 'Ability') {
  				this.add('-status', target, 'whiplash', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else {
  				this.add('-status', target, 'whiplash');
  			}
  			this.add('-message', 'The target became Whiplashed!');
  		},
  		onModifyDef(def, pokemon) {
  			return this.chainModify(0.33);
  		},
  		onResidualOrder: 9,
  		onResidual(pokemon) {
  			this.damage(pokemon.baseMaxhp / 8);
  			this.add('-message', 'The target suffers from Whiplash!');
  		},
  	},
  	blindness: {
  		name: 'blindness',
  		id: 'blindness',
  		num: 0,
  		effectType: 'Status',
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.effectType === 'Ability') {
  				this.add('-status', target, 'blindness', '[from] ability: ' + sourceEffect.name, '[of] ' + source);
  			} else {
  				this.add('-status', target, 'blindness');
  			}
  			this.add('-message', 'The target became Blinded!');
  			this.effectData.startTime = this.random(2, 5);
  			this.effectData.time = this.effectData.startTime;
  		},
  		onModifyMovePriority: 1,
  		onModifyMove(move) {
  			if (typeof move.accuracy === 'number') {
  				move.accuracy *= 0.7;
  			}
  		},
  		onBeforeMovePriority: 10,
  		onBeforeMove(pokemon, target, move) {
  			pokemon.statusData.time--;
  			if (pokemon.statusData.time <= 0) {
  				pokemon.cureStatus();
  				this.add('-message', 'The target became able to see!');
  				return;
  			}
  			return;
  		},
  		onHit(target, source, move) {
  			if (move.thawsTarget || move.type === 'Water' && move.category !== 'Status') {
  				target.cureStatus();
  				this.add('-message', 'The target became able to see!');
  			}
  		},
  	},
  	confusion: {
  		name: 'confusion',
  		id: 'confusion',
  		num: 0,
  		// this is a volatile status
  		onStart(target, source, sourceEffect) {
  			if (sourceEffect && sourceEffect.id === 'lockedmove') {
  				this.add('-start', target, 'confusion', '[fatigue]');
  			} else {
  				this.add('-start', target, 'confusion');
  			}
  			this.effectData.time = this.random(2, 6);
  		},
  		onEnd(target) {
  			this.add('-end', target, 'confusion');
  		},
  		onBeforeMovePriority: 3,
  		onBeforeMove(pokemon) {
  			pokemon.volatiles.confusion.time--;
  			if (!pokemon.volatiles.confusion.time) {
  				pokemon.removeVolatile('confusion');
  				return;
  			}
  			this.add('-activate', pokemon, 'confusion');
  			if (!this.randomChance(1, 3)) {
  				return;
  			}
  			this.activeTarget = pokemon;
  			let damage = this.getDamage(pokemon, pokemon, 40);
  			if (typeof damage !== 'number') throw new Error("Confusion damage not dealt");
  			this.damage(damage, pokemon, pokemon, /** @type {ActiveMove} */ ({
  				id: 'confused',
  				effectType: 'Move',
  				type: '???',
  			}));
  			return false;
  		},
  	},
  	flinch: {
  		name: 'flinch',
  		id: 'flinch',
  		num: 0,
  		duration: 1,
  		onBeforeMovePriority: 8,
  		onBeforeMove(pokemon) {
  			this.add('cant', pokemon, 'flinch');
  			this.runEvent('Flinch', pokemon);
  			return false;
  		},
  	},
  	trapped: {
  		name: 'trapped',
  		id: 'trapped',
  		num: 0,
  		noCopy: true,
  		onTrapPokemon(pokemon) {
  			pokemon.tryTrap();
  		},
  		onStart(target) {
  			this.add('-activate', target, 'trapped');
  		},
  	},
  	trapper: {
  		name: 'trapper',
  		id: 'trapper',
  		num: 0,
  		noCopy: true,
  	},
  	partiallytrapped: {
  		name: 'partiallytrapped',
  		id: 'partiallytrapped',
  		num: 0,
  		duration: 3,
  		durationCallback(target, source) {
  			if (source && source.hasItem('gripclaw')) return 6;
  			return this.random(2, 3, 4);
  		},
  		onStart(pokemon, source) {
  			this.add('-activate', pokemon, 'move: ' + this.effectData.sourceEffect, '[of] ' + source);
  		},
  		onResidualOrder: 11,
  		onResidual(pokemon) {
  			const source = this.effectData.source;
  			if (source && (!source.isActive || source.hp <= 0 || !source.activeTurns)) {
  				delete pokemon.volatiles['partiallytrapped'];
  				this.add('-end', pokemon, this.effectData.sourceEffect, '[partiallytrapped]', '[silent]');
  				return;
  			}
  			if (source.hasItem('bindingband')) {
  				this.damage(pokemon.baseMaxhp / 6);
  			} else {
  				this.damage(pokemon.baseMaxhp / 8);
  			}
  		},
  		onEnd(pokemon) {
  			this.add('-end', pokemon, this.effectData.sourceEffect, '[partiallytrapped]');
  		},
  		onTrapPokemon(pokemon) {
  			if (this.effectData.source && this.effectData.source.isActive) pokemon.tryTrap();
  		},
  	},
  	lockedmove: {
  		// Outrage, Thrash, Petal Dance...
  		name: 'lockedmove',
  		id: 'lockedmove',
  		num: 0,
  		duration: 2,
  		onResidual(target) {
  			if (target.status === 'slp') {
  				// don't lock, and bypass confusion for calming
  				delete target.volatiles['lockedmove'];
  			}
  			this.effectData.trueDuration--;
  		},
  		onStart(target, source, effect) {
  			this.effectData.trueDuration = this.random(2, 4);
  			this.effectData.move = effect.id;
  		},
  		onRestart() {
  			if (this.effectData.trueDuration >= 2) {
  				this.effectData.duration = 2;
  			}
  		},
  		onEnd(target) {
  			if (this.effectData.trueDuration > 1) return;
  			target.addVolatile('confusion');
  		},
  		onLockMove(pokemon) {
  			if (pokemon.volatiles['dynamax']) return;
  			return this.effectData.move;
  		},
  	},
  	twoturnmove: {
  		// Skull Bash, SolarBeam, Sky Drop...
  		name: 'twoturnmove',
  		id: 'twoturnmove',
  		num: 0,
  		duration: 2,
  		onStart(target, source, effect) {
  			this.effectData.move = effect.id;
  			target.addVolatile(effect.id, source);
  			this.attrLastMove('[still]');
  		},
  		onEnd(target) {
  			target.removeVolatile(this.effectData.move);
  		},
  		onLockMove() {
  			return this.effectData.move;
  		},
  		onMoveAborted(pokemon) {
  			pokemon.removeVolatile('twoturnmove');
  		},
  	},
  	choicelock: {
  		name: 'choicelock',
  		noCopy: true,
  		onStart(pokemon) {
  			if (!this.activeMove) throw new Error("Battle.activeMove is null");
  			if (!this.activeMove.id || this.activeMove.hasBounced) return false;
  			this.effectData.move = this.activeMove.id;
  		},
  		onBeforeMove(pokemon, target, move) {
  			if (!pokemon.getItem().isChoice) {
  				pokemon.removeVolatile('choicelock');
  				return;
  			}
  			if (
  				!pokemon.ignoringItem() && !pokemon.volatiles['dynamax'] &&
  				move.id !== this.effectData.move && move.id !== 'struggle'
  			) {
  				// Fails unless the Choice item is being ignored, and no PP is lost
  				this.addMove('move', pokemon, move.name);
  				this.attrLastMove('[still]');
  				this.debug("Disabled by Choice item lock");
  				this.add('-fail', pokemon);
  				return false;
  			}
  		},
  		onDisableMove(pokemon) {
  			if (!pokemon.getItem().isChoice || !pokemon.hasMove(this.effectData.move)) {
  				pokemon.removeVolatile('choicelock');
  				return;
  			}
  			if (pokemon.ignoringItem() || pokemon.volatiles['dynamax']) {
  				return;
  			}
  			for (const moveSlot of pokemon.moveSlots) {
  				if (moveSlot.id !== this.effectData.move) {
  					pokemon.disableMove(moveSlot.id, false, this.effectData.sourceEffect);
  				}
  			}
  		},
  	},
  	mustrecharge: {
  		name: 'mustrecharge',
  		id: 'mustrecharge',
  		num: 0,
  		duration: 2,
  		onBeforeMovePriority: 11,
  		onBeforeMove(pokemon) {
  			this.add('cant', pokemon, 'recharge');
  			pokemon.removeVolatile('mustrecharge');
  			pokemon.removeVolatile('truant');
  			return null;
  		},
  		onAfterMove(pokemon, target) {
  			if (!target || target.fainted || target.hp <= 0) {
  				pokemon.removeVolatile('mustrecharge');
  			} else {
  				this.add('-mustrecharge', pokemon);
  			}
  			
  		},
  		onLockMove: 'recharge',
  	},
  	futuremove: {
  		// this is a slot condition
  		name: 'futuremove',
  		id: 'futuremove',
  		num: 0,
  		duration: 3,
  		onResidualOrder: 3,
  		onEnd(target) {
  			const data = this.effectData;
  			// time's up; time to hit! :D
  			const move = this.dex.getMove(data.move);
  			if (target.fainted || target === data.source) {
  				this.hint(`${move.name} did not hit because the target is ${(data.fainted ? 'fainted' : 'the user')}.`);
  				return;
  			}
  
  			this.add('-end', target, 'move: ' + move.name);
  			target.removeVolatile('Protect');
  			target.removeVolatile('Endure');
  
  			if (data.source.hasAbility('infiltrator') && this.gen >= 6) {
  				data.moveData.infiltrates = true;
  			}
  			if (data.source.hasAbility('normalize') && this.gen >= 6) {
  				data.moveData.type = 'Normal';
  			}
  			if (data.source.hasAbility('adaptability') && this.gen >= 6) {
  				data.moveData.stab = 2;
  			}
  			const hitMove = new this.dex.Data.Move(data.moveData);
  
  			this.trySpreadMoveHit([target], data.source, /** @type {ActiveMove} */(/** @type {unknown} */(hitMove)));
  		},
  	},
  	healreplacement: {
  		// this is a slot condition
  		name: 'healreplacement',
  		id: 'healreplacement',
  		num: 0,
  		onStart(side, source, sourceEffect) {
  			this.effectData.sourceEffect = sourceEffect;
  			this.add('-activate', source, 'healreplacement');
  		},
  		onSwitchInPriority: 1,
  		onSwitchIn(target) {
  			if (!target.fainted) {
  				target.heal(target.maxhp);
  				this.add('-heal', target, target.getHealth, '[from] move: ' + this.effectData.sourceEffect, '[zeffect]');
  				target.side.removeSlotCondition(target, 'healreplacement');
  			}
  		},
  	},
  	stall: {
  		// Protect, Detect, Endure counter
  		name: 'stall',
  		id: 'stall',
  		num: 0,
  		duration: 2,
  		counterMax: 729,
  		onStart() {
  			this.effectData.counter = 3;
  		},
  		onStallMove(pokemon) {
  			// this.effectData.counter should never be undefined here.
  			// However, just in case, use 1 if it is undefined.
  			let counter = this.effectData.counter || 1;
  			this.debug("Success chance: " + Math.round(100 / counter) + "%");
  			let success = this.randomChance(1, counter);
  			if (!success) delete pokemon.volatiles['stall'];
  			return success;
  		},
  		onRestart() {
  			// @ts-ignore
  			if (this.effectData.counter < this.effect.counterMax) {
  				this.effectData.counter *= 3;
  			}
  			this.effectData.duration = 2;
  		},
  	},
  	gem: {
  		name: 'gem',
  		id: 'gem',
  		num: 0,
  		duration: 1,
  		affectsFainted: true,
  		onBasePower(basePower, user, target, move) {
  			this.debug('Gem Boost');
  			return this.chainModify([0x14CD, 0x1000]);
  		},
  	},
  
  	// weather is implemented here since it's so important to the game
  
  	raindance: {
  		name: 'RainDance',
  		id: 'raindance',
  		num: 0,
  		effectType: 'Weather',
  		duration: 5,
  		durationCallback(source, effect) {
  			if (source && source.hasItem('damprock')) {
  				return 8;
  			}
  			return 5;
  		},
  		onWeatherModifyDamage(damage, attacker, defender, move) {
  			if (move.type === 'Water') {
  				if (defender.hasItem('utilityumbrella')) return;
  				this.debug('rain water boost');
  				return this.chainModify(1.25);
  			}
  			if (move.type === 'Fire') {
  				this.debug('rain fire suppress');
  				return this.chainModify(0.5);
  			}
  		},
  		onStart(battle, source, effect) {
  			if (effect && effect.effectType === 'Ability') {
  				if (this.gen <= 5) this.effectData.duration = 0;
  				this.add('-weather', 'RainDance', '[from] ability: ' + effect, '[of] ' + source);
  			} else {
  				this.add('-weather', 'RainDance');
  			}
  		},
  		onResidualOrder: 1,
  		onResidual() {
  			this.add('-weather', 'RainDance', '[upkeep]');
  			this.eachEvent('Weather');
  		},
  		onEnd() {
  			this.add('-weather', 'none');
  		},
  	},
  	primordialsea: {
  		name: 'PrimordialSea',
  		id: 'primordialsea',
  		num: 0,
  		effectType: 'Weather',
  		duration: 0,
  		onTryMovePriority: 1,
  		onTryMove(attacker, defender, move) {
  			if (move.type === 'Fire' && move.category !== 'Status') {
  				this.debug('Primordial Sea fire suppress');
  				this.add('-fail', attacker, move, '[from] Primordial Sea');
  				this.attrLastMove('[still]');
  				return null;
  			}
  		},
  		onWeatherModifyDamage(damage, attacker, defender, move) {
  			if (move.type === 'Water') {
  				if (defender.hasItem('utilityumbrella')) return;
  				this.debug('Rain water boost');
  				return this.chainModify(1.25);
  			}
  		},
  		onStart(battle, source, effect) {
  			this.add('-weather', 'PrimordialSea', '[from] ability: ' + effect, '[of] ' + source);
  		},
  		onResidualOrder: 1,
  		onResidual() {
  			this.add('-weather', 'PrimordialSea', '[upkeep]');
  			this.eachEvent('Weather');
  		},
  		onEnd() {
  			this.add('-weather', 'none');
  		},
  	},
  	sunnyday: {
  		name: 'SunnyDay',
  		id: 'sunnyday',
  		num: 0,
  		effectType: 'Weather',
  		duration: 5,
  		durationCallback(source, effect) {
  			if (source && source.hasItem('heatrock')) {
  				return 8;
  			}
  			return 5;
  		},
  		onWeatherModifyDamage(damage, attacker, defender, move) {
  			if (move.type === 'Fire') {
  				if (defender.hasItem('utilityumbrella')) return;
  				this.debug('Sunny Day fire boost');
  				return this.chainModify(1.25);
  			}
  			if (move.type === 'Water') {
  				if (defender.hasItem('utilityumbrella')) return;
  				this.debug('Sunny Day water suppress');
  				return this.chainModify(0.5);
  			}
  		},
  		onStart(battle, source, effect) {
  			if (effect && effect.effectType === 'Ability') {
  				if (this.gen <= 5) this.effectData.duration = 0;
  				this.add('-weather', 'SunnyDay', '[from] ability: ' + effect, '[of] ' + source);
  			} else {
  				this.add('-weather', 'SunnyDay');
  			}
  		},
  		onImmunity(type, pokemon) {
  			if (pokemon.hasItem('utilityumbrella')) return;
  		},
  		onResidualOrder: 1,
  		onResidual() {
  			this.add('-weather', 'SunnyDay', '[upkeep]');
  			this.eachEvent('Weather');
  		},
  		onEnd() {
  			this.add('-weather', 'none');
  		},
  	},
  	desolateland: {
  		name: 'DesolateLand',
  		id: 'desolateland',
  		num: 0,
  		effectType: 'Weather',
  		duration: 0,
  		onTryMovePriority: 1,
  		onTryMove(attacker, defender, move) {
  			if (move.type === 'Water' && move.category !== 'Status') {
  				this.debug('Desolate Land water suppress');
  				this.add('-fail', attacker, move, '[from] Desolate Land');
  				this.attrLastMove('[still]');
  				return null;
  			}
  		},
  		onWeatherModifyDamage(damage, attacker, defender, move) {
  			if (move.type === 'Fire') {
  				if (defender.hasItem('utilityumbrella')) return;
  				this.debug('Sunny Day fire boost');
  				return this.chainModify(1.25);
  			}
  		},
  		onStart(battle, source, effect) {
  			this.add('-weather', 'DesolateLand', '[from] ability: ' + effect, '[of] ' + source);
  		},
  		onImmunity(type, pokemon) {
  			if (pokemon.hasItem('utilityumbrella')) return;
  		},
  		onResidualOrder: 1,
  		onResidual() {
  			this.add('-weather', 'DesolateLand', '[upkeep]');
  			this.eachEvent('Weather');
  		},
  		onEnd() {
  			this.add('-weather', 'none');
  		},
  	},
  	sandstorm: {
  		name: 'Sandstorm',
  		id: 'sandstorm',
  		num: 0,
  		effectType: 'Weather',
  		duration: 5,
  		durationCallback(source, effect) {
  			if (source && source.hasItem('smoothrock')) {
  				return 8;
  			}
  			return 5;
  		},
  		// This should be applied directly to the stat before any of the other modifiers are chained
  		// So we give it increased priority.
  		onModifySpDPriority: 10,
  		onModifySpD(spd, pokemon) {
  			if (pokemon.hasItem('utilityumbrella')) return;
  			if (pokemon.hasType('Rock') && this.field.isWeather('sandstorm')) {
  				return this.modify(spd, 1.25);
  			}
  		},
  		onStart(battle, source, effect) {
  			if (effect && effect.effectType === 'Ability') {
  				if (this.gen <= 5) this.effectData.duration = 0;
  				this.add('-weather', 'Sandstorm', '[from] ability: ' + effect, '[of] ' + source);
  			} else {
  				this.add('-weather', 'Sandstorm');
  			}
  		},
  		onResidualOrder: 1,
  		onResidual() {
  			this.add('-weather', 'Sandstorm', '[upkeep]');
  			if (this.field.isWeather('sandstorm')) this.eachEvent('Weather');
  		},
  		onWeather(target) {
  			if (target.hasItem('utilityumbrella')) return;
  			this.damage(target.baseMaxhp / 16);
  		},
  		onEnd() {
  			this.add('-weather', 'none');
  		},
  	},
  	hail: {
  		name: 'Hail',
  		id: 'hail',
  		num: 0,
  		effectType: 'Weather',
  		duration: 5,
  		durationCallback(source, effect) {
  			if (source && source.hasItem('icyrock')) {
  				return 8;
  			}
  			return 5;
  		},
  		onStart(battle, source, effect) {
  			if (effect && effect.effectType === 'Ability') {
  				if (this.gen <= 5) this.effectData.duration = 0;
  				this.add('-weather', 'Hail', '[from] ability: ' + effect, '[of] ' + source);
  			} else {
  				this.add('-weather', 'Hail');
  			}
  		},
  		// This should be applied directly to the stat before any of the other modifiers are chained
  		// So we give it increased priority.
  		onModifyDefPriority: 10,
  		onModifyDef(def, pokemon) {
  			if (pokemon.hasItem('utilityumbrella')) return;
  			if (pokemon.hasType('Ice') && this.field.isWeather('hail')) {
  				return this.modify(def, 1.25);
  			}
  		},
  		onResidualOrder: 1,
  		onResidual() {
  			this.add('-weather', 'Hail', '[upkeep]');
  			if (this.field.isWeather('hail')) this.eachEvent('Weather');
  		},
  		onWeather(target) {
  			if (target.hasItem('utilityumbrella')) return;
  			this.damage(target.baseMaxhp / 16);
  		},
  		onEnd() {
  			this.add('-weather', 'none');
  		},
  	},
  	maelstrom: {
  		name: 'Maelstrom',
  		id: 'maelstrom',
  		num: 0,
  		effectType: 'Weather',
  		duration: 5,
  		durationCallback(source, effect) {
  			if (source && source.hasItem('calamitystone')) {
  				return 8;
  			}
  			return 5;
  		},
  		onStart(battle, source, effect) {
  			if (effect && effect.effectType === 'Ability') {
  				if (this.gen <= 5) this.effectData.duration = 0;
  				this.add('-weather', 'Maelstrom', '[from] ability: ' + effect, '[of] ' + source);
  			} else {
  				this.add('-weather', 'Maelstrom');
  			}
  			this.add('-message', 'The Maelstrom turned disastrous!');
  		},
  		onResidualOrder: 1,
  		onResidual() {
  			this.add('-weather', 'Maelstrom', '[upkeep]');
  			this.add('-message', 'The Maelstrom is swirling about!');
  			if (this.field.isWeather('maelstrom')) this.eachEvent('Weather');
  		},
  		onWeather(target) {
  			if (target.hasItem('utilityumbrella')) return;
  			this.damage(target.baseMaxhp / 8);
  		},
  		onEnd() {
  			this.add('-weather', 'none');
  			this.add('-message', 'The Maelstrom died down.');
  		},
  	},
  	deltastream: {
  		name: 'DeltaStream',
  		id: 'deltastream',
  		num: 0,
  		effectType: 'Weather',
  		duration: 0,
  		onEffectiveness(typeMod, target, type, move) {
  			if (target && target.hasItem('utilityumbrella')) return;
  			if (move && move.effectType === 'Move' && move.category !== 'Status' && type === 'Flying' && typeMod > 0) {
  				this.add('-activate', '', 'deltastream');
  				return 0;
  			}
  		},
  		onStart(battle, source, effect) {
  			this.add('-weather', 'DeltaStream', '[from] ability: ' + effect, '[of] ' + source);
  		},
  		onResidualOrder: 1,
  		onResidual() {
  			this.add('-weather', 'DeltaStream', '[upkeep]');
  			this.eachEvent('Weather');
  		},
  		onEnd() {
  			this.add('-weather', 'none');
  		},
  	},
  
  	dynamax: {
  		name: 'Dynamax',
  		id: 'dynamax',
  		num: 0,
  		noCopy: true,
  		duration: 3,
  		onStart(pokemon) {
  			pokemon.removeVolatile('substitute');
  			if (pokemon.illusion) this.singleEvent('End', this.dex.getAbility('Illusion'), pokemon.abilityData, pokemon);
  			this.add('-start', pokemon, 'Dynamax');
  			if (pokemon.canGigantamax) pokemon.formeChange(pokemon.canGigantamax);
  			if (pokemon.species === 'Shedinja') return;
  
  			// Changes based on dynamax level, 1.5 is max (at LVL 5)
  			const ratio = 1.5;
  
  			pokemon.maxhp = Math.floor(pokemon.maxhp * ratio);
  			pokemon.hp = Math.ceil(pokemon.hp * ratio);
  			this.add('-heal', pokemon, pokemon.getHealth, '[silent]');
  		},
  		onTryAddVolatile(status, pokemon) {
  			if (status.id === 'flinch') return null;
  		},
  		onBeforeSwitchOut(pokemon) {
  			pokemon.removeVolatile('dynamax');
  		},
  		onSourceModifyDamage(damage, source, target, move) {
  			if (move.id === 'behemothbash' || move.id === 'behemothblade' || move.id === 'dynamaxcannon' || move.id === 'emaxeternalenergy') {
  				return this.chainModify(2);
  			}
  		},
  		// onAnyModifyBoost
  		onDragOutPriority: 2,
  		onDragOut(pokemon) {
  			this.add('-block', pokemon, 'Dynamax');
  			return null;
  		},
  		onEnd(pokemon) {
  			this.add('-end', pokemon, 'Dynamax');
  			if (pokemon.canGigantamax) pokemon.formeChange(pokemon.baseTemplate.species);
  			if (pokemon.species === 'Shedinja') return;
  			pokemon.hp = pokemon.getUndynamaxedHP();
  			pokemon.maxhp = pokemon.baseMaxhp;
  			this.add('-heal', pokemon, pokemon.getHealth, '[silent]');
  		},
  	},
  
  	// Arceus and Silvally's actual typing is implemented here.
  	// Their true typing for all their formes is Normal, and it's only
  	// Multitype and RKS System, respectively, that changes their type,
  	// but their formes are specified to be their corresponding type
  	// in the Pokedex, so that needs to be overridden.
  	// This is mainly relevant for Hackmons Cup and Balanced Hackmons.
  	arceus: {
  		name: 'Arceus',
  		id: 'arceus',
  		num: 493,
  		onTypePriority: 1,
  		onType(types, pokemon) {
  			if (pokemon.transformed || pokemon.ability !== 'multitype' && this.gen >= 8) return types;
  			/** @type {string | undefined} */
  			let type = 'Normal';
  			if (pokemon.ability === 'multitype') {
  				type = pokemon.getItem().onPlate;
  				if (!type) {
  					type = 'Normal';
  				}
  			}
  			return [type];
  		},
  	},
  	silvally: {
  		name: 'Silvally',
  		id: 'silvally',
  		num: 773,
  		onTypePriority: 1,
  		onType(types, pokemon) {
  			if (pokemon.transformed || pokemon.ability !== 'rkssystem' && this.gen >= 8) return types;
  			/** @type {string | undefined} */
  			let type = 'Normal';
  			if (pokemon.ability === 'rkssystem') {
  				type = pokemon.getItem().onMemory;
  				if (!type) {
  					type = 'Normal';
  				}
  			}
  			return [type];
  		},
  	},
  };
  
  exports.BattleStatuses = BattleStatuses;
  