diff --git a/.babelrc b/.babelrc index eaaa245c3..b7a403233 100644 --- a/.babelrc +++ b/.babelrc @@ -1,10 +1,14 @@ { - "presets": [ - "@babel/typescript" - ], "plugins": [ - "@babel/plugin-transform-member-expression-literals", - "@babel/plugin-transform-property-literals", + "@babel/plugin-transform-typescript", + ["@babel/plugin-transform-react-jsx", {"pragma": "Preact.h", "useBuiltIns": true}], + + ["@babel/plugin-proposal-class-properties", {"loose": true}], + + ["@babel/plugin-proposal-object-rest-spread", {"useBuiltIns": true}], + + "@babel/plugin-transform-exponentiation-operator", + "@babel/plugin-transform-arrow-functions", ["@babel/plugin-transform-block-scoping", {"throwIfClosureRequired": true}], ["@babel/plugin-transform-classes", {"loose": true}], @@ -16,10 +20,9 @@ "@babel/plugin-transform-shorthand-properties", ["@babel/plugin-transform-spread", {"loose": true}], ["@babel/plugin-transform-template-literals", {"loose": true}], - "@babel/plugin-transform-exponentiation-operator", - ["@babel/plugin-proposal-class-properties", {"loose": true}], - ["@babel/plugin-proposal-object-rest-spread", {"useBuiltIns": true}], - ["@babel/plugin-transform-react-jsx", {"pragma": "Preact.h", "useBuiltIns": true}] + + "@babel/plugin-transform-member-expression-literals", + "@babel/plugin-transform-property-literals" ], "ignore": [ "src/globals.d.ts" diff --git a/.gitignore b/.gitignore index d74eff66e..6606af559 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ npm-debug.log package-lock.json /vendor/ +/js/battle.js /js/battledata.js /js/battle-dex.js /js/battle-dex-data.js diff --git a/data/graphics.js b/data/graphics.js index 2b3aa5859..7aa1fd0d3 100644 --- a/data/graphics.js +++ b/data/graphics.js @@ -303,9 +303,6 @@ var BattleBackdrops = [ 'bg-orassea.jpg', 'bg-skypillar.jpg' ]; -var BattleStats = { - atk: 'Attack', def: 'Defense', spa: 'Special Attack', spd: 'Special Defense', spe: 'Speed', accuracy: 'accuracy', evasion: 'evasiveness', spc: 'Special' -}; var BattleItems = { }; var BattleOtherAnims = { diff --git a/js/client.js b/js/client.js index 0cef133e6..1458e90f2 100644 --- a/js/client.js +++ b/js/client.js @@ -2326,7 +2326,7 @@ order: 10007 }, '\u2605': { - name: "Player (\u2605)", + name: "Host (\u2605)", type: 'normal', order: 10008 }, diff --git a/src/battle-dex-data.ts b/src/battle-dex-data.ts index 424951994..51eea0bf0 100644 --- a/src/battle-dex-data.ts +++ b/src/battle-dex-data.ts @@ -10,6 +10,8 @@ * @license MIT */ +type ID = string & {__isID: true}; + const BattleNatures = { Adamant: { plus: 'atk', @@ -134,6 +136,9 @@ const BattleStatNames = { // proper style spd: 'SpD', spe: 'Spe' }; +const BattleStats = { + atk: 'Attack', def: 'Defense', spa: 'Special Attack', spd: 'Special Defense', spe: 'Speed', accuracy: 'accuracy', evasion: 'evasiveness', spc: 'Special' +}; const baseSpeciesChart = [ 'pikachu', @@ -193,8 +198,12 @@ const baseSpeciesChart = [ ]; type StatName = 'hp' | 'atk' | 'def' | 'spa' | 'spd' | 'spe'; +type StatusName = 'par' | 'psn' | 'frz' | 'slp' | 'brn'; +type BoostStatName = 'atk' | 'def' | 'spa' | 'spd' | 'spe' | 'evasion' | 'accuracy' | 'spc'; +type GenderName = 'M' | 'F' | ''; + interface Effect { - readonly id: string; + readonly id: ID; readonly name: string; readonly gen: number; readonly effectType: 'Item' | 'Move' | 'Ability' | 'Template'; @@ -202,11 +211,19 @@ interface Effect { } interface Item extends Effect { readonly effectType: 'Item'; + readonly zMoveFrom?: string; } interface Move extends Effect { readonly effectType: 'Move'; readonly type: string; readonly category: string; + readonly isZ?: string; + + // TODO: move to different interface + readonly anim: Function; + readonly residualAnim: Function; + readonly prepareAnim: Function; + readonly prepareMessage: Function; } interface Ability extends Effect { readonly effectType: 'Ability'; @@ -214,12 +231,19 @@ interface Ability extends Effect { interface Template extends Effect { readonly effectType: 'Template'; 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; + readonly weightkg: number; readonly isNonstandard: boolean; + readonly isTotem?: boolean; + readonly isMega?: boolean; } diff --git a/src/battle-dex.ts b/src/battle-dex.ts index 43b4164cc..c0a1bfdc8 100644 --- a/src/battle-dex.ts +++ b/src/battle-dex.ts @@ -88,8 +88,8 @@ function toId(text: any) { } else if (text && text.userid) { text = text.userid; } - if (typeof text !== 'string' && typeof text !== 'number') return ''; - return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, ''); + if (typeof text !== 'string' && typeof text !== 'number') return '' as ID; + return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '') as ID; } function toUserid(text: any) { @@ -116,6 +116,17 @@ function toName(name: any) { return name; } +interface SpriteData { + w: number; + h: number; + y?: number; + url?: string; + pixelated?: boolean; + isBackSprite?: boolean; + cryurl?: string; + shiny?: boolean; +} + const Tools = { resourcePrefix: (() => { @@ -627,17 +638,17 @@ const Tools = { 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 = $.extend({}, move); + move = {...move}; move.basePower = matches[2]; } if (!move.exists && id.substr(0, 6) === 'return' && id.length > 6) { move = (window.BattleMovedex && window.BattleMovedex['return']) || {}; - move = $.extend({}, move); + 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 = $.extend({}, move); + move = {...move}; move.basePower = id.slice(11); } @@ -669,7 +680,7 @@ const Tools = { if (window.BattleMoveAnims) { if (!move.anim) move.anim = BattleOtherAnims.attack.anim; - $.extend(move, BattleMoveAnims[move.id]); + Object.assign(move, BattleMoveAnims[move.id]); } } return move; @@ -839,7 +850,7 @@ const Tools = { getType(type: any): Effect { if (!type || typeof type === 'string') { - let id = toId(type); + let id = toId(type) as string; id = id.substr(0, 1).toUpperCase() + id.substr(1); type = (window.BattleTypeChart && window.BattleTypeChart[id]) || {}; if (type.damageTaken) type.exists = true; @@ -865,10 +876,14 @@ const Tools = { el.src = path + 'data/pokedex-mini-bw.js' + qs; document.getElementsByTagName('body')[0].appendChild(el); }, - getSpriteData(pokemon: any, siden: number, options?: any) { - if (!options) options = {gen: 6}; + getSpriteData(pokemon: Pokemon | Template | string, siden: number, options: {gen?: number, shiny?: boolean, gender?: GenderName, afd?: boolean, noScale?: boolean} = {gen: 6}) { if (!options.gen) options.gen = 6; - pokemon = Tools.getTemplate(pokemon); + if (pokemon instanceof Pokemon) { + options.shiny = pokemon.shiny; + options.gender = pokemon.gender; + pokemon = pokemon.getSpecies(); + } + const template = Tools.getTemplate(pokemon); let spriteData = { w: 96, h: 96, @@ -877,9 +892,9 @@ const Tools = { pixelated: true, isBackSprite: false, cryurl: '', - shiny: pokemon.shiny + shiny: options.shiny }; - let name = pokemon.spriteid; + let name = template.spriteid; let dir, facing; if (siden) { dir = ''; @@ -894,13 +909,13 @@ const Tools = { let fieldGenNum = options.gen; if (Tools.prefs('nopastgens')) fieldGenNum = 6; if (Tools.prefs('bwgfx') && fieldGenNum >= 6) fieldGenNum = 5; - let genNum = Math.max(fieldGenNum, Math.min(pokemon.gen, 5)); + let genNum = Math.max(fieldGenNum, Math.min(template.gen, 5)); let gen = ['', 'rby', 'gsc', 'rse', 'dpp', 'bw', 'xy', 'xy'][genNum]; let animationData = null; let miscData = null; - let speciesid = pokemon.speciesid; - if (pokemon.isTotem) speciesid = toId(name); + let speciesid = template.speciesid; + if (template.isTotem) speciesid = toId(name); if (gen === 'xy' && window.BattlePokemonSprites) { animationData = BattlePokemonSprites[speciesid]; } @@ -913,15 +928,15 @@ const Tools = { if (!miscData) miscData = {}; if (miscData.num > 0) { - spriteData.cryurl = 'audio/cries/' + toId(pokemon.baseSpecies); - let formeid = pokemon.formeid; - if (pokemon.isMega || formeid && (formeid === '-sky' || formeid === '-therian' || formeid === '-primal' || formeid === '-eternal' || pokemon.baseSpecies === 'Kyurem' || formeid === '-super' || formeid === '-unbound' || formeid === '-midnight' || formeid === '-school' || pokemon.baseSpecies === 'Oricorio' || pokemon.baseSpecies === 'Zygarde')) { + spriteData.cryurl = 'audio/cries/' + toId(template.baseSpecies); + let formeid = template.formeid; + if (template.isMega || formeid && (formeid === '-sky' || formeid === '-therian' || formeid === '-primal' || formeid === '-eternal' || template.baseSpecies === 'Kyurem' || formeid === '-super' || formeid === '-unbound' || formeid === '-midnight' || formeid === '-school' || template.baseSpecies === 'Oricorio' || template.baseSpecies === 'Zygarde')) { spriteData.cryurl += formeid; } spriteData.cryurl += (window.nodewebkit ? '.ogg' : '.mp3'); } - if (pokemon.shiny && options.gen > 1) dir += '-shiny'; + if (options.shiny && options.gen > 1) dir += '-shiny'; // April Fool's 2014 if (window.Config && Config.server && Config.server.afd || options.afd) { @@ -930,7 +945,7 @@ const Tools = { return spriteData; } - if (animationData[facing + 'f'] && pokemon.gender === 'F') facing += 'f'; + if (animationData[facing + 'f'] && options.gender === 'F') facing += 'f'; let allowAnim = !Tools.prefs('noanim') && !Tools.prefs('nogif'); if (allowAnim && genNum >= 6) spriteData.pixelated = false; if (allowAnim && animationData[facing] && genNum >= 5) { @@ -948,7 +963,7 @@ const Tools = { // Gender differences don't exist prior to Gen 4, // so there are no sprites for it - if (genNum >= 4 && miscData['frontf'] && pokemon.gender === 'F') { + if (genNum >= 4 && miscData['frontf'] && options.gender === 'F') { name += '-f'; } @@ -972,7 +987,7 @@ const Tools = { if (fieldGenNum === 5 && spriteData.isBackSprite) spriteData.y += 40; if (genNum <= 2) spriteData.y += 2; } - if (pokemon.isTotem && !options.noScale) { + if (template.isTotem && !options.noScale) { spriteData.w *= 1.5; spriteData.h *= 1.5; spriteData.y += -11; diff --git a/js/battle.js b/src/battle.ts similarity index 78% rename from js/battle.js rename to src/battle.ts index d3efb1044..17e06f6b4 100644 --- a/js/battle.js +++ b/src/battle.ts @@ -1,9 +1,11 @@ -/* - -License: GPLv2 - - -*/ +/** + * Pokemon Showdown Battle + * + * This is the main file for handling battle animations + * + * @author Guangcong Luo + * @license AGPLv3 + */ // par: -webkit-filter: sepia(100%) hue-rotate(373deg) saturate(592%); // -webkit-filter: sepia(100%) hue-rotate(22deg) saturate(820%) brightness(29%); @@ -12,49 +14,47 @@ License: GPLv2 // slp: -webkit-filter: grayscale(100%); // frz: -webkit-filter: sepia(100%) hue-rotate(154deg) saturate(759%) brightness(23%); -$.extend($.easing, { - ballisticUp: function (x, t, b, c, d) { +// @ts-ignore +Object.assign($.easing, { + ballisticUp(x: number, t: number, b: number, c: number, d: number) { return -3 * x * x + 4 * x; }, - ballisticDown: function (x, t, b, c, d) { + ballisticDown(x: number, t: number, b: number, c: number, d: number) { x = 1 - x; return 1 - (-3 * x * x + 4 * x); }, - quadUp: function (x, t, b, c, d) { + quadUp(x: number, t: number, b: number, c: number, d: number) { x = 1 - x; return 1 - (x * x); }, - quadDown: function (x, t, b, c, d) { + quadDown(x: number, t: number, b: number, c: number, d: number) { return x * x; } }); -var BattleSoundLibrary = (function () { - function BattleSoundLibrary() { - // effects - this.effectCache = {}; +const BattleSound = new class { + effectCache = {} as {[url: string]: any}; - // bgm - this.bgmCache = {}; - this.bgm = null; + // bgm + bgmCache = {} as {[url: string]: any}; + bgm: any = null!; - // misc - this.soundPlaceholder = { - play: function () { return this; }, - pause: function () { return this; }, - stop: function () { return this; }, - resume: function () { return this; }, - setVolume: function () { return this; }, - onposition: function () { return this; } - }; + // misc + soundPlaceholder = { + play: function () { return this; }, + pause: function () { return this; }, + stop: function () { return this; }, + resume: function () { return this; }, + setVolume: function () { return this; }, + onposition: function () { return this; } } // options - BattleSoundLibrary.prototype.effectVolume = 50; - BattleSoundLibrary.prototype.bgmVolume = 50; - BattleSoundLibrary.prototype.muted = false; + effectVolume = 50; + bgmVolume = 50; + muted = false; - BattleSoundLibrary.prototype.loadEffect = function (url) { + loadEffect(url: string) { if (this.effectCache[url] && this.effectCache[url] !== this.soundPlaceholder) { return this.effectCache[url]; } @@ -69,12 +69,12 @@ var BattleSoundLibrary = (function () { this.effectCache[url] = this.soundPlaceholder; } return this.effectCache[url]; - }; - BattleSoundLibrary.prototype.playEffect = function (url) { + } + playEffect(url: string) { if (!this.muted) this.loadEffect(url).setVolume(this.effectVolume).play(); - }; + } - BattleSoundLibrary.prototype.loadBgm = function (url, loopstart, loopend) { + loadBgm(url: string, loopstart?: number, loopend?: number) { if (this.bgmCache[url]) { if (this.bgmCache[url] !== this.soundPlaceholder || loopstart === undefined) { return this.bgmCache[url]; @@ -92,12 +92,12 @@ var BattleSoundLibrary = (function () { // suppress crash return (this.bgmCache[url] = this.soundPlaceholder); } - this.bgmCache[url].onposition(loopend, function (evP) { - this.setPosition(this.position - (loopend - loopstart)); + this.bgmCache[url].onposition(loopend, function (this: any, evP: any) { + this.setPosition(this.position - (loopend! - loopstart!)); }); return this.bgmCache[url]; - }; - BattleSoundLibrary.prototype.playBgm = function (url, loopstart, loopstop) { + } + playBgm(url: string, loopstart?: number, loopstop?: number) { if (this.bgm === this.loadBgm(url, loopstart, loopstop)) { if (!this.bgm.paused && this.bgm.playState) { return; @@ -115,21 +115,21 @@ var BattleSoundLibrary = (function () { } } } catch (e) {} - }; - BattleSoundLibrary.prototype.pauseBgm = function () { + } + pauseBgm() { if (this.bgm) { this.bgm.pause(); } - }; - BattleSoundLibrary.prototype.stopBgm = function () { + } + stopBgm() { if (this.bgm) { this.bgm.stop(); this.bgm = null; } - }; + } // setting - BattleSoundLibrary.prototype.setMute = function (muted) { + setMute(muted: boolean) { muted = !!muted; if (this.muted == muted) return; this.muted = muted; @@ -138,82 +138,128 @@ var BattleSoundLibrary = (function () { } else { if (this.bgm) this.bgm.play(); } - }; + } - function loudnessPercentToAmplitudePercent(loudnessPercent) { + loudnessPercentToAmplitudePercent(loudnessPercent: number) { // 10 dB is perceived as approximately twice as loud - var decibels = 10 * Math.log(loudnessPercent / 100) / Math.log(2); + let decibels = 10 * Math.log(loudnessPercent / 100) / Math.log(2); return Math.pow(10, decibels / 20) * 100; } - BattleSoundLibrary.prototype.setBgmVolume = function (bgmVolume) { - this.bgmVolume = loudnessPercentToAmplitudePercent(bgmVolume); + setBgmVolume(bgmVolume: number) { + this.bgmVolume = this.loudnessPercentToAmplitudePercent(bgmVolume); if (this.bgm) { try { this.bgm.setVolume(this.bgmVolume); } catch (e) {} } - }; - BattleSoundLibrary.prototype.setEffectVolume = function (effectVolume) { - this.effectVolume = loudnessPercentToAmplitudePercent(effectVolume); - }; + } + setEffectVolume(effectVolume: number) { + this.effectVolume = this.loudnessPercentToAmplitudePercent(effectVolume); + } +}; - return BattleSoundLibrary; -})(); +/** [id, element?, ...misc] */ +type EffectState = any[] & {0: string, 1: JQuery | null}; +/** [name, minTimeLeft, maxTimeLeft] */ +type WeatherState = [string, number, number]; +type EffectTable = {[effectid: string]: EffectState}; +type HPColor = 'r' | 'y' | 'g'; -var BattleSound = new BattleSoundLibrary(); +class Pokemon { + name = ''; + species = ''; -var Pokemon = (function () { - function Pokemon(species, side) { - this.name = ''; - this.species = ''; - this.searchid = ''; + /** + * A string representing information extractable from textual + * messages: side, nickname. + * + * Will be the empty string between Team Preview and the first + * switch-in. + * + * Examples: `p1: Unown` or `p2: Sparky` + */ + ident = ''; + /** + * A string representing visible information not included in + * ident: species, level, gender, shininess. Level is left off + * if it's 100; gender is left off if it's genderless. + * + * Note: Can be partially filled out in Team Preview, because certain + * forme information and shininess isn't visible there. In those + * cases, details can change during the first switch-in, but will + * otherwise not change over the course of a game. + * + * Examples: `Mimikyu, L50, F`, `Steelix, M, shiny` + */ + details = ''; + /** + * `` `${ident}|${details}` ``. Tracked for ease of searching. + * + * As with ident and details, will only change during the first + * switch-in. + */ + searchid = ''; + side: Side; + slot = 0; + + fainted = false; + hp = 0; + maxhp = 1000; + level = 100; + gender: GenderName = ''; + shiny = false; + + hpcolor = 'g' as HPColor; + moves = [] as string[]; + ability = ''; + baseAbility = ''; + item = ''; + itemEffect = ''; + prevItem = ''; + prevItemEffect = ''; + + boosts = {} as {[stat: string]: number}; + status = ''; + statusStage = 0; + volatiles = {} as EffectTable; + turnstatuses = {} as EffectTable; + movestatuses = {} as EffectTable; + weightkg = 0; + lastMove = ''; + + /** [[moveName, ppUsed]] */ + moveTrack = [] as [string, number][]; + statusData = {sleepTurns: 0, toxicTurns: 0}; + + sprite: Sprite; + + statbarElem: JQuery | null = null; + constructor(data: any, side: Side) { this.side = side; + this.species = data.species; - this.fainted = false; - this.hp = 0; - this.maxhp = 0; - this.hpcolor = ''; - this.moves = []; - this.ability = ''; - this.baseAbility = ''; - this.item = ''; - this.itemEffect = ''; - this.prevItem = ''; - this.prevItemEffect = ''; - this.species = species; - - this.boosts = {}; - this.status = ''; - this.statusStage = 0; - this.volatiles = {}; - this.turnstatuses = {}; - this.movestatuses = {}; - this.lastmove = ''; - - // [[moveName, ppUsed]] - this.moveTrack = []; - this.statusData = {sleepTurns: 0, toxicTurns: 0}; - - this.statbarElem = null; + // TODO: stop doing this + Object.assign(this, Tools.getTemplate(data.species)); + Object.assign(this, data); } - Pokemon.prototype.getHPColor = function () { + getHPColor(): HPColor { if (this.hpcolor) return this.hpcolor; - var ratio = this.hp / this.maxhp; + let ratio = this.hp / this.maxhp; if (ratio > 0.5) return 'g'; if (ratio > 0.2) return 'y'; return 'r'; - }; - Pokemon.prototype.getHPColorClass = function () { + } + getHPColorClass() { switch (this.getHPColor()) { case 'y': return ' hpbar-yellow'; case 'r': return ' hpbar-red'; } return ''; - }; - Pokemon.prototype.getPixelRange = function (pixels, color) { - var epsilon = 0.5 / 714; + } + getPixelRange(pixels: number, color: HPColor): [number, number] { + let epsilon = 0.5 / 714; if (pixels === 0) return [0, 0]; if (pixels === 1) return [0 + epsilon, 2 / 48 - epsilon]; @@ -234,16 +280,16 @@ var Pokemon = (function () { if (pixels === 48) return [1, 1]; return [pixels / 48, (pixels + 1) / 48 - epsilon]; - }; - Pokemon.prototype.getFormattedRange = function (range, precision, separator) { + } + getFormattedRange(range: [number, number], precision: number, separator: string) { if (range[0] === range[1]) { - var percentage = Math.abs(range[0] * 100); + let percentage = Math.abs(range[0] * 100); if (Math.floor(percentage) === percentage) { return percentage + '%'; } return percentage.toFixed(precision) + '%'; } - var lower, upper; + let lower, upper; if (precision === 0) { lower = Math.floor(range[0] * 100); upper = Math.ceil(range[1] * 100); @@ -252,39 +298,39 @@ var Pokemon = (function () { upper = (range[1] * 100).toFixed(precision); } return lower + separator + upper + '%'; - }; + } // Returns [min, max] damage dealt as a proportion of total HP from 0 to 1 - Pokemon.prototype.getDamageRange = function (damage) { + getDamageRange(damage: any): [number, number] { if (damage[1] !== 48) { - var ratio = damage[0] / damage[1]; + let ratio = damage[0] / damage[1]; return [ratio, ratio]; - } else if (damage[3] === undefined) { + } else if (damage.length === undefined) { // wrong pixel damage. // this case exists for backward compatibility only. return [damage[2] / 100, damage[2] / 100]; } // pixel damage - var oldrange = this.getPixelRange(damage[3], damage[4]); - var newrange = this.getPixelRange(damage[3] + damage[0], this.hpcolor); + let oldrange = this.getPixelRange(damage[3], damage[4]); + let newrange = this.getPixelRange(damage[3] + damage[0], this.hpcolor); if (damage[0] === 0) { // no change in displayed pixel width return [0, newrange[1] - newrange[0]]; } if (oldrange[0] < newrange[0]) { // swap order - var r = oldrange; + let r = oldrange; oldrange = newrange; newrange = r; } return [oldrange[0] - newrange[1], oldrange[1] - newrange[0]]; - }; - Pokemon.prototype.healthParse = function (hpstring, parsedamage, heal) { - // returns [delta, denominator, percent(, oldnum, oldcolor)] or false - if (!hpstring || !hpstring.length) return false; - var parenIndex = hpstring.lastIndexOf('('); + } + healthParse(hpstring: string, parsedamage?: boolean, heal?: boolean): [number, number, number] | [number, number, number, number, HPColor] | null { + // returns [delta, denominator, percent(, oldnum, oldcolor)] or null + if (!hpstring || !hpstring.length) return null; + let parenIndex = hpstring.lastIndexOf('('); if (parenIndex >= 0) { // old style damage and health reporting if (parsedamage) { - var damage = parseFloat(hpstring); + let damage = parseFloat(hpstring); // unusual check preseved for backward compatbility if (isNaN(damage)) damage = 50; if (heal) { @@ -294,38 +340,38 @@ var Pokemon = (function () { this.hp -= this.maxhp * damage / 100; } // parse the absolute health information - var ret = this.healthParse(hpstring); + let ret = this.healthParse(hpstring); if (ret && (ret[1] === 100)) { // support for old replays with nearest-100th damage and health return [damage, 100, damage]; } // complicated expressions preserved for backward compatibility - var percent = Math.round(Math.ceil(damage * 48 / 100) / 48 * 100); - var pixels = Math.ceil(damage * 48 / 100); + let percent = Math.round(Math.ceil(damage * 48 / 100) / 48 * 100); + let pixels = Math.ceil(damage * 48 / 100); return [pixels, 48, percent]; } if (hpstring.substr(hpstring.length - 1) !== ')') { - return false; + return null; } hpstring = hpstring.substr(parenIndex + 1, hpstring.length - parenIndex - 2); } - var oldhp = this.fainted ? 0 : (this.hp || 1); - var oldmaxhp = this.maxhp; - var oldwidth = this.hpWidth(100); - var oldcolor = this.hpcolor; + let oldhp = this.fainted ? 0 : (this.hp || 1); + let oldmaxhp = this.maxhp; + let oldwidth = this.hpWidth(100); + let oldcolor = this.hpcolor; this.side.battle.parseHealth(hpstring, this); if (oldmaxhp === 0) { // max hp not known before parsing this message oldmaxhp = oldhp = this.maxhp; } - var oldnum = oldhp ? (Math.floor(oldhp / oldmaxhp * this.maxhp) || 1) : 0; - var delta = this.hp - oldnum; - var deltawidth = this.hpWidth(100) - oldwidth; + let oldnum = oldhp ? (Math.floor(oldhp / oldmaxhp * this.maxhp) || 1) : 0; + let delta = this.hp - oldnum; + let deltawidth = this.hpWidth(100) - oldwidth; return [delta, this.maxhp, deltawidth, oldnum, oldcolor]; - }; - Pokemon.prototype.checkDetails = function (details) { + } + checkDetails(details?: string) { if (!details) return false; if (details === this.details) return true; if (this.searchid) return false; @@ -335,26 +381,26 @@ var Pokemon = (function () { // the actual forme was hidden on Team Preview details = details.replace(/(-[A-Za-z0-9]+)?(, |$)/, '-*$2'); return (details === this.details); - }; - Pokemon.prototype.getIdent = function () { - var slots = ['a', 'b', 'c', 'd', 'e', 'f']; + } + getIdent() { + let slots = ['a', 'b', 'c', 'd', 'e', 'f']; return this.ident.substr(0, 2) + slots[this.slot] + this.ident.substr(2); - }; - Pokemon.prototype.removeVolatile = function (volatile) { + } + removeVolatile(volatile: ID) { if (!this.hasVolatile(volatile)) return; if (volatile === 'formechange') { this.sprite.removeTransform(); } - if (this.volatiles[volatile][1]) this.volatiles[volatile][1].remove(); + if (this.volatiles[volatile][1]) this.volatiles[volatile][1]!.remove(); delete this.volatiles[volatile]; - }; - Pokemon.prototype.addVolatile = function (volatile) { - var battle = this.side.battle; + } + addVolatile(volatile: ID) { + let battle = this.side.battle; if (this.hasVolatile(volatile)) return; this.volatiles[volatile] = [volatile, null]; if (volatile === 'leechseed') { this.side.battle.spriteElemsFront[this.side.n].append(''); - var curelem = this.side.battle.spriteElemsFront[this.side.n].children().last(); + let curelem = this.side.battle.spriteElemsFront[this.side.n].children().last(); curelem.css(battle.pos({ display: 'block', x: this.sprite.x - 30, @@ -363,7 +409,7 @@ var Pokemon = (function () { scale: .2, opacity: .6 }, BattleEffects.energyball)); - var elem = curelem; + let elem = curelem; this.side.battle.spriteElemsFront[this.side.n].append(''); curelem = this.side.battle.spriteElemsFront[this.side.n].children().last(); @@ -390,28 +436,28 @@ var Pokemon = (function () { elem = elem.add(curelem); this.volatiles[volatile][1] = elem; } - }; - Pokemon.prototype.hasVolatile = function (volatile) { + } + hasVolatile(volatile: ID) { return !!this.volatiles[volatile]; - }; - Pokemon.prototype.removeTurnstatus = function (volatile) { + } + removeTurnstatus(volatile: ID) { if (!this.hasTurnstatus(volatile)) return; - if (this.turnstatuses[volatile][1]) this.turnstatuses[volatile][1].remove(); + if (this.turnstatuses[volatile][1]) this.turnstatuses[volatile][1]!.remove(); delete this.turnstatuses[volatile]; - }; - Pokemon.prototype.addTurnstatus = function (volatile) { + } + addTurnstatus(volatile: ID) { volatile = toId(volatile); - var battle = this.side.battle; + let battle = this.side.battle; if (this.hasTurnstatus(volatile)) { if ((volatile === 'protect' || volatile === 'magiccoat') && !battle.fastForward) { - this.turnstatuses[volatile][1].css(battle.pos({ + this.turnstatuses[volatile][1]!.animate(battle.pos({ x: this.sprite.x, y: this.sprite.y, z: this.sprite.behind(-15), xscale: 1 * 1.2, yscale: .7 * 1.2, opacity: 1 - }, BattleEffects.none), 300).animate(battle.pos({ + }, BattleEffects.none), 100).animate(battle.pos({ x: this.sprite.x, y: this.sprite.y, z: this.sprite.behind(-15), @@ -425,7 +471,7 @@ var Pokemon = (function () { this.turnstatuses[volatile] = [volatile, null]; if (volatile === 'protect' || volatile === 'magiccoat') { this.side.battle.spriteElemsFront[this.side.n].append('