From e5dfba6797cda6ec60f8908f206d81d62cc49ca5 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Sun, 16 Dec 2018 17:43:32 -0600 Subject: [PATCH] Make classes for dex data Template, Item, Move, and Ability are now classes. This guarantees that `getTemplate`, `getItem` etc return objects that implement their interfaces (they didn't before). As of their current implementation, they require trusted resource files. The plan is, whenever we support custom resource files, to pass all their strings through `sanitizeName`. This won't prevent servers from causing the client to behave unexpectedly, but it should prevent any actual vulnerabilities. In addition, pokemon forme icon index tables have been moved to `battle-dex-data.ts`. --- src/battle-dex-data.ts | 757 +++++++++++++++++++++++++++++++++++++++-- src/battle-dex.ts | 714 +++++++------------------------------- src/battle.ts | 12 +- 3 files changed, 868 insertions(+), 615 deletions(-) diff --git a/src/battle-dex-data.ts b/src/battle-dex-data.ts index d94899019..132b5f096 100644 --- a/src/battle-dex-data.ts +++ b/src/battle-dex-data.ts @@ -152,7 +152,7 @@ const BattleStats = { spc: 'Special', }; -const baseSpeciesChart = [ +const BattleBaseSpeciesChart = [ 'pikachu', 'pichu', 'unown', @@ -209,52 +209,761 @@ const baseSpeciesChart = [ // others are hardcoded by ending with 'mega' ]; +const BattlePokemonIconIndexes: {[id: string]: number} = { + egg: 816 + 1, + pikachubelle: 816 + 2, + pikachulibre: 816 + 3, + pikachuphd: 816 + 4, + pikachupopstar: 816 + 5, + pikachurockstar: 816 + 6, + pikachucosplay: 816 + 7, + // unown gap + castformrainy: 816 + 35, + castformsnowy: 816 + 36, + castformsunny: 816 + 37, + deoxysattack: 816 + 38, + deoxysdefense: 816 + 39, + deoxysspeed: 816 + 40, + burmysandy: 816 + 41, + burmytrash: 816 + 42, + wormadamsandy: 816 + 43, + wormadamtrash: 816 + 44, + cherrimsunshine: 816 + 45, + shelloseast: 816 + 46, + gastrodoneast: 816 + 47, + rotomfan: 816 + 48, + rotomfrost: 816 + 49, + rotomheat: 816 + 50, + rotommow: 816 + 51, + rotomwash: 816 + 52, + giratinaorigin: 816 + 53, + shayminsky: 816 + 54, + unfezantf: 816 + 55, + basculinbluestriped: 816 + 56, + darmanitanzen: 816 + 57, + deerlingautumn: 816 + 58, + deerlingsummer: 816 + 59, + deerlingwinter: 816 + 60, + sawsbuckautumn: 816 + 61, + sawsbucksummer: 816 + 62, + sawsbuckwinter: 816 + 63, + frillishf: 816 + 64, + jellicentf: 816 + 65, + tornadustherian: 816 + 66, + thundurustherian: 816 + 67, + landorustherian: 816 + 68, + kyuremblack: 816 + 69, + kyuremwhite: 816 + 70, + keldeoresolute: 816 + 71, + meloettapirouette: 816 + 72, + vivillonarchipelago: 816 + 73, + vivilloncontinental: 816 + 74, + vivillonelegant: 816 + 75, + vivillonfancy: 816 + 76, + vivillongarden: 816 + 77, + vivillonhighplains: 816 + 78, + vivillonicysnow: 816 + 79, + vivillonjungle: 816 + 80, + vivillonmarine: 816 + 81, + vivillonmodern: 816 + 82, + vivillonmonsoon: 816 + 83, + vivillonocean: 816 + 84, + vivillonpokeball: 816 + 85, + vivillonpolar: 816 + 86, + vivillonriver: 816 + 87, + vivillonsandstorm: 816 + 88, + vivillonsavanna: 816 + 89, + vivillonsun: 816 + 90, + vivillontundra: 816 + 91, + pyroarf: 816 + 92, + flabebeblue: 816 + 93, + flabebeorange: 816 + 94, + flabebewhite: 816 + 95, + flabebeyellow: 816 + 96, + floetteblue: 816 + 97, + floetteeternal: 816 + 98, + floetteorange: 816 + 99, + floettewhite: 816 + 100, + floetteyellow: 816 + 101, + florgesblue: 816 + 102, + florgesorange: 816 + 103, + florgeswhite: 816 + 104, + florgesyellow: 816 + 105, + furfroudandy: 816 + 106, + furfroudebutante: 816 + 107, + furfroudiamond: 816 + 108, + furfrouheart: 816 + 109, + furfroukabuki: 816 + 110, + furfroulareine: 816 + 111, + furfroumatron: 816 + 112, + furfroupharaoh: 816 + 113, + furfroustar: 816 + 114, + meowsticf: 816 + 115, + aegislashblade: 816 + 116, + hoopaunbound: 816 + 118, + rattataalola: 816 + 119, + raticatealola: 816 + 120, + raichualola: 816 + 121, + sandshrewalola: 816 + 122, + sandslashalola: 816 + 123, + vulpixalola: 816 + 124, + ninetalesalola: 816 + 125, + diglettalola: 816 + 126, + dugtrioalola: 816 + 127, + meowthalola: 816 + 128, + persianalola: 816 + 129, + geodudealola: 816 + 130, + graveleralola: 816 + 131, + golemalola: 816 + 132, + grimeralola: 816 + 133, + mukalola: 816 + 134, + exeggutoralola: 816 + 135, + marowakalola: 816 + 136, + greninjaash: 816 + 137, + zygarde10: 816 + 138, + zygardecomplete: 816 + 139, + oricoriopompom: 816 + 140, + oricoriopau: 816 + 141, + oricoriosensu: 816 + 142, + lycanrocmidnight: 816 + 143, + wishiwashischool: 816 + 144, + miniormeteor: 816 + 145, + miniororange: 816 + 146, + minioryellow: 816 + 147, + miniorgreen: 816 + 148, + miniorblue: 816 + 149, + miniorviolet: 816 + 150, + miniorindigo: 816 + 151, + magearnaoriginal: 816 + 152, + pikachuoriginal: 816 + 153, + pikachuhoenn: 816 + 154, + pikachusinnoh: 816 + 155, + pikachuunova: 816 + 156, + pikachukalos: 816 + 157, + pikachualola: 816 + 158, + pikachupartner: 816 + 159, + lycanrocdusk: 816 + 160, + necrozmaduskmane: 816 + 161, + necrozmadawnwings: 816 + 162, + necrozmaultra: 816 + 163, + pikachustarter: 816 + 164, + eeveestarter: 816 + 165, + + gumshoostotem: 735, + raticatealolatotem: 816 + 120, + marowakalolatotem: 816 + 136, + araquanidtotem: 752, + lurantistotem: 754, + salazzletotem: 758, + vikavolttotem: 738, + togedemarutotem: 777, + mimikyutotem: 778, + mimikyubustedtotem: 778, + ribombeetotem: 743, + kommoototem: 784, + + venusaurmega: 984 + 0, + charizardmegax: 984 + 1, + charizardmegay: 984 + 2, + blastoisemega: 984 + 3, + beedrillmega: 984 + 4, + pidgeotmega: 984 + 5, + alakazammega: 984 + 6, + slowbromega: 984 + 7, + gengarmega: 984 + 8, + kangaskhanmega: 984 + 9, + pinsirmega: 984 + 10, + gyaradosmega: 984 + 11, + aerodactylmega: 984 + 12, + mewtwomegax: 984 + 13, + mewtwomegay: 984 + 14, + ampharosmega: 984 + 15, + steelixmega: 984 + 16, + scizormega: 984 + 17, + heracrossmega: 984 + 18, + houndoommega: 984 + 19, + tyranitarmega: 984 + 20, + sceptilemega: 984 + 21, + blazikenmega: 984 + 22, + swampertmega: 984 + 23, + gardevoirmega: 984 + 24, + sableyemega: 984 + 25, + mawilemega: 984 + 26, + aggronmega: 984 + 27, + medichammega: 984 + 28, + manectricmega: 984 + 29, + sharpedomega: 984 + 30, + cameruptmega: 984 + 31, + altariamega: 984 + 32, + banettemega: 984 + 33, + absolmega: 984 + 34, + glaliemega: 984 + 35, + salamencemega: 984 + 36, + metagrossmega: 984 + 37, + latiasmega: 984 + 38, + latiosmega: 984 + 39, + kyogreprimal: 984 + 40, + groudonprimal: 984 + 41, + rayquazamega: 984 + 42, + lopunnymega: 984 + 43, + garchompmega: 984 + 44, + lucariomega: 984 + 45, + abomasnowmega: 984 + 46, + gallademega: 984 + 47, + audinomega: 984 + 48, + dianciemega: 984 + 49, + + syclant: 1152 + 0, + revenankh: 1152 + 1, + pyroak: 1152 + 2, + fidgit: 1152 + 3, + stratagem: 1152 + 4, + arghonaut: 1152 + 5, + kitsunoh: 1152 + 6, + cyclohm: 1152 + 7, + colossoil: 1152 + 8, + krilowatt: 1152 + 9, + voodoom: 1152 + 10, + tomohawk: 1152 + 11, + necturna: 1152 + 12, + mollux: 1152 + 13, + aurumoth: 1152 + 14, + malaconda: 1152 + 15, + cawmodore: 1152 + 16, + volkraken: 1152 + 17, + plasmanta: 1152 + 18, + naviathan: 1152 + 19, + crucibelle: 1152 + 20, + crucibellemega: 1152 + 21, + kerfluffle: 1152 + 22, + pajantom: 1152 + 23, + jumbao: 1152 + 24, + caribolt: 1152 + 25, + smokomodo: 1152 + 26, + snaelstrom: 1152 + 27, + + syclar: 1188 + 0, + embirch: 1188 + 1, + flarelm: 1188 + 2, + breezi: 1188 + 3, + scratchet: 1188 + 4, + necturine: 1188 + 5, + cupra: 1188 + 6, + argalis: 1188 + 7, + brattler: 1188 + 8, + cawdet: 1188 + 9, + volkritter: 1188 + 10, + snugglow: 1188 + 11, + floatoy: 1188 + 12, + caimanoe: 1188 + 13, + pluffle: 1188 + 14, + rebble: 1188 + 15, + tactite: 1188 + 16, + privatyke: 1188 + 17, + nohface: 1188 + 18, + monohm: 1188 + 19, + duohm: 1188 + 20, + // protowatt: 1188 + 21, + voodoll: 1188 + 22, + mumbao: 1188 + 23, +}; + +const BattlePokemonIconIndexesLeft: {[id: string]: number} = { + pikachubelle: 1044 + 0, + pikachupopstar: 1044 + 1, + clefairy: 1044 + 2, + clefable: 1044 + 3, + jigglypuff: 1044 + 4, + wigglytuff: 1044 + 5, + dugtrioalola: 1044 + 6, + poliwhirl: 1044 + 7, + poliwrath: 1044 + 8, + mukalola: 1044 + 9, + kingler: 1044 + 10, + croconaw: 1044 + 11, + cleffa: 1044 + 12, + igglybuff: 1044 + 13, + politoed: 1044 + 14, + // unown gap + sneasel: 1044 + 35, + teddiursa: 1044 + 36, + roselia: 1044 + 37, + zangoose: 1044 + 38, + seviper: 1044 + 39, + castformrainy: 1044 + 40, + absolmega: 1044 + 41, + absol: 1044 + 42, + regirock: 1044 + 43, + torterra: 1044 + 44, + budew: 1044 + 45, + roserade: 1044 + 46, + magmortar: 1044 + 47, + togekiss: 1044 + 48, + rotomwash: 1044 + 49, + shayminsky: 1044 + 50, + emboar: 1044 + 51, + pansear: 1044 + 52, + simisear: 1044 + 53, + drilbur: 1044 + 54, + excadrill: 1044 + 55, + sawk: 1044 + 56, + lilligant: 1044 + 57, + garbodor: 1044 + 58, + solosis: 1044 + 59, + vanilluxe: 1044 + 60, + amoonguss: 1044 + 61, + klink: 1044 + 62, + klang: 1044 + 63, + klinklang: 1044 + 64, + litwick: 1044 + 65, + golett: 1044 + 66, + golurk: 1044 + 67, + kyuremblack: 1044 + 68, + kyuremwhite: 1044 + 69, + kyurem: 1044 + 70, + keldeoresolute: 1044 + 71, + meloetta: 1044 + 72, + greninja: 1044 + 73, + greninjaash: 1044 + 74, + furfroudebutante: 1044 + 75, + barbaracle: 1044 + 76, + clauncher: 1044 + 77, + clawitzer: 1044 + 78, + sylveon: 1044 + 79, + klefki: 1044 + 80, + zygarde: 1044 + 81, + zygarde10: 1044 + 82, + zygardecomplete: 1044 + 83, + dartrix: 1044 + 84, + steenee: 1044 + 85, + tsareena: 1044 + 86, + comfey: 1044 + 87, + miniormeteor: 1044 + 88, + minior: 1044 + 89, + miniororange: 1044 + 90, + minioryellow: 1044 + 91, + miniorgreen: 1044 + 92, + miniorblue: 1044 + 93, + miniorviolet: 1044 + 94, + miniorindigo: 1044 + 95, + dhelmise: 1044 + 96, + necrozma: 1044 + 97, + marshadow: 1044 + 98, + pikachuoriginal: 1044 + 99, + pikachupartner: 1044 + 100, + necrozmaduskmane: 1044 + 101, + necrozmadawnwings: 1044 + 102, + necrozmaultra: 1044 + 103, + stakataka: 1044 + 104, + blacephalon: 1044 + 105, +}; + type StatName = 'hp' | 'atk' | 'def' | 'spa' | 'spd' | 'spe'; +type TypeName = 'Normal' | 'Fighting' | 'Flying' | 'Poison' | 'Ground' | 'Rock' | 'Bug' | 'Ghost' | 'Steel' | + 'Fire' | 'Water' | 'Grass' | 'Electric' | 'Psychic' | 'Ice' | 'Dragon' | 'Dark' | 'Fairy' | '???'; type StatusName = 'par' | 'psn' | 'frz' | 'slp' | 'brn'; type BoostStatName = 'atk' | 'def' | 'spa' | 'spd' | 'spe' | 'evasion' | 'accuracy' | 'spc'; -type GenderName = 'M' | 'F' | ''; +type GenderName = 'M' | 'F' | 'N'; interface Effect { readonly id: ID; readonly name: string; readonly gen: number; - readonly effectType: 'Item' | 'Move' | 'Ability' | 'Template'; + readonly effectType: 'Item' | 'Move' | 'Ability' | 'Template' | 'PureEffect'; + /** + * Do we have data on this item/move/ability/template? + * WARNING: Always false if the relevant data files aren't loaded. + */ readonly exists: boolean; } -interface Item extends Effect { - readonly effectType: 'Item'; - readonly zMoveFrom?: string; + +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; + } } -interface Move extends Effect { - readonly effectType: 'Move'; - readonly type: string; - readonly category: string; - readonly isZ?: string; + +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: string; + readonly megaEvolves: string; + readonly zMove: string | true | null; + readonly zMoveType: TypeName | ''; + readonly zMoveFrom: string; + readonly zMoveUser: string[] | null; + + 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 || ''; + this.megaEvolves = data.megaEvolves || ''; + this.zMove = data.zMove || null; + this.zMoveType = data.zMoveType || ''; + this.zMoveFrom = data.zMoveFrom || ''; + this.zMoveUser = data.zMoveUser || null; + + 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; + } + } + } } -interface Ability extends Effect { - readonly effectType: 'Ability'; + +interface MoveFlags { + /** Ignores a target's substitute. */ + authentic?: 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; + /** 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; + /** Unknown effect. */ + mystery?: 1 | 0; + /** Prevented from being executed or selected in a Sky Battle. */ + nonsky?: 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; + /** 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; } -interface Template extends Effect { - readonly effectType: 'Template'; + +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: 'Physical' | 'Special' | 'Status'; + readonly priority: number; + readonly target: + 'normal' | 'any' | 'adjacentAlly' | 'adjacentFoe' | 'adjacentAllyOrSelf' | // single-target + 'self' | 'randomNormal' | // single-target, automatic + 'allAdjacent' | 'allAdjacentFoes' | // spread + 'allySide' | 'foeSide' | 'all'; // side and field + readonly flags: Readonly; + readonly critRatio: number; + + readonly desc: string; + readonly shortDesc: string; + readonly isViable: boolean; + readonly isNonstandard: boolean; + readonly isZ: ID; + readonly zMovePower: number; + readonly zMoveEffect: string; + readonly zMoveBoost: {[stat in StatName]?: number} | 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.flags = data.flags || {}; + this.critRatio = data.critRatio === 0 ? 0 : (data.critRatio || 1); + + // TODO: move to text.js + this.desc = data.desc; + this.shortDesc = data.shortDesc; + this.isViable = !!data.isViable; + this.isNonstandard = !!data.isNonstandard; + this.isZ = data.isZ || ''; + this.zMovePower = data.zMovePower || 0; + this.zMoveEffect = data.zMoveEffect || ''; + this.zMoveBoost = data.zMoveBoost || null; + + this.num = data.num || 0; + if (!this.gen) { + 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; + } + } + } +} + +class Ability implements Effect { + // effect + readonly effectType = 'Ability'; + readonly id: ID; + readonly name: string; + readonly gen: number; + readonly exists: boolean; + + readonly num: number; + readonly desc: 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.desc = data.desc || data.shortDesc || ''; + if (!this.gen) { + 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; + } + } + } +} + +class Template implements Effect { + // effect + readonly effectType = 'Template'; + readonly id: ID; + readonly name: string; + readonly gen: number; + readonly exists: boolean; + + // name readonly species: string; readonly speciesid: ID; readonly baseSpecies: string; readonly forme: string; - readonly formeLetter: string; readonly formeid: string; readonly spriteid: string; - readonly types: string[]; - readonly abilities: {0: string, 1?: string, H?: string, S?: string}; - readonly baseStats: {hp: number, atk: number, def: number, spa: number, spd: number, spe: number}; - readonly unreleasedHidden: boolean; - readonly tier: string; + + // basic data + readonly num: number; + readonly types: ReadonlyArray; + 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 weightkg: number; + + // flavor data + readonly heightm: number; + readonly gender: GenderName; + readonly color: string; + readonly genderRatio: Readonly<{M: number, F: number}> | null; + readonly eggGroups: ReadonlyArray; + + // format data + readonly otherFormes: ReadonlyArray | null; + // TODO: rename to cosmeticForms + readonly otherForms: ReadonlyArray | null; + readonly evos: ReadonlyArray | null; + readonly prevo: ID; + readonly requiredItem: string; + readonly tier: string; + readonly isTotem: boolean; + readonly isMega: boolean; + readonly isPrimal: boolean; + readonly battleOnly: boolean; readonly isNonstandard: boolean; - readonly isTotem?: boolean; - readonly isMega?: boolean; + readonly unreleasedHidden: boolean; + + constructor(id: ID, name: string, data: any) { + if (!data || typeof data !== 'object') data = {}; + if (data.name || data.species) name = data.name || data.species; + this.name = Dex.sanitizeName(name); + this.id = id; + this.gen = data.gen || 0; + this.exists = ('exists' in data ? !!data.exists : true); + this.species = this.name; + this.speciesid = this.id; + if (!data.abilities && + !['hooh', 'hakamoo', 'jangmoo', 'kommoo', 'porygonz'].includes(this.id)) { + const dashIndex = name.indexOf('-'); + if (this.id === 'kommoototem') { + data.baseSpecies = 'Kommo-o'; + data.forme = 'Totem'; + } else if (dashIndex > 0) { + data.baseSpecies = name.slice(0, dashIndex); + data.forme = name.slice(dashIndex + 1); + } + } + if (!data.abilities) { + // deprecated BattleBaseSpeciesChart + for (const baseid of BattleBaseSpeciesChart) { + if (this.id.length > baseid.length && this.id.slice(0, baseid.length) === baseid) { + data.baseSpecies = baseid; + data.forme = this.id.slice(baseid.length); + } + } + if (this.id !== 'yanmega' && this.id.slice(-4) === 'mega') { + data.baseSpecies = this.id.slice(0, -4); + data.forme = this.id.slice(-4); + } else if (this.id.slice(-6) === 'primal') { + data.baseSpecies = this.id.slice(0, -6); + data.forme = this.id.slice(-6); + } else if (this.id.slice(-5) === 'alola') { + data.baseSpecies = this.id.slice(0, -5); + data.forme = this.id.slice(-5); + } + } + 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.slice(-5) === 'totem') this.spriteid = this.spriteid.slice(0, -5); + if (this.spriteid.slice(-1) === '-') this.spriteid = this.spriteid.slice(0, -1); + + this.num = data.num || 0; + this.types = data.types || ['???']; + let abilities: any = {0: "No Ability"}; + this.abilities = data.abilities || {0: "No Ability"}; + this.baseStats = data.baseStats || {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}; + 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.otherFormes = data.otherFormes || null; + this.otherForms = data.otherForms || null; + this.evos = data.evos || null; + this.prevo = data.prevo || ''; + this.requiredItem = data.requiredItem || ''; + this.tier = data.tier || ''; + + this.isTotem = false; + this.isMega = false; + this.isPrimal = false; + this.battleOnly = false; + this.isNonstandard = !!data.isNonstandard; + this.unreleasedHidden = !!data.unreleasedHidden; + if (!this.gen) { + if (this.forme && ['-mega', '-megax', '-megay'].includes(this.formeid)) { + this.gen = 6; + this.isMega = true; + this.battleOnly = true; + } else if (this.formeid === '-primal') { + this.gen = 6; + this.isPrimal = true; + this.battleOnly = true; + } else if (this.formeid === '-totem' || this.formeid === '-alolatotem') { + this.gen = 7; + this.isTotem = true; + } else if (this.formeid === '-alola') { + this.gen = 7; + } else if (this.num >= 722) { + this.gen = 7; + } 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; + } + } + } } if (typeof require === 'function') { // in Node - (global as any).baseSpeciesChart = baseSpeciesChart; + (global as any).BattleBaseSpeciesChart = BattleBaseSpeciesChart; + (global as any).PureEffect = PureEffect; + (global as any).Template = Template; + (global as any).Ability = Ability; + (global as any).Item = Item; + (global as any).Move = Move; } diff --git a/src/battle-dex.ts b/src/battle-dex.ts index dbb85a3bb..e2644c29a 100644 --- a/src/battle-dex.ts +++ b/src/battle-dex.ts @@ -221,8 +221,20 @@ const Dex = { return Dex.resolveAvatar(sprites[Math.floor(Math.random() * sprites.length)]); }, - sanitizeName(name: string) { - return ('' + name).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); + /** + * This is used to sanitize strings from data files like `moves.js` and + * `teambuilder-tables.js`. + * + * This makes sure untrusted strings can't wreak havoc if someone forgets to + * escape it before putting it in HTML. + * + * None of these characters belong in these files, anyway. (They can be used + * in move descriptions, but those are served from `text.js`, which are + * definitely always treated as unsanitized.) + */ + sanitizeName(name: any) { + if (!name) return ''; + return ('' + name).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').slice(0, 50); }, prefs(prop: string, value?: any, save?: boolean) { @@ -239,99 +251,57 @@ const Dex = { return shortName; }, - getEffect(effect: any): Effect { - if (!effect || typeof effect === 'string') { - let name = (effect || '').trim(); - if (name.substr(0, 5) === 'item:') { - return Dex.getItem(name.substr(5)); - } else if (name.substr(0, 8) === 'ability:') { - return Dex.getAbility(name.substr(8)); - } else if (name.substr(0, 5) === 'move:') { - return Dex.getMove(name.substr(5)); - } - let id = toId(name); - effect = {}; - if (id && window.BattleStatuses && BattleStatuses[id]) { - effect = BattleStatuses[id]; - effect.exists = true; - } else if (id && window.BattleMovedex && BattleMovedex[id] && BattleMovedex[id].effect) { - effect = BattleMovedex[id].effect; - effect.exists = true; - } else if (id && window.BattleAbilities && BattleAbilities[id] && BattleAbilities[id].effect) { - effect = BattleAbilities[id].effect; - effect.exists = true; - } else if (id && window.BattleItems && BattleItems[id] && BattleItems[id].effect) { - effect = BattleItems[id].effect; - effect.exists = true; - } else if (id === 'recoil') { - effect = { - effectType: 'Recoil', - }; - effect.exists = true; - } else if (id === 'drain') { - effect = { - effectType: 'Drain', - }; - effect.exists = true; - } - if (!effect.id) effect.id = id; - if (!effect.name) effect.name = Dex.sanitizeName(name); - if (!effect.category) effect.category = 'Effect'; - if (!effect.effectType) effect.effectType = 'Effect'; + getEffect(name: string | null | undefined): PureEffect | Item | Ability | Move { + name = (name || '').trim(); + if (name.substr(0, 5) === 'item:') { + return Dex.getItem(name.substr(5)); + } else if (name.substr(0, 8) === 'ability:') { + return Dex.getAbility(name.substr(8)); + } else if (name.substr(0, 5) === 'move:') { + return Dex.getMove(name.substr(5)); } - return effect; + let id = toId(name); + return new PureEffect(id, name); }, - getMove(move: any): Move { - if (!move || typeof move === 'string') { - let name = (move || '').trim(); - let id = toId(name); - move = (window.BattleMovedex && window.BattleMovedex[id]) || {}; - if (move.name) move.exists = true; - - if (!move.exists && id.substr(0, 11) === 'hiddenpower' && id.length > 11) { - let matches = /([a-z]*)([0-9]*)/.exec(id)!; - move = (window.BattleMovedex && window.BattleMovedex[matches[1]]) || {}; - move = {...move}; - move.basePower = matches[2]; - } - if (!move.exists && id.substr(0, 6) === 'return' && id.length > 6) { - move = (window.BattleMovedex && window.BattleMovedex['return']) || {}; - move = {...move}; - move.basePower = id.slice(6); - } - if (!move.exists && id.substr(0, 11) === 'frustration' && id.length > 11) { - move = (window.BattleMovedex && window.BattleMovedex['frustration']) || {}; - move = {...move}; - move.basePower = id.slice(11); - } - - if (!move.id) move.id = id; - if (!move.name) move.name = Dex.sanitizeName(name); - - if (!move.critRatio) move.critRatio = 1; - if (!move.baseType) move.baseType = move.type; - if (!move.effectType) move.effectType = 'Move'; - if (!move.secondaries && move.secondary) move.secondaries = [move.secondary]; - if (!move.flags) move.flags = {}; - if (!move.gen) { - if (move.num >= 560) { - move.gen = 6; - } else if (move.num >= 468) { - move.gen = 5; - } else if (move.num >= 355) { - move.gen = 4; - } else if (move.num >= 252) { - move.gen = 3; - } else if (move.num >= 166) { - move.gen = 2; - } else if (move.num >= 1) { - move.gen = 1; - } else { - move.gen = 0; - } - } + getMove(nameOrMove: string | Move | null | undefined): Move { + if (nameOrMove && typeof nameOrMove !== 'string') { + // TODO: don't accept Moves here + return nameOrMove; } + let name = nameOrMove || ''; + let id = toId(nameOrMove); + if (window.BattleAliases && id in BattleAliases) { + name = BattleAliases[id]; + id = toId(name); + } + if (!window.BattleMovedex) window.BattleMovedex = {}; + let data = window.BattleMovedex[id]; + if (data && typeof data.exists === 'boolean') return data; + + if (!data && id.substr(0, 11) === 'hiddenpower' && id.length > 11) { + let [, hpWithType, hpPower] = /([a-z]*)([0-9]*)/.exec(id)!; + data = { + ...(window.BattleMovedex[hpWithType] || {}), + basePower: Number(hpPower) || 60, + }; + } + if (!data && id.substr(0, 6) === 'return' && id.length > 6) { + data = { + ...(window.BattleMovedex['return'] || {}), + basePower: Number(id.slice(6)), + }; + } + if (!data && id.substr(0, 11) === 'frustration' && id.length > 11) { + data = { + ...(window.BattleMovedex['frustration'] || {}), + basePower: Number(id.slice(11)), + }; + } + + if (!data) data = {exists: false}; + let move = new Move(id, name, data); + window.BattleMovedex[id] = move; return move; }, @@ -344,156 +314,77 @@ const Dex = { return move.category; }, - getItem(item: any): Item { - if (!item || typeof item === 'string') { - let name = (item || '').trim(); - let id = toId(name); - item = (window.BattleItems && window.BattleItems[id]) || {}; - if (item.name) item.exists = true; - if (!item.id) item.id = id; - if (!item.name) item.name = Dex.sanitizeName(name); - if (!item.category) item.category = 'Effect'; - if (!item.effectType) item.effectType = 'Item'; - if (!item.gen) { - if (item.num >= 577) { - item.gen = 6; - } else if (item.num >= 537) { - item.gen = 5; - } else if (item.num >= 377) { - item.gen = 4; - } else { - item.gen = 3; - } - } + getItem(nameOrItem: string | Item | null | undefined): Item { + if (nameOrItem && typeof nameOrItem !== 'string') { + // TODO: don't accept Items here + return nameOrItem; } + let name = nameOrItem || ''; + let id = toId(nameOrItem); + if (window.BattleAliases && id in BattleAliases) { + name = BattleAliases[id]; + id = toId(name); + } + if (!window.BattleItems) window.BattleItems = {}; + let data = window.BattleItems[id]; + if (data && typeof data.exists === 'boolean') return data; + if (!data) data = {exists: false}; + let item = new Item(id, name, data); + window.BattleItems[id] = item; return item; }, - getAbility(ability: any): Ability { - if (!ability || typeof ability === 'string') { - let name = (ability || '').trim(); - let id = toId(name); - ability = (window.BattleAbilities && window.BattleAbilities[id]) || {}; - if (ability.name) ability.exists = true; - if (!ability.id) ability.id = id; - if (!ability.name) ability.name = Dex.sanitizeName(name); - if (!ability.category) ability.category = 'Effect'; - if (!ability.effectType) ability.effectType = 'Ability'; - if (!ability.gen) { - if (ability.num >= 165) { - ability.gen = 6; - } else if (ability.num >= 124) { - ability.gen = 5; - } else if (ability.num >= 77) { - ability.gen = 4; - } else if (ability.num >= 1) { - ability.gen = 3; - } else { - ability.gen = 0; - } - } + getAbility(nameOrAbility: string | Ability | null | undefined): Ability { + if (nameOrAbility && typeof nameOrAbility !== 'string') { + // TODO: don't accept Abilities here + return nameOrAbility; } + let name = nameOrAbility || ''; + let id = toId(nameOrAbility); + if (window.BattleAliases && id in BattleAliases) { + name = BattleAliases[id]; + id = toId(name); + } + if (!window.BattleAbilities) window.BattleAbilities = {}; + let data = window.BattleAbilities[id]; + if (data && typeof data.exists === 'boolean') return data; + if (!data) data = {exists: false}; + let ability = new Ability(id, name, data); + window.BattleAbilities[id] = ability; return ability; }, - getTemplate(template: any): Template { - if (!template || typeof template === 'string') { - let name = template; - let id = toId(name); - let speciesid = id; - if (window.BattleAliases && BattleAliases[id]) { - name = BattleAliases[id]; - id = toId(name); - } - if (!id) name = ''; - if (!window.BattlePokedex) window.BattlePokedex = {}; - if (!window.BattlePokedex[id]) { - template = window.BattlePokedex[id] = {}; - for (const baseid of baseSpeciesChart) { - if (id.length > baseid.length && id.substr(0, baseid.length) === baseid) { - template.baseSpecies = baseid; - template.forme = id.substr(baseid.length); - } - } - if (id !== 'yanmega' && id.slice(-4) === 'mega') { - template.baseSpecies = id.slice(0, -4); - template.forme = id.slice(-4); - } else if (id.slice(-6) === 'primal') { - template.baseSpecies = id.slice(0, -6); - template.forme = id.slice(-6); - } else if (id.slice(-5) === 'alola') { - template.baseSpecies = id.slice(0, -5); - template.forme = id.slice(-5); - } - template.exists = false; - } - template = window.BattlePokedex[id]; - if (template.species) name = template.species; - if (template.exists === undefined) template.exists = true; - if (!template.id) template.id = id; - if (!template.name) template.name = name = Dex.sanitizeName(name); - if (!template.speciesid) template.speciesid = id; - if (!template.species) template.species = name; - if (!template.baseSpecies) template.baseSpecies = name; - if (!template.forme) template.forme = ''; - if (!template.formeLetter) template.formeLetter = ''; - if (!template.formeid) { - let formeid = ''; - if (template.baseSpecies !== name) { - formeid = '-' + toId(template.forme); - } - template.formeid = formeid; - } - if (!template.spriteid) template.spriteid = toId(template.baseSpecies) + template.formeid; - if (!template.effectType) template.effectType = 'Template'; - if (!template.gen) { - if (template.forme && ['-mega', '-megax', '-megay'].includes(template.formeid)) { - template.gen = 6; - template.isMega = true; - template.battleOnly = true; - } else if (template.formeid === '-primal') { - template.gen = 6; - template.isPrimal = true; - template.battleOnly = true; - } else if (template.formeid.slice(-5) === 'totem') { - template.gen = 7; - template.isTotem = true; - } else if (template.formeid === '-alola') { - template.gen = 7; - } else if (template.num >= 722) { - template.gen = 7; - } else if (template.num >= 650) { - template.gen = 6; - } else if (template.num >= 494) { - template.gen = 5; - } else if (template.num >= 387) { - template.gen = 4; - } else if (template.num >= 252) { - template.gen = 3; - } else if (template.num >= 152) { - template.gen = 2; - } else if (template.num >= 1) { - template.gen = 1; - } else { - template.gen = 0; - } - } - if (template.otherForms && template.otherForms.indexOf(speciesid) >= 0) { - if (!window.BattlePokedexAltForms) window.BattlePokedexAltForms = {}; - if (!window.BattlePokedexAltForms[speciesid]) { - template = window.BattlePokedexAltForms[speciesid] = {...template}; - let form = speciesid.slice(template.baseSpecies.length); - let formid = '-' + form; - form = form[0].toUpperCase() + form.slice(1); - template.form = form; - template.species = template.baseSpecies + (form ? '-' + form : ''); - template.speciesid = toId(template.species); - template.spriteid = toId(template.baseSpecies) + formid; - } - template = window.BattlePokedexAltForms[speciesid]; - } - if (template.spriteid.slice(-5) === 'totem') template.spriteid = template.spriteid.slice(0, -5); - if (template.spriteid.slice(-1) === '-') template.spriteid = template.spriteid.slice(0, -1); + getTemplate(nameOrTemplate: string | Template | null | undefined): Template { + if (nameOrTemplate && typeof nameOrTemplate !== 'string') { + // TODO: don't accept Templates here + return nameOrTemplate; + } + let name = nameOrTemplate || ''; + let id = toId(nameOrTemplate); + let formid = id; + if (!window.BattlePokedexAltForms) window.BattlePokedexAltForms = {}; + if (formid in window.BattlePokedexAltForms) return window.BattlePokedexAltForms[formid]; + if (window.BattleAliases && id in BattleAliases) { + name = BattleAliases[id]; + id = toId(name); + } + if (!window.BattlePokedex) window.BattlePokedex = {}; + let data = window.BattlePokedex[id]; + if (data && typeof data.exists === 'boolean') return data; + if (!data) data = {exists: false}; + let template = new Template(id, name, data); + window.BattlePokedex[id] = template; + if (formid !== id && template.otherForms && template.otherForms.includes(formid)) { + id = formid; + let forme = formid.slice(id.length); + forme = forme[0].toUpperCase() + forme.slice(1); + name = template.baseSpecies + (forme ? '-' + forme : ''); + + template = window.BattlePokedexAltForms[formid] = new Template(id, name, { + ...template, + name, + forme, + }); } return template; }, @@ -721,367 +612,20 @@ const Dex = { else if (window.BattlePokedex && window.BattlePokedex[id] && BattlePokedex[id].num) num = BattlePokedex[id].num; if (num < 0) num = 0; if (num > 809) num = 0; - let altNums: {[id: string]: number} = { - egg: 816 + 1, - pikachubelle: 816 + 2, - pikachulibre: 816 + 3, - pikachuphd: 816 + 4, - pikachupopstar: 816 + 5, - pikachurockstar: 816 + 6, - pikachucosplay: 816 + 7, - // unown gap - castformrainy: 816 + 35, - castformsnowy: 816 + 36, - castformsunny: 816 + 37, - deoxysattack: 816 + 38, - deoxysdefense: 816 + 39, - deoxysspeed: 816 + 40, - burmysandy: 816 + 41, - burmytrash: 816 + 42, - wormadamsandy: 816 + 43, - wormadamtrash: 816 + 44, - cherrimsunshine: 816 + 45, - shelloseast: 816 + 46, - gastrodoneast: 816 + 47, - rotomfan: 816 + 48, - rotomfrost: 816 + 49, - rotomheat: 816 + 50, - rotommow: 816 + 51, - rotomwash: 816 + 52, - giratinaorigin: 816 + 53, - shayminsky: 816 + 54, - unfezantf: 816 + 55, - basculinbluestriped: 816 + 56, - darmanitanzen: 816 + 57, - deerlingautumn: 816 + 58, - deerlingsummer: 816 + 59, - deerlingwinter: 816 + 60, - sawsbuckautumn: 816 + 61, - sawsbucksummer: 816 + 62, - sawsbuckwinter: 816 + 63, - frillishf: 816 + 64, - jellicentf: 816 + 65, - tornadustherian: 816 + 66, - thundurustherian: 816 + 67, - landorustherian: 816 + 68, - kyuremblack: 816 + 69, - kyuremwhite: 816 + 70, - keldeoresolute: 816 + 71, - meloettapirouette: 816 + 72, - vivillonarchipelago: 816 + 73, - vivilloncontinental: 816 + 74, - vivillonelegant: 816 + 75, - vivillonfancy: 816 + 76, - vivillongarden: 816 + 77, - vivillonhighplains: 816 + 78, - vivillonicysnow: 816 + 79, - vivillonjungle: 816 + 80, - vivillonmarine: 816 + 81, - vivillonmodern: 816 + 82, - vivillonmonsoon: 816 + 83, - vivillonocean: 816 + 84, - vivillonpokeball: 816 + 85, - vivillonpolar: 816 + 86, - vivillonriver: 816 + 87, - vivillonsandstorm: 816 + 88, - vivillonsavanna: 816 + 89, - vivillonsun: 816 + 90, - vivillontundra: 816 + 91, - pyroarf: 816 + 92, - flabebeblue: 816 + 93, - flabebeorange: 816 + 94, - flabebewhite: 816 + 95, - flabebeyellow: 816 + 96, - floetteblue: 816 + 97, - floetteeternal: 816 + 98, - floetteorange: 816 + 99, - floettewhite: 816 + 100, - floetteyellow: 816 + 101, - florgesblue: 816 + 102, - florgesorange: 816 + 103, - florgeswhite: 816 + 104, - florgesyellow: 816 + 105, - furfroudandy: 816 + 106, - furfroudebutante: 816 + 107, - furfroudiamond: 816 + 108, - furfrouheart: 816 + 109, - furfroukabuki: 816 + 110, - furfroulareine: 816 + 111, - furfroumatron: 816 + 112, - furfroupharaoh: 816 + 113, - furfroustar: 816 + 114, - meowsticf: 816 + 115, - aegislashblade: 816 + 116, - hoopaunbound: 816 + 118, - rattataalola: 816 + 119, - raticatealola: 816 + 120, - raichualola: 816 + 121, - sandshrewalola: 816 + 122, - sandslashalola: 816 + 123, - vulpixalola: 816 + 124, - ninetalesalola: 816 + 125, - diglettalola: 816 + 126, - dugtrioalola: 816 + 127, - meowthalola: 816 + 128, - persianalola: 816 + 129, - geodudealola: 816 + 130, - graveleralola: 816 + 131, - golemalola: 816 + 132, - grimeralola: 816 + 133, - mukalola: 816 + 134, - exeggutoralola: 816 + 135, - marowakalola: 816 + 136, - greninjaash: 816 + 137, - zygarde10: 816 + 138, - zygardecomplete: 816 + 139, - oricoriopompom: 816 + 140, - oricoriopau: 816 + 141, - oricoriosensu: 816 + 142, - lycanrocmidnight: 816 + 143, - wishiwashischool: 816 + 144, - miniormeteor: 816 + 145, - miniororange: 816 + 146, - minioryellow: 816 + 147, - miniorgreen: 816 + 148, - miniorblue: 816 + 149, - miniorviolet: 816 + 150, - miniorindigo: 816 + 151, - magearnaoriginal: 816 + 152, - pikachuoriginal: 816 + 153, - pikachuhoenn: 816 + 154, - pikachusinnoh: 816 + 155, - pikachuunova: 816 + 156, - pikachukalos: 816 + 157, - pikachualola: 816 + 158, - pikachupartner: 816 + 159, - lycanrocdusk: 816 + 160, - necrozmaduskmane: 816 + 161, - necrozmadawnwings: 816 + 162, - necrozmaultra: 816 + 163, - pikachustarter: 816 + 164, - eeveestarter: 816 + 165, - gumshoostotem: 735, - raticatealolatotem: 816 + 120, - marowakalolatotem: 816 + 136, - araquanidtotem: 752, - lurantistotem: 754, - salazzletotem: 758, - vikavolttotem: 738, - togedemarutotem: 777, - mimikyutotem: 778, - mimikyubustedtotem: 778, - ribombeetotem: 743, - kommoototem: 784, - - venusaurmega: 984 + 0, - charizardmegax: 984 + 1, - charizardmegay: 984 + 2, - blastoisemega: 984 + 3, - beedrillmega: 984 + 4, - pidgeotmega: 984 + 5, - alakazammega: 984 + 6, - slowbromega: 984 + 7, - gengarmega: 984 + 8, - kangaskhanmega: 984 + 9, - pinsirmega: 984 + 10, - gyaradosmega: 984 + 11, - aerodactylmega: 984 + 12, - mewtwomegax: 984 + 13, - mewtwomegay: 984 + 14, - ampharosmega: 984 + 15, - steelixmega: 984 + 16, - scizormega: 984 + 17, - heracrossmega: 984 + 18, - houndoommega: 984 + 19, - tyranitarmega: 984 + 20, - sceptilemega: 984 + 21, - blazikenmega: 984 + 22, - swampertmega: 984 + 23, - gardevoirmega: 984 + 24, - sableyemega: 984 + 25, - mawilemega: 984 + 26, - aggronmega: 984 + 27, - medichammega: 984 + 28, - manectricmega: 984 + 29, - sharpedomega: 984 + 30, - cameruptmega: 984 + 31, - altariamega: 984 + 32, - banettemega: 984 + 33, - absolmega: 984 + 34, - glaliemega: 984 + 35, - salamencemega: 984 + 36, - metagrossmega: 984 + 37, - latiasmega: 984 + 38, - latiosmega: 984 + 39, - kyogreprimal: 984 + 40, - groudonprimal: 984 + 41, - rayquazamega: 984 + 42, - lopunnymega: 984 + 43, - garchompmega: 984 + 44, - lucariomega: 984 + 45, - abomasnowmega: 984 + 46, - gallademega: 984 + 47, - audinomega: 984 + 48, - dianciemega: 984 + 49, - - syclant: 1152 + 0, - revenankh: 1152 + 1, - pyroak: 1152 + 2, - fidgit: 1152 + 3, - stratagem: 1152 + 4, - arghonaut: 1152 + 5, - kitsunoh: 1152 + 6, - cyclohm: 1152 + 7, - colossoil: 1152 + 8, - krilowatt: 1152 + 9, - voodoom: 1152 + 10, - tomohawk: 1152 + 11, - necturna: 1152 + 12, - mollux: 1152 + 13, - aurumoth: 1152 + 14, - malaconda: 1152 + 15, - cawmodore: 1152 + 16, - volkraken: 1152 + 17, - plasmanta: 1152 + 18, - naviathan: 1152 + 19, - crucibelle: 1152 + 20, - crucibellemega: 1152 + 21, - kerfluffle: 1152 + 22, - pajantom: 1152 + 23, - jumbao: 1152 + 24, - caribolt: 1152 + 25, - smokomodo: 1152 + 26, - snaelstrom: 1152 + 27, - - syclar: 1188 + 0, - embirch: 1188 + 1, - flarelm: 1188 + 2, - breezi: 1188 + 3, - scratchet: 1188 + 4, - necturine: 1188 + 5, - cupra: 1188 + 6, - argalis: 1188 + 7, - brattler: 1188 + 8, - cawdet: 1188 + 9, - volkritter: 1188 + 10, - snugglow: 1188 + 11, - floatoy: 1188 + 12, - caimanoe: 1188 + 13, - pluffle: 1188 + 14, - rebble: 1188 + 15, - tactite: 1188 + 16, - privatyke: 1188 + 17, - nohface: 1188 + 18, - monohm: 1188 + 19, - duohm: 1188 + 20, - // protowatt: 1188 + 21, - voodoll: 1188 + 22, - mumbao: 1188 + 23, - }; - - if (altNums[id]) { - num = altNums[id]; + if (BattlePokemonIconIndexes[id]) { + num = BattlePokemonIconIndexes[id]; } if (pokemon && pokemon.gender === 'F') { if (id === 'unfezant' || id === 'frillish' || id === 'jellicent' || id === 'meowstic' || id === 'pyroar') { - num = altNums[id + 'f']; + num = BattlePokemonIconIndexes[id + 'f']; } } if (facingLeft) { - altNums = { - pikachubelle: 1044 + 0, - pikachupopstar: 1044 + 1, - clefairy: 1044 + 2, - clefable: 1044 + 3, - jigglypuff: 1044 + 4, - wigglytuff: 1044 + 5, - dugtrioalola: 1044 + 6, - poliwhirl: 1044 + 7, - poliwrath: 1044 + 8, - mukalola: 1044 + 9, - kingler: 1044 + 10, - croconaw: 1044 + 11, - cleffa: 1044 + 12, - igglybuff: 1044 + 13, - politoed: 1044 + 14, - // unown gap - sneasel: 1044 + 35, - teddiursa: 1044 + 36, - roselia: 1044 + 37, - zangoose: 1044 + 38, - seviper: 1044 + 39, - castformrainy: 1044 + 40, - absolmega: 1044 + 41, - absol: 1044 + 42, - regirock: 1044 + 43, - torterra: 1044 + 44, - budew: 1044 + 45, - roserade: 1044 + 46, - magmortar: 1044 + 47, - togekiss: 1044 + 48, - rotomwash: 1044 + 49, - shayminsky: 1044 + 50, - emboar: 1044 + 51, - pansear: 1044 + 52, - simisear: 1044 + 53, - drilbur: 1044 + 54, - excadrill: 1044 + 55, - sawk: 1044 + 56, - lilligant: 1044 + 57, - garbodor: 1044 + 58, - solosis: 1044 + 59, - vanilluxe: 1044 + 60, - amoonguss: 1044 + 61, - klink: 1044 + 62, - klang: 1044 + 63, - klinklang: 1044 + 64, - litwick: 1044 + 65, - golett: 1044 + 66, - golurk: 1044 + 67, - kyuremblack: 1044 + 68, - kyuremwhite: 1044 + 69, - kyurem: 1044 + 70, - keldeoresolute: 1044 + 71, - meloetta: 1044 + 72, - greninja: 1044 + 73, - greninjaash: 1044 + 74, - furfroudebutante: 1044 + 75, - barbaracle: 1044 + 76, - clauncher: 1044 + 77, - clawitzer: 1044 + 78, - sylveon: 1044 + 79, - klefki: 1044 + 80, - zygarde: 1044 + 81, - zygarde10: 1044 + 82, - zygardecomplete: 1044 + 83, - dartrix: 1044 + 84, - steenee: 1044 + 85, - tsareena: 1044 + 86, - comfey: 1044 + 87, - miniormeteor: 1044 + 88, - minior: 1044 + 89, - miniororange: 1044 + 90, - minioryellow: 1044 + 91, - miniorgreen: 1044 + 92, - miniorblue: 1044 + 93, - miniorviolet: 1044 + 94, - miniorindigo: 1044 + 95, - dhelmise: 1044 + 96, - necrozma: 1044 + 97, - marshadow: 1044 + 98, - pikachuoriginal: 1044 + 99, - pikachupartner: 1044 + 100, - necrozmaduskmane: 1044 + 101, - necrozmadawnwings: 1044 + 102, - necrozmaultra: 1044 + 103, - stakataka: 1044 + 104, - blacephalon: 1044 + 105, - }; - if (altNums[id]) { - num = altNums[id]; + if (BattlePokemonIconIndexesLeft[id]) { + num = BattlePokemonIconIndexesLeft[id]; } } diff --git a/src/battle.ts b/src/battle.ts index 8f052ac60..e14c5d113 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -76,7 +76,7 @@ class Pokemon { hp = 0; maxhp = 1000; level = 100; - gender: GenderName = ''; + gender: GenderName = 'N'; shiny = false; hpcolor: HPColor = 'g'; @@ -807,7 +807,7 @@ class Side { this.battle.scene.animUnsummon(pokemon); } - swapTo(pokemon: Pokemon, slot: number, kwArgs: {[k: string]: string}) { + swapTo(pokemon: Pokemon, slot: number, kwArgs: KWArgs) { if (pokemon.slot === slot) return; let target = this.active[slot]; @@ -825,7 +825,7 @@ class Side { this.battle.scene.animSummon(pokemon, slot, true); if (target) this.battle.scene.animSummon(target, oslot, true); } - swapWith(pokemon: Pokemon, target: Pokemon, kwArgs: {[k: string]: string}) { + swapWith(pokemon: Pokemon, target: Pokemon, kwArgs: KWArgs) { // method provided for backwards compatibility only if (pokemon === target) return; @@ -1189,7 +1189,7 @@ class Battle { } this.scene.updateWeather(); } - useMove(pokemon: Pokemon, move: Move, target: Pokemon | null, kwArgs: {[k: string]: string}) { + useMove(pokemon: Pokemon, move: Move, target: Pokemon | null, kwArgs: KWArgs) { let fromeffect = Dex.getEffect(kwArgs.from); pokemon.clearMovestatuses(); if (move.id === 'focuspunch') { @@ -1222,7 +1222,7 @@ class Battle { pokemon.side.wisher = pokemon; } } - animateMove(pokemon: Pokemon, move: Move, target: Pokemon | null, kwArgs: {[k: string]: string}) { + animateMove(pokemon: Pokemon, move: Move, target: Pokemon | null, kwArgs: KWArgs) { if (this.fastForward || kwArgs.still) return; if (!target) target = pokemon.side.foe.active[0]; @@ -1258,7 +1258,7 @@ class Battle { this.scene.runMoveAnim(usedMove.id, targets); } - cantUseMove(pokemon: Pokemon, effect: Effect, move: Move, kwArgs: {[k: string]: string}) { + cantUseMove(pokemon: Pokemon, effect: Effect, move: Move, kwArgs: KWArgs) { pokemon.clearMovestatuses(); this.scene.updateStatbar(pokemon); if (effect.id in BattleStatusAnims) {