/**
 * Pokemon Showdown Dex Data
 *
 * A collection of data and definitions for src/battle-dex.ts.
 *
 * Larger data has their own files in data/, so this is just for small
 * miscellaneous data that doesn't need its own file.
 *
 * Licensing note: PS's client has complicated licensing:
 * - The client as a whole is AGPLv3
 * - The battle replay/animation engine (battle-*.ts) by itself is MIT
 *
 * @author Guangcong Luo <guangcongluo@gmail.com>
 * @license MIT
 */

import { Dex, toID } from "./battle-dex";

/**
 * String that contains only lowercase alphanumeric characters.
 */
export type ID = string & { __isID: true };

export interface Nature {
	plus?: StatNameExceptHP;
	minus?: StatNameExceptHP;
}

export const BattleNatures: { [k in NatureName]: Nature } = {
	Adamant: {
		plus: 'atk',
		minus: 'spa',
	},
	Bashful: {},
	Bold: {
		plus: 'def',
		minus: 'atk',
	},
	Brave: {
		plus: 'atk',
		minus: 'spe',
	},
	Calm: {
		plus: 'spd',
		minus: 'atk',
	},
	Careful: {
		plus: 'spd',
		minus: 'spa',
	},
	Docile: {},
	Gentle: {
		plus: 'spd',
		minus: 'def',
	},
	Hardy: {},
	Hasty: {
		plus: 'spe',
		minus: 'def',
	},
	Impish: {
		plus: 'def',
		minus: 'spa',
	},
	Jolly: {
		plus: 'spe',
		minus: 'spa',
	},
	Lax: {
		plus: 'def',
		minus: 'spd',
	},
	Lonely: {
		plus: 'atk',
		minus: 'def',
	},
	Mild: {
		plus: 'spa',
		minus: 'def',
	},
	Modest: {
		plus: 'spa',
		minus: 'atk',
	},
	Naive: {
		plus: 'spe',
		minus: 'spd',
	},
	Naughty: {
		plus: 'atk',
		minus: 'spd',
	},
	Quiet: {
		plus: 'spa',
		minus: 'spe',
	},
	Quirky: {},
	Rash: {
		plus: 'spa',
		minus: 'spd',
	},
	Relaxed: {
		plus: 'def',
		minus: 'spe',
	},
	Sassy: {
		plus: 'spd',
		minus: 'spe',
	},
	Serious: {},
	Timid: {
		plus: 'spe',
		minus: 'atk',
	},
};
export const BattleStatIDs: { [k: string]: StatName | undefined } = {
	HP: 'hp',
	hp: 'hp',
	Atk: 'atk',
	atk: 'atk',
	Def: 'def',
	def: 'def',
	SpA: 'spa',
	SAtk: 'spa',
	SpAtk: 'spa',
	spa: 'spa',
	spc: 'spa',
	Spc: 'spa',
	SpD: 'spd',
	SDef: 'spd',
	SpDef: 'spd',
	spd: 'spd',
	Spe: 'spe',
	Spd: 'spe',
	spe: 'spe',
};
/** Stat short names */
export const BattleStatNames = {
	hp: 'HP',
	atk: 'Atk',
	def: 'Def',
	spa: 'SpA',
	spd: 'SpD',
	spe: 'Spe',
} as const;

export const BattleBaseSpeciesChart = [
	"unown", "burmy", "shellos", "gastrodon", "deerling", "sawsbuck", "vivillon", "flabebe", "floette", "florges", "furfrou", "minior", "alcremie", "tatsugiri", "pokestarufo", "pokestarbrycenman", "pokestarmt", "pokestarmt2", "pokestartransport", "pokestargiant", "pokestarhumanoid", "pokestarmonster", "pokestarf00", "pokestarf002", "pokestarspirit", "pokestarblackdoor", "pokestarwhitedoor", "pokestarblackbelt",
] as ID[];

