mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-09-30 06:43:02 -05:00
Migrate to optional chaining and TypeScript 3.7
This is something I probably wouldn't have rushed nearly as much,
except I have a serious use-case coming up; specifically
```js
foo = document.querySelector('foo')?.bar;
```
in a class property.
`document.querySelector('foo') && document.querySelector('foo').bar`
is incredibly inefficient here, but TypeScript doesn't consider
`(document.querySelector('foo') || {}).bar` to be valid because `{}`
doesn't have the `.bar` property. Creating a temporary variable
would have been a huge mess because this was a class property
initializer.
In hindsight, `(document.querySelector('foo')! || {}).bar` would
probably have worked, but too late now.
This commit is contained in:
1
.babelrc
1
.babelrc
@@ -5,6 +5,7 @@
|
||||
|
||||
["@babel/plugin-proposal-class-properties", {"loose": true}],
|
||||
|
||||
["@babel/plugin-proposal-optional-chaining", {"loose": true}],
|
||||
["@babel/plugin-proposal-object-rest-spread", {"loose": true, "useBuiltIns": true}],
|
||||
"@babel/plugin-proposal-optional-catch-binding",
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.3.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.3.3",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.6.0",
|
||||
"@babel/plugin-transform-member-expression-literals": "^7.2.0",
|
||||
"@babel/plugin-transform-property-literals": "^7.2.0",
|
||||
"@babel/plugin-transform-react-jsx": "^7.3.0",
|
||||
@@ -33,7 +34,7 @@
|
||||
"mocha": "^6.0.2",
|
||||
"preact": "^8.3.1",
|
||||
"tslint": "^5.13.0",
|
||||
"typescript": "^3.4.5"
|
||||
"typescript": "^3.7.0-dev.20191011"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -557,7 +557,7 @@ class BattleScene {
|
||||
if (Dex.prefs('nopastgens')) gen = 6;
|
||||
if (Dex.prefs('bwgfx') && gen > 5) gen = 5;
|
||||
this.gen = gen;
|
||||
this.activeCount = this.battle.mySide && this.battle.mySide.active.length || 1;
|
||||
this.activeCount = this.battle.mySide?.active.length || 1;
|
||||
|
||||
const isSPL = (typeof this.battle.rated === 'string' && this.battle.rated.startsWith("Smogon Premier League"));
|
||||
let bg: string;
|
||||
@@ -584,7 +584,7 @@ class BattleScene {
|
||||
}
|
||||
|
||||
getDetailsText(pokemon: Pokemon) {
|
||||
let name = pokemon.side && pokemon.side.n &&
|
||||
let name = pokemon.side?.n &&
|
||||
(this.battle.ignoreOpponent || this.battle.ignoreNicks) ? pokemon.species : pokemon.name;
|
||||
if (name !== pokemon.species) {
|
||||
name += ' (' + pokemon.species + ')';
|
||||
@@ -618,9 +618,9 @@ class BattleScene {
|
||||
let poke = side.pokemon[i];
|
||||
if (i >= side.totalPokemon && i >= side.pokemon.length) {
|
||||
pokemonhtml += `<span class="picon" style="` + Dex.getPokemonIcon('pokeball-none') + `"></span>`;
|
||||
} else if (noShow && poke && poke.fainted) {
|
||||
} else if (noShow && poke?.fainted) {
|
||||
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball-fainted') + `" title="Fainted" aria-label="Fainted"></span>`;
|
||||
} else if (noShow && poke && poke.status) {
|
||||
} else if (noShow && poke?.status) {
|
||||
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball-statused') + `" title="Status" aria-label="Status"></span>`;
|
||||
} else if (noShow) {
|
||||
pokemonhtml += `<span class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${i}" style="` + Dex.getPokemonIcon('pokeball') + `" title="Non-statused" aria-label="Non-statused"></span>`;
|
||||
@@ -1322,9 +1322,7 @@ class BattleScene {
|
||||
return pokemon.sprite.afterMove();
|
||||
}
|
||||
updateSpritesForSide(side: Side) {
|
||||
if (side.missedPokemon && side.missedPokemon.sprite) {
|
||||
side.missedPokemon.sprite.destroy();
|
||||
}
|
||||
side.missedPokemon?.sprite?.destroy();
|
||||
|
||||
side.missedPokemon = {
|
||||
sprite: new PokemonSprite(null, {
|
||||
|
||||
@@ -133,9 +133,9 @@ function getString(str: any) {
|
||||
}
|
||||
|
||||
function toID(text: any) {
|
||||
if (text && text.id) {
|
||||
if (text?.id) {
|
||||
text = text.id;
|
||||
} else if (text && text.userid) {
|
||||
} else if (text?.userid) {
|
||||
text = text.userid;
|
||||
}
|
||||
if (typeof text !== 'string' && typeof text !== 'number') return '' as ID;
|
||||
@@ -220,16 +220,16 @@ const Dex = new class implements ModdedDex {
|
||||
|
||||
resourcePrefix = (() => {
|
||||
let prefix = '';
|
||||
if (!window.document || !document.location || document.location.protocol !== 'http:') prefix = 'https:';
|
||||
return prefix + '//play.pokemonshowdown.com/';
|
||||
if (window.document?.location?.protocol !== 'http:') prefix = 'https:';
|
||||
return `${prefix}//play.pokemonshowdown.com/`;
|
||||
})();
|
||||
|
||||
fxPrefix = (() => {
|
||||
if (window.document && document.location && document.location.protocol === 'file:') {
|
||||
if (window.Replays) return 'https://play.pokemonshowdown.com/fx/';
|
||||
return 'fx/';
|
||||
if (window.document?.location?.protocol === 'file:') {
|
||||
if (window.Replays) return `https://play.pokemonshowdown.com/fx/`;
|
||||
return `fx/`;
|
||||
}
|
||||
return '//play.pokemonshowdown.com/fx/';
|
||||
return `//play.pokemonshowdown.com/fx/`;
|
||||
})();
|
||||
|
||||
loadedSpriteData = {xy: 1, bw: 0};
|
||||
@@ -255,7 +255,7 @@ const Dex = new class implements ModdedDex {
|
||||
if (avatar.charAt(0) === '#') {
|
||||
return Dex.resourcePrefix + 'sprites/trainers-custom/' + toID(avatar.substr(1)) + '.png';
|
||||
}
|
||||
if (avatar.includes('.') && window.Config && Config.server && Config.server.registered) {
|
||||
if (avatar.includes('.') && window.Config?.server?.registered) {
|
||||
// custom avatar served by the server
|
||||
let protocol = (Config.server.port === 443) ? 'https' : 'http';
|
||||
return protocol + '://' + Config.server.host + ':' + Config.server.port +
|
||||
@@ -284,8 +284,7 @@ const Dex = new class implements ModdedDex {
|
||||
|
||||
prefs(prop: string, value?: any, save?: boolean) {
|
||||
// @ts-ignore
|
||||
if (window.Storage && Storage.prefs) return Storage.prefs(prop, value, save);
|
||||
return undefined;
|
||||
return window.Storage?.prefs?.(prop, value, save);
|
||||
}
|
||||
|
||||
getShortName(name: string) {
|
||||
@@ -661,9 +660,9 @@ const Dex = new class implements ModdedDex {
|
||||
|
||||
getPokemonIconNum(id: ID, isFemale?: boolean, facingLeft?: boolean) {
|
||||
let num = 0;
|
||||
if (window.BattlePokemonSprites && BattlePokemonSprites[id] && BattlePokemonSprites[id].num) {
|
||||
if (window.BattlePokemonSprites?.[id]?.num) {
|
||||
num = BattlePokemonSprites[id].num;
|
||||
} else if (window.BattlePokedex && window.BattlePokedex[id] && BattlePokedex[id].num) {
|
||||
} else if (window.BattlePokedex?.[id]?.num) {
|
||||
num = BattlePokedex[id].num;
|
||||
}
|
||||
if (num < 0) num = 0;
|
||||
@@ -688,26 +687,26 @@ const Dex = new class implements ModdedDex {
|
||||
|
||||
getPokemonIcon(pokemon: any, facingLeft?: boolean) {
|
||||
if (pokemon === 'pokeball') {
|
||||
return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -0px 4px';
|
||||
return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -0px 4px`;
|
||||
} else if (pokemon === 'pokeball-statused') {
|
||||
return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -40px 4px';
|
||||
return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -40px 4px`;
|
||||
} else if (pokemon === 'pokeball-fainted') {
|
||||
return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -80px 4px;opacity:.4;filter:contrast(0)';
|
||||
return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -80px 4px;opacity:.4;filter:contrast(0)`;
|
||||
} else if (pokemon === 'pokeball-none') {
|
||||
return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -80px 4px';
|
||||
return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-pokeball-sheet.png) no-repeat scroll -80px 4px`;
|
||||
}
|
||||
|
||||
let id = toID(pokemon);
|
||||
if (pokemon && pokemon.species) id = toID(pokemon.species);
|
||||
if (pokemon && pokemon.volatiles && pokemon.volatiles.formechange && !pokemon.volatiles.transform) {
|
||||
if (pokemon?.species) id = toID(pokemon.species);
|
||||
if (pokemon?.volatiles?.formechange && !pokemon.volatiles.transform) {
|
||||
id = toID(pokemon.volatiles.formechange[1]);
|
||||
}
|
||||
let num = this.getPokemonIconNum(id, pokemon && pokemon.gender === 'F', facingLeft);
|
||||
let num = this.getPokemonIconNum(id, pokemon?.gender === 'F', facingLeft);
|
||||
|
||||
let top = Math.floor(num / 12) * 30;
|
||||
let left = (num % 12) * 40;
|
||||
let fainted = (pokemon && pokemon.fainted ? ';opacity:.3;filter:grayscale(100%) brightness(.5)' : '');
|
||||
return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/pokemonicons-sheet.png?a6) no-repeat scroll -' + left + 'px -' + top + 'px' + fainted;
|
||||
let fainted = (pokemon?.fainted ? `;opacity:.3;filter:grayscale(100%) brightness(.5)` : ``);
|
||||
return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-sheet.png?a6) no-repeat scroll -${left}px -${top}px${fainted}`;
|
||||
}
|
||||
|
||||
getTeambuilderSprite(pokemon: any, gen: number = 0) {
|
||||
@@ -723,7 +722,7 @@ const Dex = new class implements ModdedDex {
|
||||
}
|
||||
let shiny = (pokemon.shiny ? '-shiny' : '');
|
||||
// let sdata;
|
||||
// if (BattlePokemonSprites[id] && BattlePokemonSprites[id].front && !Dex.prefs('bwgfx')) {
|
||||
// if (BattlePokemonSprites[id]?.front && !Dex.prefs('bwgfx')) {
|
||||
// if (BattlePokemonSprites[id].front.anif && pokemon.gender === 'F') {
|
||||
// spriteid += '-f';
|
||||
// sdata = BattlePokemonSprites[id].front.anif;
|
||||
@@ -757,7 +756,7 @@ const Dex = new class implements ModdedDex {
|
||||
getItemIcon(item: any) {
|
||||
let num = 0;
|
||||
if (typeof item === 'string' && exports.BattleItems) item = exports.BattleItems[toID(item)];
|
||||
if (item && item.spritenum) num = item.spritenum;
|
||||
if (item?.spritenum) num = item.spritenum;
|
||||
|
||||
let top = Math.floor(num / 16) * 24;
|
||||
let left = (num % 16) * 24;
|
||||
|
||||
@@ -63,13 +63,13 @@ class BattleLog {
|
||||
this.elem.onscroll = null;
|
||||
}
|
||||
add(args: Args, kwArgs?: KWArgs, preempt?: boolean) {
|
||||
if (kwArgs && kwArgs.silent) return;
|
||||
if (kwArgs?.silent) return;
|
||||
let divClass = 'chat';
|
||||
let divHTML = '';
|
||||
let noNotify: boolean | undefined;
|
||||
switch (args[0]) {
|
||||
case 'chat': case 'c': case 'c:':
|
||||
let battle = this.scene && this.scene.battle;
|
||||
let battle = this.scene?.battle;
|
||||
let name;
|
||||
let message;
|
||||
if (args[0] === 'c:') {
|
||||
@@ -80,12 +80,12 @@ class BattleLog {
|
||||
message = args[2];
|
||||
}
|
||||
let rank = name.charAt(0);
|
||||
if (battle && battle.ignoreSpects && ' +'.includes(rank)) return;
|
||||
if (battle && battle.ignoreOpponent) {
|
||||
if (battle?.ignoreSpects && ' +'.includes(rank)) return;
|
||||
if (battle?.ignoreOpponent) {
|
||||
if ('\u2605\u2606'.includes(rank) && toUserid(name) !== app.user.get('userid')) return;
|
||||
}
|
||||
if (window.app && app.ignore && app.ignore[toUserid(name)] && ' +\u2605\u2606'.includes(rank)) return;
|
||||
let isHighlighted = window.app && app.rooms && app.rooms[battle!.roomid].getHighlight(message);
|
||||
if (window.app?.ignore?.[toUserid(name)] && ' +\u2605\u2606'.includes(rank)) return;
|
||||
let isHighlighted = window.app?.rooms?.[battle!.roomid].getHighlight(message);
|
||||
[divClass, divHTML, noNotify] = this.parseChatMessage(message, name, '', isHighlighted);
|
||||
if (!noNotify && isHighlighted) {
|
||||
let notifyTitle = "Mentioned by " + name + " in " + battle!.roomid;
|
||||
@@ -403,7 +403,7 @@ class BattleLog {
|
||||
static usernameColor(name: ID) {
|
||||
if (this.colorCache[name]) return this.colorCache[name];
|
||||
let hash;
|
||||
if (window.Config && Config.customcolors && Config.customcolors[name]) {
|
||||
if (window.Config?.customcolors?.[name]) {
|
||||
hash = MD5(Config.customcolors[name]);
|
||||
} else {
|
||||
hash = MD5(name);
|
||||
@@ -460,7 +460,7 @@ class BattleLog {
|
||||
|
||||
static prefs(name: string) {
|
||||
// @ts-ignore
|
||||
if (window.Storage && Storage.prefs) return Storage.prefs(name);
|
||||
if (window.Storage?.prefs) return Storage.prefs(name);
|
||||
// @ts-ignore
|
||||
if (window.PS) return PS.prefs[name];
|
||||
return undefined;
|
||||
@@ -469,7 +469,7 @@ class BattleLog {
|
||||
parseChatMessage(
|
||||
message: string, name: string, timestamp: string, isHighlighted?: boolean
|
||||
): [string, string, boolean?] {
|
||||
let showMe = !(BattleLog.prefs('chatformatting') || {}).hideme;
|
||||
let showMe = !BattleLog.prefs('chatformatting')?.hideme;
|
||||
let group = ' ';
|
||||
if (!/[A-Za-z0-9]/.test(name.charAt(0))) {
|
||||
// Backwards compatibility
|
||||
@@ -479,8 +479,7 @@ class BattleLog {
|
||||
const colorStyle = ` style="color:${BattleLog.usernameColor(toID(name))}"`;
|
||||
const clickableName = `<small>${BattleLog.escapeHTML(group)}</small><span class="username" data-name="${BattleLog.escapeHTML(name)}">${BattleLog.escapeHTML(name)}</span>`;
|
||||
let hlClass = isHighlighted ? ' highlighted' : '';
|
||||
let isMine = (window.app && app.user && app.user.get('name') === name) ||
|
||||
(window.PS && PS.user.name === name);
|
||||
let isMine = (window.app?.user?.get('name') === name) || (window.PS?.user.name === name);
|
||||
let mineClass = isMine ? ' mine' : '';
|
||||
|
||||
let cmd = '';
|
||||
@@ -589,7 +588,7 @@ class BattleLog {
|
||||
}
|
||||
|
||||
static interstice = (() => {
|
||||
const whitelist: string[] = (window.Config && Config.whitelist) ? Config.whitelist : [];
|
||||
const whitelist: string[] = window.Config?.whitelist || [];
|
||||
const patterns = whitelist.map(entry => new RegExp(
|
||||
`^(https?:)?//([A-Za-z0-9-]*\\.)?${entry}(/.*)?`,
|
||||
'i'));
|
||||
@@ -610,7 +609,7 @@ class BattleLog {
|
||||
};
|
||||
})();
|
||||
|
||||
static tagPolicy: (tagName: string, attribs: string[]) => any = null!;
|
||||
static tagPolicy: ((tagName: string, attribs: string[]) => any) | null = null;
|
||||
static initSanitizeHTML() {
|
||||
if (this.tagPolicy) return;
|
||||
if (!('html4' in window)) {
|
||||
@@ -755,7 +754,7 @@ class BattleLog {
|
||||
|
||||
let formattedTime;
|
||||
// Try using Intl API if it exists
|
||||
if (window.Intl && Intl.DateTimeFormat) {
|
||||
if (window.Intl?.DateTimeFormat) {
|
||||
formattedTime = new Intl.DateTimeFormat(undefined, {
|
||||
month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric',
|
||||
}).format(parsedTime);
|
||||
|
||||
@@ -732,7 +732,7 @@ class BattleSearch {
|
||||
let uselessMoves: SearchRow[] = [];
|
||||
let sketchedMoves: SearchRow[] = [];
|
||||
for (const id of moves) {
|
||||
let isViable: boolean = BattleMovedex[id] && BattleMovedex[id].isViable;
|
||||
let isViable: boolean = BattleMovedex[id]?.isViable;
|
||||
if (id === 'aerialace') isViable = ['scyther', 'aerodactylmega', 'kricketune'].includes(toID(set.species));
|
||||
if (id === 'ancientpower') {
|
||||
isViable = (
|
||||
@@ -813,7 +813,7 @@ class BattleSearch {
|
||||
'acidarmor', 'amnesia', 'barrier', 'bind', 'clamp', 'confuseray', 'counter', 'firespin',
|
||||
'hyperbeam', 'mirrormove', 'pinmissile', 'razorleaf', 'sing', 'slash', 'sludge',
|
||||
'twineedle', 'wrap',
|
||||
].includes) {
|
||||
].includes(id)) {
|
||||
isViable = true;
|
||||
}
|
||||
|
||||
@@ -904,7 +904,7 @@ class BattleSearch {
|
||||
let poorAbilities: SearchRow[] = [['header', "Situational Abilities"]];
|
||||
let badAbilities: SearchRow[] = [['header', "Unviable Abilities"]];
|
||||
for (const id of abilities) {
|
||||
let rating = BattleAbilities[id] && BattleAbilities[id].rating;
|
||||
let rating = BattleAbilities[id]?.rating;
|
||||
if (id === 'normalize') rating = 3;
|
||||
if (rating >= 3) {
|
||||
goodAbilities.push(['ability', id]);
|
||||
|
||||
@@ -521,7 +521,7 @@ class BattleTextParser {
|
||||
if (kwArgs.block) templateId = 'block';
|
||||
if (kwArgs.upkeep) templateId = 'upkeep';
|
||||
if (id === 'reflect' || id === 'lightscreen') templateId = 'startGen1';
|
||||
if (templateId === 'start' && kwArgs.from && kwArgs.from.startsWith('item:')) {
|
||||
if (templateId === 'start' && kwArgs.from?.startsWith('item:')) {
|
||||
templateId += 'FromItem';
|
||||
}
|
||||
const template = this.template(templateId, effect);
|
||||
@@ -538,7 +538,7 @@ class BattleTextParser {
|
||||
}
|
||||
let templateId = 'end';
|
||||
let template = '';
|
||||
if (kwArgs.from && kwArgs.from.startsWith('item:')) {
|
||||
if (kwArgs.from?.startsWith('item:')) {
|
||||
template = this.template('endFromItem', effect);
|
||||
}
|
||||
if (!template) template = this.template(templateId, effect);
|
||||
@@ -654,7 +654,7 @@ class BattleTextParser {
|
||||
return template.replace('[POKEMON]', this.pokemon(pokemon));
|
||||
}
|
||||
const line1 = this.maybeAbility(kwArgs.from, kwArgs.of || pokemon);
|
||||
if (kwArgs.from && kwArgs.from.startsWith('item:')) {
|
||||
if (kwArgs.from?.startsWith('item:')) {
|
||||
const template = this.template('endFromItem', status);
|
||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(kwArgs.from));
|
||||
}
|
||||
@@ -859,7 +859,7 @@ class BattleTextParser {
|
||||
else if (amount === 0) templateId += '0';
|
||||
if (amount && kwArgs.zeffect) {
|
||||
templateId += (kwArgs.multiple ? 'MultipleFromZEffect' : 'FromZEffect');
|
||||
} else if (amount && kwArgs.from && kwArgs.from.startsWith('item:')) {
|
||||
} else if (amount && kwArgs.from?.startsWith('item:')) {
|
||||
templateId += 'FromItem';
|
||||
}
|
||||
const template = this.template(templateId, kwArgs.from);
|
||||
|
||||
@@ -26,7 +26,7 @@ class ModifiableValue {
|
||||
this.serverPokemon = serverPokemon;
|
||||
|
||||
this.itemName = Dex.getItem(serverPokemon.item).name;
|
||||
const ability = serverPokemon.ability || (pokemon && pokemon.ability) || serverPokemon.baseAbility;
|
||||
const ability = serverPokemon.ability || pokemon?.ability || serverPokemon.baseAbility;
|
||||
this.abilityName = Dex.getAbility(ability).name;
|
||||
this.weatherName = Dex.getMove(battle.weather).exists ?
|
||||
Dex.getMove(battle.weather).name : Dex.getAbility(battle.weather).name;
|
||||
@@ -43,7 +43,7 @@ class ModifiableValue {
|
||||
this.comment.push(` (${itemName} suppressed by Magic Room)`);
|
||||
return false;
|
||||
}
|
||||
if (this.pokemon && this.pokemon.volatiles['embargo']) {
|
||||
if (this.pokemon?.volatiles['embargo']) {
|
||||
this.comment.push(` (${itemName} suppressed by Embargo)`);
|
||||
return false;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ class ModifiableValue {
|
||||
}
|
||||
tryAbility(abilityName: string) {
|
||||
if (abilityName !== this.abilityName) return false;
|
||||
if (this.pokemon && this.pokemon.volatiles['gastroacid']) {
|
||||
if (this.pokemon?.volatiles['gastroacid']) {
|
||||
this.comment.push(` (${abilityName} suppressed by Gastro Acid)`);
|
||||
return false;
|
||||
}
|
||||
@@ -620,7 +620,7 @@ class BattleTooltips {
|
||||
|
||||
text += '<h2>' + name + genderBuf + (pokemon.level !== 100 ? ' <small>L' + pokemon.level + '</small>' : '') + '<br />';
|
||||
|
||||
if (clientPokemon && clientPokemon.volatiles.formechange) {
|
||||
if (clientPokemon?.volatiles.formechange) {
|
||||
if (clientPokemon.volatiles.transform) {
|
||||
text += '<small>(Transformed into ' + clientPokemon.volatiles.formechange[1] + ')</small><br />';
|
||||
} else {
|
||||
@@ -672,7 +672,7 @@ class BattleTooltips {
|
||||
}
|
||||
|
||||
let itemText = '';
|
||||
if (serverPokemon && serverPokemon.item) {
|
||||
if (serverPokemon?.item) {
|
||||
itemText = '<small>Item:</small> ' + Dex.getItem(serverPokemon.item).name;
|
||||
} else if (clientPokemon) {
|
||||
let item = '';
|
||||
@@ -706,7 +706,7 @@ class BattleTooltips {
|
||||
for (const moveid of serverPokemon.moves) {
|
||||
let move = Dex.getMove(moveid);
|
||||
let moveName = '• ' + move.name;
|
||||
if (battlePokemon && battlePokemon.moveTrack) {
|
||||
if (battlePokemon?.moveTrack) {
|
||||
for (const row of battlePokemon.moveTrack) {
|
||||
if (moveName === row[0]) {
|
||||
moveName = this.getPPUseText(row, true);
|
||||
@@ -717,7 +717,7 @@ class BattleTooltips {
|
||||
text += moveName + '<br />';
|
||||
}
|
||||
text += '</p>';
|
||||
} else if (!this.battle.hardcoreMode && clientPokemon && clientPokemon.moveTrack.length) {
|
||||
} else if (!this.battle.hardcoreMode && clientPokemon?.moveTrack.length) {
|
||||
// move list (guessed)
|
||||
text += '<p class="section">';
|
||||
for (const row of clientPokemon.moveTrack) {
|
||||
@@ -739,7 +739,7 @@ class BattleTooltips {
|
||||
calculateModifiedStats(clientPokemon: Pokemon | null, serverPokemon: ServerPokemon) {
|
||||
let stats = {...serverPokemon.stats};
|
||||
let pokemon = clientPokemon || serverPokemon;
|
||||
const isPowerTrick = clientPokemon && clientPokemon.volatiles['powertrick'];
|
||||
const isPowerTrick = clientPokemon?.volatiles['powertrick'];
|
||||
for (const statName of Dex.statNamesExceptHP) {
|
||||
let sourceStatName = statName;
|
||||
if (isPowerTrick) {
|
||||
@@ -857,7 +857,7 @@ class BattleTooltips {
|
||||
if (ability === 'solarpower') {
|
||||
stats.spa = Math.floor(stats.spa * 1.5);
|
||||
}
|
||||
let allyActive = clientPokemon && clientPokemon.side.active;
|
||||
let allyActive = clientPokemon?.side.active;
|
||||
if (allyActive) {
|
||||
for (const ally of allyActive) {
|
||||
if (!ally || ally.fainted) continue;
|
||||
@@ -955,7 +955,7 @@ class BattleTooltips {
|
||||
}
|
||||
|
||||
renderStats(clientPokemon: Pokemon | null, serverPokemon?: ServerPokemon | null, short?: boolean) {
|
||||
const isTransformed = clientPokemon && clientPokemon.volatiles.transform;
|
||||
const isTransformed = clientPokemon?.volatiles.transform;
|
||||
if (!serverPokemon || isTransformed) {
|
||||
if (!clientPokemon) throw new Error('Must pass either clientPokemon or serverPokemon');
|
||||
let [min, max] = this.getSpeedRange(clientPokemon);
|
||||
@@ -1164,7 +1164,7 @@ class BattleTooltips {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
if (pokemon && pokemon.boosts.accuracy) {
|
||||
if (pokemon?.boosts.accuracy) {
|
||||
if (pokemon.boosts.accuracy > 0) {
|
||||
value.modify((pokemon.boosts.accuracy + 3) / 3);
|
||||
} else {
|
||||
@@ -1238,7 +1238,7 @@ class BattleTooltips {
|
||||
else basePower = 20;
|
||||
value.set(basePower);
|
||||
}
|
||||
if (move.id === 'hex' && target && target.status) {
|
||||
if (move.id === 'hex' && target?.status) {
|
||||
value.modify(2, 'Hex + status');
|
||||
}
|
||||
if (move.id === 'punishment' && target) {
|
||||
@@ -1582,7 +1582,7 @@ class BattleTooltips {
|
||||
abilityData.baseAbility = clientPokemon.baseAbility;
|
||||
}
|
||||
} else {
|
||||
const species = clientPokemon.getSpecies() || (serverPokemon && serverPokemon.species) || '';
|
||||
const species = clientPokemon.getSpecies() || serverPokemon?.species || '';
|
||||
const template = this.battle.dex.getTemplate(species);
|
||||
if (template.exists && template.abilities) {
|
||||
abilityData.possibilities = [template.abilities['0']];
|
||||
@@ -2242,9 +2242,9 @@ class BattleStatGuesser {
|
||||
}
|
||||
if (natureOverride) {
|
||||
val *= natureOverride;
|
||||
} else if (BattleNatures[set.nature!] && BattleNatures[set.nature!].plus === stat) {
|
||||
} else if (BattleNatures[set.nature!]?.plus === stat) {
|
||||
val *= 1.1;
|
||||
} else if (BattleNatures[set.nature!] && BattleNatures[set.nature!].minus === stat) {
|
||||
} else if (BattleNatures[set.nature!]?.minus === stat) {
|
||||
val *= 0.9;
|
||||
}
|
||||
if (!this.supportsEVs) {
|
||||
|
||||
@@ -392,7 +392,7 @@ class Pokemon implements PokemonDetails, PokemonHealth {
|
||||
return '' + badBoostTable[-this.boosts[boostStat]] + ' ' + boostStatTable[boostStat];
|
||||
}
|
||||
getWeightKg(serverPokemon?: ServerPokemon) {
|
||||
let autotomizeFactor = this.volatiles.autotomize ? this.volatiles.autotomize[1] * 100 : 0;
|
||||
let autotomizeFactor = this.volatiles.autotomize?.[1] * 100 || 0;
|
||||
return Math.max(this.getTemplate(serverPokemon).weightkg - autotomizeFactor, 0.1);
|
||||
}
|
||||
getBoostType(boostStat: BoostStatName) {
|
||||
@@ -486,7 +486,7 @@ class Pokemon implements PokemonDetails, PokemonHealth {
|
||||
}
|
||||
|
||||
let item = toID(serverPokemon ? serverPokemon.item : this.item);
|
||||
let ability = toID(this.ability || (serverPokemon && serverPokemon.ability));
|
||||
let ability = toID(this.ability || serverPokemon?.ability);
|
||||
if (battle.hasPseudoWeather('Magic Room') || this.volatiles['embargo'] || ability === 'klutz') {
|
||||
item = '' as ID;
|
||||
}
|
||||
@@ -779,8 +779,8 @@ class Side {
|
||||
pokemon.clearVolatile();
|
||||
pokemon.lastMove = '';
|
||||
this.battle.lastMove = 'switch-in';
|
||||
if (this.lastPokemon && (this.lastPokemon.lastMove === 'batonpass' || this.lastPokemon.lastMove === 'zbatonpass')) {
|
||||
pokemon.copyVolatileFrom(this.lastPokemon);
|
||||
if (['batonpass', 'zbatonpass'].includes(this.lastPokemon?.lastMove!)) {
|
||||
pokemon.copyVolatileFrom(this.lastPokemon!);
|
||||
}
|
||||
|
||||
this.battle.scene.animSummon(pokemon, slot);
|
||||
@@ -1271,7 +1271,7 @@ class Battle {
|
||||
updateToxicTurns() {
|
||||
for (const side of this.sides) {
|
||||
for (const poke of side.active) {
|
||||
if (poke && poke.status === 'tox') poke.statusData.toxicTurns++;
|
||||
if (poke?.status === 'tox') poke.statusData.toxicTurns++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1783,7 +1783,7 @@ class Battle {
|
||||
let poke = this.getPokemon(args[1]);
|
||||
if (poke) {
|
||||
this.scene.resultAnim(poke, 'Super-effective', 'bad');
|
||||
if (window.Config && Config.server && Config.server.afd) {
|
||||
if (window.Config?.server?.afd) {
|
||||
this.scene.runOtherAnim('hitmark' as ID, [poke]);
|
||||
}
|
||||
}
|
||||
@@ -2969,7 +2969,7 @@ class Battle {
|
||||
// search p1's pokemon
|
||||
if (siden !== this.p2.n && !isNew) {
|
||||
const active = this.p1.active[slot];
|
||||
if (active && active.searchid === searchid && !isSwitch) {
|
||||
if (active?.searchid === searchid && !isSwitch) {
|
||||
active.slot = slot;
|
||||
return active;
|
||||
}
|
||||
@@ -2996,7 +2996,7 @@ class Battle {
|
||||
// search p2's pokemon
|
||||
if (siden !== this.p1.n && !isNew) {
|
||||
const active = this.p2.active[slot];
|
||||
if (active && active.searchid === searchid && !isSwitch) {
|
||||
if (active?.searchid === searchid && !isSwitch) {
|
||||
if (slot >= 0) active.slot = slot;
|
||||
return active;
|
||||
}
|
||||
@@ -3188,7 +3188,7 @@ class Battle {
|
||||
return;
|
||||
} else if (args[1].slice(-14) === ' seconds left.') {
|
||||
let hasIndex = args[1].indexOf(' has ');
|
||||
let userid = (window.app && app!.user && app!.user.get('userid'));
|
||||
let userid = window.app?.user?.get('userid');
|
||||
if (toID(args[1].slice(0, hasIndex)) === userid) {
|
||||
this.kickingInactive = parseInt(args[1].slice(hasIndex + 5), 10) || true;
|
||||
}
|
||||
@@ -3407,7 +3407,7 @@ class Battle {
|
||||
let nextArgs: Args = [''];
|
||||
let nextKwargs: KWArgs = {};
|
||||
const nextLine = this.activityQueue[this.activityStep + 1] || '';
|
||||
if (nextLine && nextLine.substr(0, 2) === '|-') {
|
||||
if (nextLine.slice(0, 2) === '|-') {
|
||||
({args: nextArgs, kwArgs: nextKwargs} = BattleTextParser.parseLine(nextLine));
|
||||
}
|
||||
|
||||
|
||||
@@ -159,10 +159,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||
const formatid = toID(format);
|
||||
if (!window.BattleFormats) return formatid;
|
||||
const formatEntry = BattleFormats[formatid];
|
||||
if (formatEntry && formatEntry.teambuilderFormat) {
|
||||
return formatEntry.teambuilderFormat;
|
||||
}
|
||||
return formatid;
|
||||
return formatEntry?.teambuilderFormat || formatid;
|
||||
}
|
||||
getKey(team: Team | null) {
|
||||
if (!team) return '';
|
||||
@@ -209,7 +206,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||
let slashIndex = line.lastIndexOf('/', pipeIndex);
|
||||
if (slashIndex < 0) slashIndex = bracketIndex; // line.slice(slashIndex + 1, pipeIndex) will be ''
|
||||
let format = bracketIndex > 0 ? line.slice(0, bracketIndex) : 'gen7';
|
||||
if (format && format.slice(0, 3) !== 'gen') format = 'gen6' + format;
|
||||
if (format.slice(0, 3) !== 'gen') format = 'gen6' + format;
|
||||
return {
|
||||
name: line.slice(slashIndex + 1, pipeIndex),
|
||||
format: format as ID,
|
||||
@@ -786,7 +783,7 @@ const PS = new class extends PSModel {
|
||||
}
|
||||
|
||||
const roomType = this.roomTypes[options.type];
|
||||
if (roomType && roomType.title) options.title = roomType.title;
|
||||
if (roomType?.title) options.title = roomType.title;
|
||||
const Model = roomType ? roomType.Model : PlaceholderRoom;
|
||||
return new Model(options);
|
||||
}
|
||||
|
||||
@@ -73,11 +73,11 @@ class MainMenuRoom extends PSRoom {
|
||||
if (!room.pmTarget) continue;
|
||||
const targetUserid = toID(room.pmTarget);
|
||||
if (!room.challengedFormat && !(targetUserid in json.challengesFrom) &&
|
||||
!room.challengingFormat && (json.challengeTo || {}).to !== targetUserid) {
|
||||
!room.challengingFormat && json.challengeTo?.to !== targetUserid) {
|
||||
continue;
|
||||
}
|
||||
room.challengedFormat = json.challengesFrom[targetUserid] || null;
|
||||
room.challengingFormat = (json.challengeTo || {}).to === targetUserid ? json.challengeTo.format : null;
|
||||
room.challengingFormat = json.challengeTo?.to === targetUserid ? json.challengeTo.format : null;
|
||||
room.update('');
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ class MainMenuRoom extends PSRoom {
|
||||
isTeambuilderFormat = false;
|
||||
}
|
||||
}
|
||||
if (BattleFormats[id] && BattleFormats[id].isTeambuilderFormat) {
|
||||
if (BattleFormats[id]?.isTeambuilderFormat) {
|
||||
isTeambuilderFormat = true;
|
||||
}
|
||||
// make sure formats aren't out-of-order
|
||||
@@ -332,10 +332,7 @@ class MainMenuPanel extends PSRoomPanel {
|
||||
class FormatDropdown extends preact.Component<{format?: string, onChange?: JSX.EventHandler<Event>}> {
|
||||
base?: HTMLButtonElement;
|
||||
getFormat() {
|
||||
if (this.base && this.base.value) {
|
||||
return this.base.value;
|
||||
}
|
||||
return '[Gen 7] Random Battle';
|
||||
return this.base?.value || '[Gen 7] Random Battle';
|
||||
}
|
||||
componentDidMount() {
|
||||
this.base!.value = this.getFormat();
|
||||
@@ -379,7 +376,7 @@ class TeamDropdown extends preact.Component<{format: string}> {
|
||||
change = () => this.forceUpdate();
|
||||
render() {
|
||||
const formatid = PS.teams.teambuilderFormat(this.props.format);
|
||||
const formatData = window.BattleFormats && BattleFormats[formatid];
|
||||
const formatData = window.BattleFormats?.[formatid];
|
||||
if (formatData && formatData.team) {
|
||||
return <button class="select teamselect preselected" name="team" value="random" disabled>
|
||||
<div class="team">
|
||||
|
||||
@@ -46,15 +46,15 @@ class PSHeader extends preact.Component<{style: {}}> {
|
||||
formatid = idChunks[idChunks.length - 2];
|
||||
}
|
||||
if (!title) {
|
||||
let battle = (room as any).battle;
|
||||
let p1 = (battle && battle.p1 && battle.p1.name) || '';
|
||||
let p2 = (battle && battle.p2 && battle.p2.name) || '';
|
||||
let battle = (room as any).battle as Battle | undefined;
|
||||
let p1 = battle?.p1?.name || '';
|
||||
let p2 = battle?.p2?.name || '';
|
||||
if (p1 && p2) {
|
||||
title = '' + p1 + ' v. ' + p2;
|
||||
title = `${p1} v. ${p2}`;
|
||||
} else if (p1 || p2) {
|
||||
title = '' + p1 + p2;
|
||||
title = `${p1}${p2}`;
|
||||
} else {
|
||||
title = '(empty room)';
|
||||
title = `(empty room)`;
|
||||
}
|
||||
}
|
||||
icon = <i class="text">{formatid}</i>;
|
||||
|
||||
@@ -165,7 +165,7 @@ class PSMain extends preact.Component {
|
||||
|
||||
window.addEventListener('click', e => {
|
||||
let elem = e.target as HTMLElement | null;
|
||||
if (elem && elem.className === 'ps-overlay') {
|
||||
if (elem?.className === 'ps-overlay') {
|
||||
PS.closePopup();
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
Reference in New Issue
Block a user