export const BattlePokemonIconIndexes: { [id: string]: number } = {
	// alt forms
	egg: 1032 + 1,
	pikachubelle: 1032 + 2,
	pikachulibre: 1032 + 3,
	pikachuphd: 1032 + 4,
	pikachupopstar: 1032 + 5,
	pikachurockstar: 1032 + 6,
	pikachucosplay: 1032 + 7,
	unownexclamation: 1032 + 8,
	unownquestion: 1032 + 9,
	unownb: 1032 + 10,
	unownc: 1032 + 11,
	unownd: 1032 + 12,
	unowne: 1032 + 13,
	unownf: 1032 + 14,
	unowng: 1032 + 15,
	unownh: 1032 + 16,
	unowni: 1032 + 17,
	unownj: 1032 + 18,
	unownk: 1032 + 19,
	unownl: 1032 + 20,
	unownm: 1032 + 21,
	unownn: 1032 + 22,
	unowno: 1032 + 23,
	unownp: 1032 + 24,
	unownq: 1032 + 25,
	unownr: 1032 + 26,
	unowns: 1032 + 27,
	unownt: 1032 + 28,
	unownu: 1032 + 29,
	unownv: 1032 + 30,
	unownw: 1032 + 31,
	unownx: 1032 + 32,
	unowny: 1032 + 33,
	unownz: 1032 + 34,
	castformrainy: 1032 + 35,
	castformsnowy: 1032 + 36,
	castformsunny: 1032 + 37,
	deoxysattack: 1032 + 38,
	deoxysdefense: 1032 + 39,
	deoxysspeed: 1032 + 40,
	burmysandy: 1032 + 41,
	burmytrash: 1032 + 42,
	wormadamsandy: 1032 + 43,
	wormadamtrash: 1032 + 44,
	cherrimsunshine: 1032 + 45,
	shelloseast: 1032 + 46,
	gastrodoneast: 1032 + 47,
	rotomfan: 1032 + 48,
	rotomfrost: 1032 + 49,
	rotomheat: 1032 + 50,
	rotommow: 1032 + 51,
	rotomwash: 1032 + 52,
	giratinaorigin: 1032 + 53,
	shayminsky: 1032 + 54,
	unfezantf: 1032 + 55,
	basculinbluestriped: 1032 + 56,
	darmanitanzen: 1032 + 57,
	deerlingautumn: 1032 + 58,
	deerlingsummer: 1032 + 59,
	deerlingwinter: 1032 + 60,
	sawsbuckautumn: 1032 + 61,
	sawsbucksummer: 1032 + 62,
	sawsbuckwinter: 1032 + 63,
	frillishf: 1032 + 64,
	jellicentf: 1032 + 65,
	tornadustherian: 1032 + 66,
	thundurustherian: 1032 + 67,
	landorustherian: 1032 + 68,
	kyuremblack: 1032 + 69,
	kyuremwhite: 1032 + 70,
	keldeoresolute: 1032 + 71,
	meloettapirouette: 1032 + 72,
	vivillonarchipelago: 1032 + 73,
	vivilloncontinental: 1032 + 74,
	vivillonelegant: 1032 + 75,
	vivillonfancy: 1032 + 76,
	vivillongarden: 1032 + 77,
	vivillonhighplains: 1032 + 78,
	vivillonicysnow: 1032 + 79,
	vivillonjungle: 1032 + 80,
	vivillonmarine: 1032 + 81,
	vivillonmodern: 1032 + 82,
	vivillonmonsoon: 1032 + 83,
	vivillonocean: 1032 + 84,
	vivillonpokeball: 1032 + 85,
	vivillonpolar: 1032 + 86,
	vivillonriver: 1032 + 87,
	vivillonsandstorm: 1032 + 88,
	vivillonsavanna: 1032 + 89,
	vivillonsun: 1032 + 90,
	vivillontundra: 1032 + 91,
	pyroarf: 1032 + 92,
	flabebeblue: 1032 + 93,
	flabebeorange: 1032 + 94,
	flabebewhite: 1032 + 95,
	flabebeyellow: 1032 + 96,
	floetteblue: 1032 + 97,
	floetteeternal: 1032 + 98,
	floetteorange: 1032 + 99,
	floettewhite: 1032 + 100,
	floetteyellow: 1032 + 101,
	florgesblue: 1032 + 102,
	florgesorange: 1032 + 103,
	florgeswhite: 1032 + 104,
	florgesyellow: 1032 + 105,
	furfroudandy: 1032 + 106,
	furfroudebutante: 1032 + 107,
	furfroudiamond: 1032 + 108,
	furfrouheart: 1032 + 109,
	furfroukabuki: 1032 + 110,
	furfroulareine: 1032 + 111,
	furfroumatron: 1032 + 112,
	furfroupharaoh: 1032 + 113,
	furfroustar: 1032 + 114,
	meowsticf: 1032 + 115,
	aegislashblade: 1032 + 116,
	xerneasneutral: 1032 + 117,
	hoopaunbound: 1032 + 118,
	rattataalola: 1032 + 119,
	raticatealola: 1032 + 120,
	raichualola: 1032 + 121,
	sandshrewalola: 1032 + 122,
	sandslashalola: 1032 + 123,
	vulpixalola: 1032 + 124,
	ninetalesalola: 1032 + 125,
	diglettalola: 1032 + 126,
	dugtrioalola: 1032 + 127,
	meowthalola: 1032 + 128,
	persianalola: 1032 + 129,
	geodudealola: 1032 + 130,
	graveleralola: 1032 + 131,
	golemalola: 1032 + 132,
	grimeralola: 1032 + 133,
	mukalola: 1032 + 134,
	exeggutoralola: 1032 + 135,
	marowakalola: 1032 + 136,
	greninjaash: 1032 + 137,
	zygarde10: 1032 + 138,
	zygardecomplete: 1032 + 139,
	oricoriopompom: 1032 + 140,
	oricoriopau: 1032 + 141,
	oricoriosensu: 1032 + 142,
	lycanrocmidnight: 1032 + 143,
	wishiwashischool: 1032 + 144,
	miniormeteor: 1032 + 145,
	miniororange: 1032 + 146,
	minioryellow: 1032 + 147,
	miniorgreen: 1032 + 148,
	miniorblue: 1032 + 149,
	miniorindigo: 1032 + 150,
	miniorviolet: 1032 + 151,
	magearnaoriginal: 1032 + 152,
	pikachuoriginal: 1032 + 153,
	pikachuhoenn: 1032 + 154,
	pikachusinnoh: 1032 + 155,
	pikachuunova: 1032 + 156,
	pikachukalos: 1032 + 157,
	pikachualola: 1032 + 158,
	pikachupartner: 1032 + 159,
	lycanrocdusk: 1032 + 160,
	necrozmaduskmane: 1032 + 161,
	necrozmadawnwings: 1032 + 162,
	necrozmaultra: 1032 + 163,
	pikachustarter: 1032 + 164,
	eeveestarter: 1032 + 165,
	meowthgalar: 1032 + 166,
	ponytagalar: 1032 + 167,
	rapidashgalar: 1032 + 168,
	farfetchdgalar: 1032 + 169,
	weezinggalar: 1032 + 170,
	mrmimegalar: 1032 + 171,
	corsolagalar: 1032 + 172,
	zigzagoongalar: 1032 + 173,
	linoonegalar: 1032 + 174,
	darumakagalar: 1032 + 175,
	darmanitangalar: 1032 + 176,
	darmanitangalarzen: 1032 + 177,
	yamaskgalar: 1032 + 178,
	stunfiskgalar: 1032 + 179,
	cramorantgulping: 1032 + 180,
	cramorantgorging: 1032 + 181,
	toxtricitylowkey: 1032 + 182,
	alcremierubycream: 1032 + 183,
	alcremiematchacream: 1032 + 184,
	alcremiemintcream: 1032 + 185,
	alcremielemoncream: 1032 + 186,
	alcremiesaltedcream: 1032 + 187,
	alcremierubyswirl: 1032 + 188,
	alcremiecaramelswirl: 1032 + 189,
	alcremierainbowswirl: 1032 + 190,
	eiscuenoice: 1032 + 191,
	indeedeef: 1032 + 192,
	morpekohangry: 1032 + 193,
	zaciancrowned: 1032 + 194,
	zamazentacrowned: 1032 + 195,
	slowpokegalar: 1032 + 196,
	slowbrogalar: 1032 + 197,
	zarudedada: 1032 + 198,
	pikachuworld: 1032 + 199,
	articunogalar: 1032 + 200,
	zapdosgalar: 1032 + 201,
	moltresgalar: 1032 + 202,
	slowkinggalar: 1032 + 203,
	calyrexice: 1032 + 204,
	calyrexshadow: 1032 + 205,
	growlithehisui: 1032 + 206,
	arcaninehisui: 1032 + 207,
	voltorbhisui: 1032 + 208,
	electrodehisui: 1032 + 209,
	typhlosionhisui: 1032 + 210,
	qwilfishhisui: 1032 + 211,
	sneaselhisui: 1032 + 212,
	samurotthisui: 1032 + 213,
	lilliganthisui: 1032 + 214,
	zoruahisui: 1032 + 215,
	zoroarkhisui: 1032 + 216,
	braviaryhisui: 1032 + 217,
	sliggoohisui: 1032 + 218,
	goodrahisui: 1032 + 219,
	avalugghisui: 1032 + 220,
	decidueyehisui: 1032 + 221,
	basculegionf: 1032 + 222,
	enamorustherian: 1032 + 223,
	taurospaldeacombat: 1032 + 224,
	taurospaldeablaze: 1032 + 225,
	taurospaldeaaqua: 1032 + 226,
	wooperpaldea: 1032 + 227,
	oinkolognef: 1032 + 228,
	palafinhero: 1032 + 229,
	mausholdfour: 1032 + 230,
	tatsugiridroopy: 1032 + 231,
	tatsugiristretchy: 1032 + 232,
	squawkabillyblue: 1032 + 233,
	squawkabillyyellow: 1032 + 234,
	squawkabillywhite: 1032 + 235,
	gimmighoulroaming: 1032 + 236,
	dialgaorigin: 1032 + 237,
	palkiaorigin: 1032 + 238,
	basculinwhitestriped: 1032 + 239,
	ursalunabloodmoon: 1032 + 240,
	ogerponwellspring: 1032 + 241,
	ogerponhearthflame: 1032 + 242,
	ogerponcornerstone: 1032 + 243,
	terapagosterastal: 1032 + 244,
	terapagosstellar: 1032 + 245,

	arceusbug: 1032 + 246,
	arceusdark: 1032 + 247,
	arceusdragon: 1032 + 248,
	arceuselectric: 1032 + 249,
	arceusfairy: 1032 + 250,
	arceusfighting: 1032 + 251,
	arceusfire: 1032 + 252,
	arceusflying: 1032 + 253,
	arceusghost: 1032 + 254,
	arceusgrass: 1032 + 255,
	arceusground: 1032 + 256,
	arceusice: 1032 + 257,
	arceuspoison: 1032 + 258,
	arceuspsychic: 1032 + 259,
	arceusrock: 1032 + 260,
	arceussteel: 1032 + 261,
	arceuswater: 1032 + 262,

	genesectdouse: 1032 + 263,
	genesectshock: 1032 + 264,
	genesectburn: 1032 + 265,
	genesectchill: 1032 + 266,

	silvallybug: 1032 + 267,
	silvallydark: 1032 + 268,
	silvallydragon: 1032 + 269,
	silvallyelectric: 1032 + 270,
	silvallyfairy: 1032 + 271,
	silvallyfighting: 1032 + 272,
	silvallyfire: 1032 + 273,
	silvallyflying: 1032 + 274,
	silvallyghost: 1032 + 275,
	silvallygrass: 1032 + 276,
	silvallyground: 1032 + 277,
	silvallyice: 1032 + 278,
	silvallypoison: 1032 + 279,
	silvallypsychic: 1032 + 280,
	silvallyrock: 1032 + 281,
	silvallysteel: 1032 + 282,
	silvallywater: 1032 + 283,

	// alt forms with duplicate icons
	greninjabond: 658,
	gumshoostotem: 735,
	raticatealolatotem: 1032 + 120,
	marowakalolatotem: 1032 + 136,
	araquanidtotem: 752,
	lurantistotem: 754,
	salazzletotem: 758,
	vikavolttotem: 738,
	togedemarutotem: 777,
	mimikyutotem: 778,
	mimikyubustedtotem: 778,
	ribombeetotem: 743,
	kommoototem: 784,
	sinisteaantique: 854,
	polteageistantique: 855,
	poltchageistartisan: 1012,
	sinistchamasterpiece: 1013,
	ogerpontealtera: 1017,
	ogerponwellspringtera: 1032 + 241,
	ogerponhearthflametera: 1032 + 242,
	ogerponcornerstonetera: 1032 + 243,
	toxtricitylowkeygmax: 1320 + 69,

	// Mega/G-Max
	venusaurmega: 1320 + 0,
	charizardmegax: 1320 + 1,
	charizardmegay: 1320 + 2,
	blastoisemega: 1320 + 3,
	beedrillmega: 1320 + 4,
	pidgeotmega: 1320 + 5,
	alakazammega: 1320 + 6,
	slowbromega: 1320 + 7,
	gengarmega: 1320 + 8,
	kangaskhanmega: 1320 + 9,
	pinsirmega: 1320 + 10,
	gyaradosmega: 1320 + 11,
	aerodactylmega: 1320 + 12,
	mewtwomegax: 1320 + 13,
	mewtwomegay: 1320 + 14,
	ampharosmega: 1320 + 15,
	steelixmega: 1320 + 16,
	scizormega: 1320 + 17,
	heracrossmega: 1320 + 18,
	houndoommega: 1320 + 19,
	tyranitarmega: 1320 + 20,
	sceptilemega: 1320 + 21,
	blazikenmega: 1320 + 22,
	swampertmega: 1320 + 23,
	gardevoirmega: 1320 + 24,
	sableyemega: 1320 + 25,
	mawilemega: 1320 + 26,
	aggronmega: 1320 + 27,
	medichammega: 1320 + 28,
	manectricmega: 1320 + 29,
	sharpedomega: 1320 + 30,
	cameruptmega: 1320 + 31,
	altariamega: 1320 + 32,
	banettemega: 1320 + 33,
	absolmega: 1320 + 34,
	glaliemega: 1320 + 35,
	salamencemega: 1320 + 36,
	metagrossmega: 1320 + 37,
	latiasmega: 1320 + 38,
	latiosmega: 1320 + 39,
	kyogreprimal: 1320 + 40,
	groudonprimal: 1320 + 41,
	rayquazamega: 1320 + 42,
	lopunnymega: 1320 + 43,
	garchompmega: 1320 + 44,
	lucariomega: 1320 + 45,
	abomasnowmega: 1320 + 46,
	gallademega: 1320 + 47,
	audinomega: 1320 + 48,
	dianciemega: 1320 + 49,
	charizardgmax: 1320 + 50,
	butterfreegmax: 1320 + 51,
	pikachugmax: 1320 + 52,
	meowthgmax: 1320 + 53,
	machampgmax: 1320 + 54,
	gengargmax: 1320 + 55,
	kinglergmax: 1320 + 56,
	laprasgmax: 1320 + 57,
	eeveegmax: 1320 + 58,
	snorlaxgmax: 1320 + 59,
	garbodorgmax: 1320 + 60,
	melmetalgmax: 1320 + 61,
	corviknightgmax: 1320 + 62,
	orbeetlegmax: 1320 + 63,
	drednawgmax: 1320 + 64,
	coalossalgmax: 1320 + 65,
	flapplegmax: 1320 + 66,
	appletungmax: 1320 + 67,
	sandacondagmax: 1320 + 68,
	toxtricitygmax: 1320 + 69,
	centiskorchgmax: 1320 + 70,
	hatterenegmax: 1320 + 71,
	grimmsnarlgmax: 1320 + 72,
	alcremiegmax: 1320 + 73,
	copperajahgmax: 1320 + 74,
	duraludongmax: 1320 + 75,
	eternatuseternamax: 1320 + 76,
	venusaurgmax: 1320 + 77,
	blastoisegmax: 1320 + 78,
	rillaboomgmax: 1320 + 79,
	cinderacegmax: 1320 + 80,
	inteleongmax: 1320 + 81,
	urshifugmax: 1320 + 82,
	urshifurapidstrikegmax: 1320 + 83,
	clefablemega: 1320 + 84,
	victreebelmega: 1320 + 85,
	starmiemega: 1320 + 86,
	dragonitemega: 1320 + 87,
	meganiummega: 1320 + 88,
	feraligatrmega: 1320 + 89,
	skarmorymega: 1320 + 90,
	froslassmega: 1320 + 91,
	emboarmega: 1320 + 92,
	excadrillmega: 1320 + 93,
	scolipedemega: 1320 + 94,
	scraftymega: 1320 + 95,
	eelektrossmega: 1320 + 96,
	chandeluremega: 1320 + 97,
	chesnaughtmega: 1320 + 98,
	delphoxmega: 1320 + 99,
	greninjamega: 1320 + 100,
	pyroarmega: 1320 + 101,
	floettemega: 1320 + 102,
	malamarmega: 1320 + 103,
	barbaraclemega: 1320 + 104,
	dragalgemega: 1320 + 105,
	hawluchamega: 1320 + 106,
	zygardemega: 1320 + 107,
	drampamega: 1320 + 108,
	falinksmega: 1320 + 109,
	raichumegax: 1320 + 110,
	raichumegay: 1320 + 111,
	chimechomega: 1320 + 112,
	absolmegaz: 1320 + 113,
	staraptormega: 1320 + 114,
	garchompmegaz: 1320 + 115,
	lucariomegaz: 1320 + 116,
	heatranmega: 1320 + 117,
	darkraimega: 1320 + 118,
	golurkmega: 1320 + 119,
	meowsticmmega: 1320 + 120,
	meowsticfmega: 1320 + 120,
	crabominablemega: 1320 + 121,
	golisopodmega: 1320 + 122,
	magearnamega: 1320 + 123,
	magearnaoriginalmega: 1320 + 124,
	zeraoramega: 1320 + 125,
	scovillainmega: 1320 + 126,
	glimmoramega: 1320 + 127,
	tatsugiricurlymega: 1320 + 128,
	tatsugiridroopymega: 1320 + 129,
	tatsugiristretchymega: 1320 + 130,
	baxcaliburmega: 1320 + 131,

	// CAP
	syclant: 1560 + 0,
	revenankh: 1560 + 1,
	pyroak: 1560 + 2,
	fidgit: 1560 + 3,
	stratagem: 1560 + 4,
	arghonaut: 1560 + 5,
	kitsunoh: 1560 + 6,
	cyclohm: 1560 + 7,
	colossoil: 1560 + 8,
	krilowatt: 1560 + 9,
	voodoom: 1560 + 10,
	tomohawk: 1560 + 11,
	necturna: 1560 + 12,
	mollux: 1560 + 13,
	aurumoth: 1560 + 14,
	malaconda: 1560 + 15,
	cawmodore: 1560 + 16,
	volkraken: 1560 + 17,
	plasmanta: 1560 + 18,
	naviathan: 1560 + 19,
	crucibelle: 1560 + 20,
	crucibellemega: 1560 + 21,
	kerfluffle: 1560 + 22,
	pajantom: 1560 + 23,
	jumbao: 1560 + 24,
	caribolt: 1560 + 25,
	smokomodo: 1560 + 26,
	snaelstrom: 1560 + 27,
	equilibra: 1560 + 28,
	astrolotl: 1560 + 29,
	miasmaw: 1560 + 30,
	chromera: 1560 + 31,
	venomicon: 1560 + 32,
	venomiconepilogue: 1560 + 33,
	saharaja: 1560 + 34,
	hemogoblin: 1560 + 35,
	syclar: 1560 + 36,
	embirch: 1560 + 37,
	flarelm: 1560 + 38,
	breezi: 1560 + 39,
	scratchet: 1560 + 40,
	necturine: 1560 + 41,
	cupra: 1560 + 42,
	argalis: 1560 + 43,
	brattler: 1560 + 44,
	cawdet: 1560 + 45,
	volkritter: 1560 + 46,
	snugglow: 1560 + 47,
	floatoy: 1560 + 48,
	caimanoe: 1560 + 49,
	pluffle: 1560 + 50,
	rebble: 1560 + 51,
	tactite: 1560 + 52,
	privatyke: 1560 + 53,
	nohface: 1560 + 54,
	monohm: 1560 + 55,
	duohm: 1560 + 56,
	protowatt: 1560 + 57,
	voodoll: 1560 + 58,
	mumbao: 1560 + 59,
	fawnifer: 1560 + 60,
	electrelk: 1560 + 61,
	smogecko: 1560 + 62,
	smoguana: 1560 + 63,
	swirlpool: 1560 + 64,
	coribalis: 1560 + 65,
	justyke: 1560 + 66,
	solotl: 1560 + 67,
	miasmite: 1560 + 68,
	dorsoil: 1560 + 69,
	saharascal: 1560 + 70,
	ababo: 1560 + 71,
	scattervein: 1560 + 72,
	cresceidon: 1560 + 73,
	chuggalong: 1560 + 74,
	shox: 1560 + 75,
	chuggon: 1560 + 76,
	draggalong: 1560 + 77,
	ramnarok: 1560 + 78,
	ramnarokradiant: 1560 + 79,
	flox: 1560 + 80,
};

export const BattlePokemonIconIndexesLeft: { [id: string]: number } = {
	pikachubelle: 1452 + 0,
	pikachupopstar: 1452 + 1,
	clefairy: 1452 + 2,
	clefable: 1452 + 3,
	jigglypuff: 1452 + 4,
	wigglytuff: 1452 + 5,
	dugtrioalola: 1452 + 6,
	poliwhirl: 1452 + 7,
	poliwrath: 1452 + 8,
	mukalola: 1452 + 9,
	kingler: 1452 + 10,
	croconaw: 1452 + 11,
	cleffa: 1452 + 12,
	igglybuff: 1452 + 13,
	politoed: 1452 + 14,
	unownb: 1452 + 15,
	unownc: 1452 + 16,
	unownd: 1452 + 17,
	unowne: 1452 + 18,
	unownf: 1452 + 19,
	unowng: 1452 + 20,
	unownh: 1452 + 21,
	unownj: 1452 + 22,
	unownk: 1452 + 23,
	unownl: 1452 + 24,
	unownm: 1452 + 25,
	unownn: 1452 + 26,
	unownp: 1452 + 27,
	unownq: 1452 + 28,
	unownquestion: 1452 + 29,
	unownr: 1452 + 30,
	unowns: 1452 + 31,
	unownt: 1452 + 32,
	unownv: 1452 + 33,
	unownz: 1452 + 34,
	sneasel: 1452 + 35,
	teddiursa: 1452 + 36,
	roselia: 1452 + 37,
	zangoose: 1452 + 38,
	seviper: 1452 + 39,
	castformsnowy: 1452 + 40,
	absolmega: 1452 + 41,
	absol: 1452 + 42,
	regirock: 1452 + 43,
	torterra: 1452 + 44,
	budew: 1452 + 45,
	roserade: 1452 + 46,
	magmortar: 1452 + 47,
	togekiss: 1452 + 48,
	rotomwash: 1452 + 49,
	shayminsky: 1452 + 50,
	emboar: 1452 + 51,
	pansear: 1452 + 52,
	simisear: 1452 + 53,
	drilbur: 1452 + 54,
	excadrill: 1452 + 55,
	sawk: 1452 + 56,
	lilligant: 1452 + 57,
	garbodor: 1452 + 58,
	solosis: 1452 + 59,
	vanilluxe: 1452 + 60,
	amoonguss: 1452 + 61,
	klink: 1452 + 62,
	klang: 1452 + 63,
	klinklang: 1452 + 64,
	litwick: 1452 + 65,
	golett: 1452 + 66,
	golurk: 1452 + 67,
	kyuremblack: 1452 + 68,
	kyuremwhite: 1452 + 69,
	kyurem: 1452 + 70,
	keldeoresolute: 1452 + 71,
	meloetta: 1452 + 72,
	greninja: 1452 + 73,
	greninjabond: 1452 + 73,
	greninjaash: 1452 + 74,
	furfroudebutante: 1452 + 75,
	barbaracle: 1452 + 76,
	clauncher: 1452 + 77,
	clawitzer: 1452 + 78,
	sylveon: 1452 + 79,
	klefki: 1452 + 80,
	zygarde: 1452 + 81,
	zygarde10: 1452 + 82,
	zygardecomplete: 1452 + 83,
	dartrix: 1452 + 84,
	steenee: 1452 + 85,
	tsareena: 1452 + 86,
	comfey: 1452 + 87,
	miniormeteor: 1452 + 88,
	minior: 1452 + 89,
	miniororange: 1452 + 90,
	minioryellow: 1452 + 91,
	miniorgreen: 1452 + 92,
	miniorblue: 1452 + 93,
	miniorviolet: 1452 + 94,
	miniorindigo: 1452 + 95,
	dhelmise: 1452 + 96,
	necrozma: 1452 + 97,
	marshadow: 1452 + 98,
	pikachuoriginal: 1452 + 99,
	pikachupartner: 1452 + 100,
	necrozmaduskmane: 1452 + 101,
	necrozmadawnwings: 1452 + 102,
	necrozmaultra: 1452 + 103,
	stakataka: 1452 + 104,
	blacephalon: 1452 + 105,
};

export const BattleAvatarNumbers: { [k: string]: string } = {
	1: 'lucas',
	2: 'dawn',
	3: 'youngster-gen4dp',
	4: 'lass-gen4dp',
	5: 'camper',
	6: 'picnicker',
	7: 'bugcatcher-gen4dp',
	8: 'aromalady',
	9: 'twins-gen4dp',
	10: 'hiker-gen4',
	11: 'battlegirl-gen4',
	12: 'fisherman-gen4',
	13: 'cyclist-gen4',
	14: 'cyclistf-gen4',
	15: 'blackbelt-gen4dp',
	16: 'artist-gen4',
	17: 'pokemonbreeder-gen4',
	18: 'pokemonbreederf-gen4',
	19: 'cowgirl',
	20: 'jogger',
	21: 'pokefan-gen4',
	22: 'pokefanf-gen4',
	23: 'pokekid',
	24: 'youngcouple-gen4dp',
	25: 'acetrainer-gen4dp',
	26: 'acetrainerf-gen4dp',
	27: 'waitress-gen4',
	28: 'veteran-gen4',
	29: 'ninjaboy',
	30: 'dragontamer',
	31: 'birdkeeper-gen4dp',
	32: 'doubleteam',
	33: 'richboy-gen4',
	34: 'lady-gen4',
	35: 'gentleman-gen4dp',
	36: 'madame-gen4dp',
	37: 'beauty-gen4dp',
	38: 'collector',
	39: 'policeman-gen4',
	40: 'pokemonranger-gen4',
	41: 'pokemonrangerf-gen4',
	42: 'scientist-gen4dp',
	43: 'swimmer-gen4dp',
	44: 'swimmerf-gen4dp',
	45: 'tuber',
	46: 'tuberf',
	47: 'sailor',
	48: 'sisandbro',
	49: 'ruinmaniac',
	50: 'psychic-gen4',
	51: 'psychicf-gen4',
	52: 'gambler',
	53: 'guitarist-gen4',
	54: 'acetrainersnow',
	55: 'acetrainersnowf',
	56: 'skier',
	57: 'skierf-gen4dp',
	58: 'roughneck-gen4',
	59: 'clown',
	60: 'worker-gen4',
	61: 'schoolkid-gen4dp',
	62: 'schoolkidf-gen4',
	63: 'roark',
	64: 'barry',
	65: 'byron',
	66: 'aaron',
	67: 'bertha',
	68: 'flint',
	69: 'lucian',
	70: 'cynthia-gen4',
	71: 'bellepa',
	72: 'rancher',
	73: 'mars',
	74: 'galacticgrunt',
	75: 'gardenia',
	76: 'crasherwake',
	77: 'maylene',
	78: 'fantina',
	79: 'candice',
	80: 'volkner',
	81: 'parasollady-gen4',
	82: 'waiter-gen4dp',
	83: 'interviewers',
	84: 'cameraman',
	85: 'reporter',
	86: 'idol',
	87: 'cyrus',
	88: 'jupiter',
	89: 'saturn',
	90: 'galacticgruntf',
	91: 'argenta',
	92: 'palmer',
	93: 'thorton',
	94: 'buck',
	95: 'darach-caitlin',
	96: 'marley',
	97: 'mira',
	98: 'cheryl',
	99: 'riley',
	100: 'dahlia',
	101: 'ethan',
	102: 'lyra',
	103: 'twins-gen4',
	104: 'lass-gen4',
	105: 'acetrainer-gen4',
	106: 'acetrainerf-gen4',
	107: 'juggler',
	108: 'sage',
	109: 'li',
	110: 'gentleman-gen4',
	111: 'teacher',
	112: 'beauty',
	113: 'birdkeeper',
	114: 'swimmer-gen4',
	115: 'swimmerf-gen4',
	116: 'kimonogirl',
	117: 'scientist-gen4',
	118: 'acetrainercouple',
	119: 'youngcouple',
	120: 'supernerd',
	121: 'medium',
	122: 'schoolkid-gen4',
	123: 'blackbelt-gen4',
	124: 'pokemaniac',
	125: 'firebreather',
	126: 'burglar',
	127: 'biker-gen4',
	128: 'skierf',
	129: 'boarder',
	130: 'rocketgrunt',
	131: 'rocketgruntf',
	132: 'archer',
	133: 'ariana',
	134: 'proton',
	135: 'petrel',
	136: 'eusine',
	137: 'lucas-gen4pt',
	138: 'dawn-gen4pt',
	139: 'madame-gen4',
	140: 'waiter-gen4',
	141: 'falkner',
	142: 'bugsy',
	143: 'whitney',
	144: 'morty',
	145: 'chuck',
	146: 'jasmine',
	147: 'pryce',
	148: 'clair',
	149: 'will',
	150: 'koga',
	151: 'bruno',
	152: 'karen',
	153: 'lance',
	154: 'brock',
	155: 'misty',
	156: 'ltsurge',
	157: 'erika',
	158: 'janine',
	159: 'sabrina',
	160: 'blaine',
	161: 'blue',
	162: 'red',
	163: 'red',
	164: 'silver',
	165: 'giovanni',
	166: 'unknownf',
	167: 'unknown',
	168: 'unknown',
	169: 'hilbert',
	170: 'hilda',
	171: 'youngster',
	172: 'lass',
	173: 'schoolkid',
	174: 'schoolkidf',
	175: 'smasher',
	176: 'linebacker',
	177: 'waiter',
	178: 'waitress',
	179: 'chili',
	180: 'cilan',
	181: 'cress',
	182: 'nurseryaide',
	183: 'preschoolerf',
	184: 'preschooler',
	185: 'twins',
	186: 'pokemonbreeder',
	187: 'pokemonbreederf',
	188: 'lenora',
	189: 'burgh',
	190: 'elesa',
	191: 'clay',
	192: 'skyla',
	193: 'pokemonranger',
	194: 'pokemonrangerf',
	195: 'worker',
	196: 'backpacker',
	197: 'backpackerf',
	198: 'fisherman',
	199: 'musician',
	200: 'dancer',
	201: 'harlequin',
	202: 'artist',
	203: 'baker',
	204: 'psychic',
	205: 'psychicf',
	206: 'cheren',
	207: 'bianca',
	208: 'plasmagrunt-gen5bw',
	209: 'n',
	210: 'richboy',
	211: 'lady',
	212: 'pilot',
	213: 'workerice',
	214: 'hoopster',
	215: 'scientistf',
	216: 'clerkf',
	217: 'acetrainerf',
	218: 'acetrainer',
	219: 'blackbelt',
	220: 'scientist',
	221: 'striker',
	222: 'brycen',
	223: 'iris',
	224: 'drayden',
	225: 'roughneck',
	226: 'janitor',
	227: 'pokefan',
	228: 'pokefanf',
	229: 'doctor',
	230: 'nurse',
	231: 'hooligans',
	232: 'battlegirl',
	233: 'parasollady',
	234: 'clerk',
	235: 'clerk-boss',
	236: 'backers',
	237: 'backersf',
	238: 'veteran',
	239: 'veteranf',
	240: 'biker',
	241: 'infielder',
	242: 'hiker',
	243: 'madame',
	244: 'gentleman',
	245: 'plasmagruntf-gen5bw',
	246: 'shauntal',
	247: 'marshal',
	248: 'grimsley',
	249: 'caitlin',
	250: 'ghetsis-gen5bw',
	251: 'depotagent',
	252: 'swimmer',
	253: 'swimmerf',
	254: 'policeman',
	255: 'maid',
	256: 'ingo',
	257: 'alder',
	258: 'cyclist',
	259: 'cyclistf',
	260: 'cynthia',
	261: 'emmet',
	262: 'hilbert-wonderlauncher',
	263: 'hilda-wonderlauncher',
	264: 'hugh',
	265: 'rosa',
	266: 'nate',
	267: 'colress',
	268: 'beauty-gen5bw2',
	269: 'ghetsis',
	270: 'plasmagrunt',
	271: 'plasmagruntf',
	272: 'iris-gen5bw2',
	273: 'brycenman',
	274: 'shadowtriad',
	275: 'rood',
	276: 'zinzolin',
	277: 'cheren-gen5bw2',
	278: 'marlon',
	279: 'roxie',
	280: 'roxanne',
	281: 'brawly',
	282: 'wattson',
	283: 'flannery',
	284: 'norman',
	285: 'winona',
	286: 'tate',
	287: 'liza',
	288: 'juan',
	289: 'guitarist',
	290: 'steven',
	291: 'wallace',
	292: 'bellelba',
	293: 'benga',
	294: 'ash',
	'#bw2elesa': 'elesa-gen5bw2',
	'#teamrocket': 'teamrocket',
	'#yellow': 'yellow',
	'#zinnia': 'zinnia',
	'#clemont': 'clemont',
	'#wally': 'wally',
	breeder: 'pokemonbreeder',
	breederf: 'pokemonbreederf',
	'hilbert-dueldisk': 'hilbert-wonderlauncher',
	'hilda-dueldisk': 'hilda-wonderlauncher',
	'nate-dueldisk': 'nate-wonderlauncher',
	'rosa-dueldisk': 'rosa-wonderlauncher',

	1001: '#1001',
	1002: '#1002',
	1003: '#1003',
	1005: '#1005',
	1010: '#1010',
};

export type StatName = 'hp' | 'atk' | 'def' | 'spa' | 'spd' | 'spe';
export type NatureName = 'Adamant' | 'Bashful' | 'Bold' | 'Brave' | 'Calm' | 'Careful' | 'Docile' | 'Gentle' |
	'Hardy' | 'Hasty' | 'Impish' | 'Jolly' | 'Lax' | 'Lonely' | 'Mild' | 'Modest' | 'Naive' | 'Naughty' |
	'Quiet' | 'Quirky' | 'Rash' | 'Relaxed' | 'Sassy' | 'Serious' | 'Timid';
export type StatNameExceptHP = 'atk' | 'def' | 'spa' | 'spd' | 'spe';
export type TypeName = 'Normal' | 'Fighting' | 'Flying' | 'Poison' | 'Ground' | 'Rock' | 'Bug' | 'Ghost' | 'Steel' |
	'Fire' | 'Water' | 'Grass' | 'Electric' | 'Psychic' | 'Ice' | 'Dragon' | 'Dark' | 'Fairy' | 'Stellar' | '???';
export type CategoryName = 'Physical' | 'Special' | 'Status';
export type StatusName = 'par' | 'psn' | 'frz' | 'slp' | 'brn';
export type BoostStatName = 'atk' | 'def' | 'spa' | 'spd' | 'spe' | 'evasion' | 'accuracy' | 'spc';
export type GenderName = 'M' | 'F' | 'N';

export interface Effect {
	readonly id: ID;
	readonly name: string;
	readonly gen: number;
	readonly effectType: 'Item' | 'Move' | 'Ability' | 'Species' | 'PureEffect';
	/**
	 * Do we have data on this item/move/ability/species?
	 * WARNING: Always false if the relevant data files aren't loaded.
	 */
	readonly exists: boolean;
}

export class PureEffect implements Effect {
	readonly effectType = 'PureEffect';
	readonly id: ID;
	readonly name: string;
	readonly gen: number;
	readonly exists: boolean;
	constructor(id: ID, name: string) {
		this.id = id;
		this.name = name;
		this.gen = 0;
		this.exists = false;
	}
}

export class Item implements Effect {
	// effect
	readonly effectType = 'Item';
	readonly id: ID;
	readonly name: string;
	readonly gen: number;
	readonly exists: boolean;

	readonly num: number;
	readonly spritenum: number;
	readonly desc: string;
	readonly shortDesc: string;

	readonly megaStone: { [megaEvolves: string]: string };
	readonly zMove: string | true | null;
	readonly zMoveType: TypeName | '';
	readonly zMoveFrom: string;
	readonly zMoveUser: readonly string[] | null;
	readonly onPlate: TypeName;
	readonly onMemory: TypeName;
	readonly onDrive: TypeName;
	readonly fling: any;
	readonly naturalGift: { basePower: number, type: TypeName };
	readonly isPokeball: boolean;
	readonly itemUser?: readonly string[];

	constructor(id: ID, name: string, data: any) {
		if (!data || typeof data !== 'object') data = {};
		if (data.name) name = data.name;
		this.name = Dex.sanitizeName(name);
		this.id = id;
		this.gen = data.gen || 0;
		this.exists = ('exists' in data ? !!data.exists : true);

		this.num = data.num || 0;
		this.spritenum = data.spritenum || 0;
		this.desc = data.desc || data.shortDesc || '';
		this.shortDesc = data.shortDesc || this.desc;

		this.megaStone = data.megaStone || null;
		this.zMove = data.zMove || null;
		this.zMoveType = data.zMoveType || '';
		this.zMoveFrom = data.zMoveFrom || '';
		this.zMoveUser = data.zMoveUser || null;
		this.onPlate = data.onPlate || '';
		this.onMemory = data.onMemory || '';
		this.onDrive = data.onDrive || '';
		this.fling = data.fling || null;
		this.naturalGift = data.naturalGift || null;
		this.isPokeball = !!data.isPokeball;
		this.itemUser = data.itemUser;

		if (!this.gen) {
			if (this.num >= 577) {
				this.gen = 6;
			} else if (this.num >= 537) {
				this.gen = 5;
			} else if (this.num >= 377) {
				this.gen = 4;
			} else {
				this.gen = 3;
			}
		}
	}
}

export interface MoveFlags {
	/** The move has an animation when used on an ally. */
	allyanim?: 1 | 0;
	/** Power is multiplied by 1.5 when used by a Pokemon with the Strong Jaw Ability. */
	bite?: 1 | 0;
	/** Has no effect on Pokemon with the Bulletproof Ability. */
	bullet?: 1 | 0;
	/** Ignores a target's substitute. */
	bypasssub?: 1 | 0;
	/** The user is unable to make a move between turns. */
	charge?: 1 | 0;
	/** Makes contact. */
	contact?: 1 | 0;
	/** When used by a Pokemon, other Pokemon with the Dancer Ability can attempt to execute the same move. */
	dance?: 1 | 0;
	/** Thaws the user if executed successfully while the user is frozen. */
	defrost?: 1 | 0;
	/** Can target a Pokemon positioned anywhere in a Triple Battle. */
	distance?: 1 | 0;
	/** Prevented from being executed or selected during Gravity's effect. */
	gravity?: 1 | 0;
	/** Prevented from being executed or selected during Heal Block's effect. */
	heal?: 1 | 0;
	/** Can be copied by Mirror Move. */
	mirror?: 1 | 0;
	/** Prevented from being executed or selected in a Sky Battle. */
	nonsky?: 1 | 0;
	/** Cannot be copied by Sketch */
	nosketch?: 1 | 0;
	/** Has no effect on Grass-type Pokemon, Pokemon with the Overcoat Ability, and Pokemon holding Safety Goggles. */
	powder?: 1 | 0;
	/** Blocked by Detect, Protect, Spiky Shield, and if not a Status move, King's Shield. */
	protect?: 1 | 0;
	/** Power is multiplied by 1.5 when used by a Pokemon with the Mega Launcher Ability. */
	pulse?: 1 | 0;
	/** Power is multiplied by 1.2 when used by a Pokemon with the Iron Fist Ability. */
	punch?: 1 | 0;
	/** If this move is successful, the user must recharge on the following turn and cannot make a move. */
	recharge?: 1 | 0;
	/** Bounced back to the original user by Magic Coat or the Magic Bounce Ability. */
	reflectable?: 1 | 0;
	/** Power is multiplied by 1.5 when used by a Pokemon with the Sharpness Ability. */
	slicing?: 1 | 0;
	/** Can be stolen from the original user and instead used by another Pokemon using Snatch. */
	snatch?: 1 | 0;
	/** Has no effect on Pokemon with the Soundproof Ability. */
	sound?: 1 | 0;
	/** Activates the effects of the Wind Power and Wind Rider Abilities. */
	wind?: 1 | 0;
}

export type MoveTarget = 'normal' | 'any' | 'adjacentAlly' | 'adjacentFoe' | 'adjacentAllyOrSelf' | // single-target
	'self' | 'randomNormal' | // single-target, automatic
	'allAdjacent' | 'allAdjacentFoes' | // spread
	'allySide' | 'foeSide' | 'all'; // side and field

export class Move implements Effect {
	// effect
	readonly effectType = 'Move';
	readonly id: ID;
	readonly name: string;
	readonly gen: number;
	readonly exists: boolean;

	readonly basePower: number;
	readonly accuracy: number | true;
	readonly pp: number;
	readonly type: TypeName;
	readonly category: CategoryName;
	readonly priority: number;
	readonly target: MoveTarget;
	readonly pressureTarget: MoveTarget;
	readonly flags: Readonly<MoveFlags>;
	readonly critRatio: number;
	readonly damage?: number | 'level' | false | null;

	readonly desc: string;
	readonly shortDesc: string;
	readonly isNonstandard: string | null;
	readonly isZ: ID;
	readonly zMove?: {
		basePower?: number,
		effect?: string,
		boost?: { [stat in StatName]?: number },
	};
	readonly isMax: boolean | string;
	readonly maxMove: { basePower: number };
	readonly ohko: true | 'Ice' | null;
	readonly recoil: number[] | null;
	readonly heal: number[] | null;
	readonly multihit: number[] | number | null;
	readonly hasCrashDamage: boolean;
	readonly basePowerCallback: boolean;
	readonly noPPBoosts: boolean;
	readonly status: string;
	readonly secondaries: readonly any[] | null;
	readonly num: number;

	constructor(id: ID, name: string, data: any) {
		if (!data || typeof data !== 'object') data = {};
		if (data.name) name = data.name;
		this.name = Dex.sanitizeName(name);
		this.id = id;
		this.gen = data.gen || 0;
		this.exists = ('exists' in data ? !!data.exists : true);

		this.basePower = data.basePower || 0;
		this.accuracy = data.accuracy || 0;
		this.pp = data.pp || 1;
		this.type = data.type || '???';
		this.category = data.category || 'Physical';
		this.priority = data.priority || 0;
		this.target = data.target || 'normal';
		this.pressureTarget = data.pressureTarget || this.target;
		this.flags = data.flags || {};
		this.critRatio = data.critRatio === 0 ? 0 : (data.critRatio || 1);
		this.damage = data.damage;

		// TODO: move to text.js
		this.desc = data.desc;
		this.shortDesc = data.shortDesc;
		this.isNonstandard = data.isNonstandard || null;
		this.isZ = data.isZ || '';
		this.zMove = data.zMove || {};
		this.ohko = data.ohko || null;
		this.recoil = data.recoil || null;
		this.heal = data.heal || null;
		this.multihit = data.multihit || null;
		this.hasCrashDamage = data.hasCrashDamage || false;
		this.basePowerCallback = !!data.basePowerCallback;
		this.noPPBoosts = data.noPPBoosts || false;
		this.status = data.status || '';
		this.secondaries = data.secondaries || (data.secondary ? [data.secondary] : null);

		this.isMax = data.isMax || false;
		this.maxMove = data.maxMove || { basePower: 0 };
		if (this.category !== 'Status' && !this.maxMove?.basePower) {
			if (this.isZ || this.isMax) {
				this.maxMove = { basePower: 1 };
			} else if (!this.basePower) {
				this.maxMove = { basePower: 100 };
			} else if (['Fighting', 'Poison'].includes(this.type)) {
				if (this.basePower >= 150) {
					this.maxMove = { basePower: 100 };
				} else if (this.basePower >= 110) {
					this.maxMove = { basePower: 95 };
				} else if (this.basePower >= 75) {
					this.maxMove = { basePower: 90 };
				} else if (this.basePower >= 65) {
					this.maxMove = { basePower: 85 };
				} else if (this.basePower >= 55) {
					this.maxMove = { basePower: 80 };
				} else if (this.basePower >= 45) {
					this.maxMove = { basePower: 75 };
				} else {
					this.maxMove = { basePower: 70 };
				}
			} else {
				if (this.basePower >= 150) {
					this.maxMove = { basePower: 150 };
				} else if (this.basePower >= 110) {
					this.maxMove = { basePower: 140 };
				} else if (this.basePower >= 75) {
					this.maxMove = { basePower: 130 };
				} else if (this.basePower >= 65) {
					this.maxMove = { basePower: 120 };
				} else if (this.basePower >= 55) {
					this.maxMove = { basePower: 110 };
				} else if (this.basePower >= 45) {
					this.maxMove = { basePower: 100 };
				} else {
					this.maxMove = { basePower: 90 };
				}
			}
		}

		if (this.category !== 'Status' && !this.isZ && !this.isMax) {
			let basePower = this.basePower;
			this.zMove = {};
			if (Array.isArray(this.multihit)) basePower *= 3;
			if (!basePower) {
				this.zMove.basePower = 100;
			} else if (basePower >= 140) {
				this.zMove.basePower = 200;
			} else if (basePower >= 130) {
				this.zMove.basePower = 195;
			} else if (basePower >= 120) {
				this.zMove.basePower = 190;
			} else if (basePower >= 110) {
				this.zMove.basePower = 185;
			} else if (basePower >= 100) {
				this.zMove.basePower = 180;
			} else if (basePower >= 90) {
				this.zMove.basePower = 175;
			} else if (basePower >= 80) {
				this.zMove.basePower = 160;
			} else if (basePower >= 70) {
				this.zMove.basePower = 140;
			} else if (basePower >= 60) {
				this.zMove.basePower = 120;
			} else {
				this.zMove.basePower = 100;
			}
			if (data.zMove) this.zMove.basePower = data.zMove.basePower;
		}

		this.num = data.num || 0;
		if (!this.gen) {
			if (this.num >= 743) {
				this.gen = 8;
			} else if (this.num >= 622) {
				this.gen = 7;
			} else if (this.num >= 560) {
				this.gen = 6;
			} else if (this.num >= 468) {
				this.gen = 5;
			} else if (this.num >= 355) {
				this.gen = 4;
			} else if (this.num >= 252) {
				this.gen = 3;
			} else if (this.num >= 166) {
				this.gen = 2;
			} else if (this.num >= 1) {
				this.gen = 1;
			}
		}
	}
}

export interface AbilityFlags {
	/** Can be suppressed by Mold Breaker and related effects */
	breakable?: 1;
	/** Ability can't be suppressed by e.g. Gastro Acid or Neutralizing Gas */
	cantsuppress?: 1;
	/** Role Play fails if target has this Ability */
	failroleplay?: 1;
	/** Skill Swap fails if either the user or target has this Ability */
	failskillswap?: 1;
	/** Entrainment fails if user has this Ability */
	noentrain?: 1;
	/** Receiver and Power of Alchemy will not activate if an ally faints with this Ability */
	noreceiver?: 1;
	/** Trace cannot copy this Ability */
	notrace?: 1;
	/** Disables the Ability if the user is Transformed */
	notransform?: 1;
}

export class Ability implements Effect {
	// effect
	readonly effectType = 'Ability';
	readonly id: ID;
	readonly name: string;
	readonly gen: number;
	readonly exists: boolean;

	readonly num: number;
	readonly shortDesc: string;
	readonly desc: string;

	readonly rating: number;
	readonly flags: AbilityFlags;
	readonly isNonstandard: boolean;

	constructor(id: ID, name: string, data: any) {
		if (!data || typeof data !== 'object') data = {};
		if (data.name) name = data.name;
		this.name = Dex.sanitizeName(name);
		this.id = id;
		this.gen = data.gen || 0;
		this.exists = ('exists' in data ? !!data.exists : true);
		this.num = data.num || 0;
		this.shortDesc = data.shortDesc || data.desc || '';
		this.desc = data.desc || data.shortDesc || '';
		this.rating = data.rating || 1;
		this.flags = data.flags || {};
		this.isNonstandard = !!data.isNonstandard;
		if (!this.gen) {
			if (this.num >= 234) {
				this.gen = 8;
			} else if (this.num >= 192) {
				this.gen = 7;
			} else if (this.num >= 165) {
				this.gen = 6;
			} else if (this.num >= 124) {
				this.gen = 5;
			} else if (this.num >= 77) {
				this.gen = 4;
			} else if (this.num >= 1) {
				this.gen = 3;
			}
		}
	}
}

export class Species implements Effect {
	// effect
	readonly effectType = 'Species';
	readonly id: ID;
	readonly name: string;
	readonly gen: number;
	readonly exists: boolean;

	// name
	readonly baseSpecies: string;
	readonly forme: string;
	readonly formeid: string;
	readonly spriteid: string;
	readonly baseForme: string;

	// basic data
	readonly num: number;
	readonly types: readonly TypeName[];
	readonly abilities: Readonly<{
		0: string, 1?: string, H?: string, S?: string,
	}>;
	readonly baseStats: Readonly<{
		hp: number, atk: number, def: number, spa: number, spd: number, spe: number,
	}>;
	readonly bst: number;
	readonly weightkg: number;

	// flavor data
	readonly heightm: number;
	readonly gender: GenderName;
	readonly color: string;
	readonly genderRatio: Readonly<{ M: number, F: number }> | null;
	readonly eggGroups: readonly string[];
	readonly tags: readonly string[];

	// format data
	readonly otherFormes: readonly string[] | null;
	readonly cosmeticFormes: readonly string[] | null;
	readonly evos: readonly string[] | null;
	readonly prevo: string;
	readonly evoType: 'trade' | 'useItem' | 'levelMove' | 'levelExtra' | 'levelFriendship' | 'levelHold' | 'other' | '';
	readonly evoLevel: number;
	readonly evoMove: string;
	readonly evoItem: string;
	readonly evoCondition: string;
	readonly nfe: boolean;
	readonly requiredItems: readonly string[];
	readonly tier: string;
	readonly isTotem: boolean;
	readonly isMega: boolean;
	readonly isPrimal: boolean;
	readonly canGigantamax: boolean;
	readonly cannotDynamax: boolean;
	readonly requiredTeraType: TypeName;
	readonly battleOnly: string | string[] | undefined;
	readonly isNonstandard: string | null;
	readonly unreleasedHidden: boolean | 'Past';
	readonly changesFrom: string | undefined;

	constructor(id: ID, name: string, data: any) {
		if (!data || typeof data !== 'object') data = {};
		if (data.name) name = data.name;
		this.name = Dex.sanitizeName(name);
		this.id = id;
		this.gen = data.gen || 0;
		this.exists = ('exists' in data ? !!data.exists : true);
		this.baseSpecies = data.baseSpecies || name;
		this.forme = data.forme || '';
		const baseId = toID(this.baseSpecies);
		this.formeid = (baseId === this.id ? '' : '-' + toID(this.forme));
		this.spriteid = baseId + this.formeid;
		if (this.spriteid.endsWith('totem')) this.spriteid = this.spriteid.slice(0, -5);
		if (this.spriteid === 'greninja-bond') this.spriteid = 'greninja';
		if (this.spriteid === 'rockruff-dusk') this.spriteid = 'rockruff';
		if (this.spriteid.endsWith('-')) this.spriteid = this.spriteid.slice(0, -1);
		this.baseForme = data.baseForme || '';

		this.num = data.num || 0;
		this.types = data.types || ['???'];
		this.abilities = data.abilities || { 0: "No Ability" };
		this.baseStats = data.baseStats || { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
		this.bst = this.baseStats.hp + this.baseStats.atk + this.baseStats.def +
			this.baseStats.spa + this.baseStats.spd + this.baseStats.spe;
		this.weightkg = data.weightkg || 0;

		this.heightm = data.heightm || 0;
		this.gender = data.gender || '';
		this.color = data.color || '';
		this.genderRatio = data.genderRatio || null;
		this.eggGroups = data.eggGroups || [];
		this.tags = data.tags || [];

		this.otherFormes = data.otherFormes || null;
		this.cosmeticFormes = data.cosmeticFormes || null;
		this.evos = data.evos || null;
		this.prevo = data.prevo || '';
		this.evoType = data.evoType || '';
		this.evoLevel = data.evoLevel || 0;
		this.evoMove = data.evoMove || '';
		this.evoItem = data.evoItem || '';
		this.evoCondition = data.evoCondition || '';
		this.nfe = data.nfe || false;
		this.requiredItems = data.requiredItems || (data.requiredItem ? [data.requiredItem] : []);
		this.tier = data.tier || '';

		this.isTotem = false;
		this.isMega = !!(this.forme && ['-mega', '-megax', '-megay'].includes(this.formeid));
		this.isPrimal = !!(this.forme && this.formeid === '-primal');
		this.canGigantamax = !!data.canGigantamax;
		this.cannotDynamax = !!data.cannotDynamax;
		this.requiredTeraType = data.requiredTeraType || '';
		this.battleOnly = data.battleOnly || (this.isMega ? this.baseSpecies : undefined);
		this.isNonstandard = data.isNonstandard || null;
		this.unreleasedHidden = data.unreleasedHidden || false;
		this.changesFrom = data.changesFrom ||
			(this.battleOnly !== this.baseSpecies ? this.battleOnly : this.baseSpecies);
		if (!this.gen) {
			if (this.num >= 906 || this.formeid.startsWith('-paldea')) {
				this.gen = 9;
			} else if (this.num >= 810 || this.formeid.startsWith('-galar') || this.formeid.startsWith('-hisui')) {
				this.gen = 8;
			} else if (this.num >= 722 || this.formeid === '-alola' || this.formeid === '-starter') {
				this.gen = 7;
			} else if (this.isMega || this.isPrimal) {
				this.gen = 6;
				this.battleOnly = this.baseSpecies;
			} else if (this.formeid === '-totem' || this.formeid === '-alolatotem') {
				this.gen = 7;
				this.isTotem = true;
			} else if (this.num >= 650) {
				this.gen = 6;
			} else if (this.num >= 494) {
				this.gen = 5;
			} else if (this.num >= 387) {
				this.gen = 4;
			} else if (this.num >= 252) {
				this.gen = 3;
			} else if (this.num >= 152) {
				this.gen = 2;
			} else if (this.num >= 1) {
				this.gen = 1;
			}
		}
	}
}

export interface Type extends Effect {
	damageTaken?: Record<Dex.TypeName | 'powder' | 'prankster' | 'trapped', Dex.WeaknessType>;
	HPivs?: Partial<Dex.StatsTable>;
	HPdvs?: Partial<Dex.StatsTable>;
}

declare const require: any;
declare const global: any;
if (typeof require === 'function') {
	// in Node
	global.BattleBaseSpeciesChart = BattleBaseSpeciesChart;
	global.BattleNatures = BattleNatures;
	global.PureEffect = PureEffect;
	global.Species = Species;
	global.Ability = Ability;
	global.Item = Item;
	global.Move = Move;
}
