mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
Update to ESLint 9 (#2326)
Some checks are pending
Node.js CI / build (22.x) (push) Waiting to run
Some checks are pending
Node.js CI / build (22.x) (push) Waiting to run
This finally removes the tslint dependency and switches to eslint. There are a lot of other changes here, too, to bring the codebase up to server standards. TSLint never had much in the way of indentation enforcement. Not very happy about eslint splitting itself up over 6 dependencies, or its documentation over three websites, nor how poorly documented the new flat config is, but I mean, eslint's gonna eslint. Customizing would be even harder if we tried to use Biome or something. They mostly seem to go full Prettier. Also here are some changes to our style rules. In particular: - Curly brackets (for objects etc) now have spaces inside them. Sorry for the huge change. ESLint doesn't support our old style, and most projects use Prettier style, so we might as well match them in this way. See https://github.com/eslint-stylistic/eslint-stylistic/issues/415 - String + number concatenation is no longer allowed (except in ES3 code). We otherwise now consistently use template strings for this.
This commit is contained in:
parent
3d4de88706
commit
a10821ab8b
|
|
@ -1,16 +0,0 @@
|
||||||
node_modules/
|
|
||||||
/play.pokemonshowdown.com/data/*
|
|
||||||
/play.pokemonshowdown.com/js/*
|
|
||||||
|
|
||||||
!/play.pokemonshowdown.com/js/client-battle.js
|
|
||||||
!/play.pokemonshowdown.com/js/client-chat-tournament.js
|
|
||||||
!/play.pokemonshowdown.com/js/client-chat.js
|
|
||||||
!/play.pokemonshowdown.com/js/client-ladder.js
|
|
||||||
!/play.pokemonshowdown.com/js/client-mainmenu.js
|
|
||||||
!/play.pokemonshowdown.com/js/client-rooms.js
|
|
||||||
!/play.pokemonshowdown.com/js/client-teambuilder.js
|
|
||||||
!/play.pokemonshowdown.com/js/client-topbar.js
|
|
||||||
!/play.pokemonshowdown.com/js/client.js
|
|
||||||
!/play.pokemonshowdown.com/js/replay-embed.template.js
|
|
||||||
!/play.pokemonshowdown.com/js/search.js
|
|
||||||
!/play.pokemonshowdown.com/js/storage.js
|
|
||||||
210
.eslintrc.js
210
.eslintrc.js
|
|
@ -1,210 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
"root": true,
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 3,
|
|
||||||
"sourceType": "script"
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"browser": true
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
// Libraries
|
|
||||||
"_": false, "$": false, "Backbone": false, "d3": false, "html": false, "html4": false, "jQuery": false, "SockJS": false, "ColorThief": false,
|
|
||||||
|
|
||||||
// Environment-specific
|
|
||||||
"fs": false, "gui": false, "ga": false, "macgap": false, "nwWindow": false, "webkitNotifications": false, "nw": false,
|
|
||||||
|
|
||||||
// Battle stuff
|
|
||||||
"Battle": true, "Pokemon": true, "BattleSound": true, "BattleTooltips": true, "BattleLog": true,
|
|
||||||
"BattleAbilities": false, "BattleAliases": false, "BattleBackdrops": false, "BattleBackdropsFive": false, "BattleBackdropsFour": false, "BattleBackdropsThree": false, "BattleEffects": false,
|
|
||||||
"BattleFormats": false, "BattleFormatsData": false, "BattleLearnsets": false, "BattleItems": false, "BattleMoveAnims": false, "BattleMovedex": false, "BattleNatures": false,
|
|
||||||
"BattleOtherAnims": false, "BattlePokedex": false,"BattlePokemonSprites": false, "BattlePokemonSpritesBW": false, "BattleSearchCountIndex": false, "BattleSearchIndex": false, "BattleArticleTitles": false,
|
|
||||||
"BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStatusAnims": false, "BattleStatuses": false, "BattleTeambuilderTable": false,
|
|
||||||
"ModifiableValue": false, "BattleStatGuesser": false, "BattleStatOptimizer": false, "BattleText": true, "BattleTextAFD": false, "BattleTextNotAFD": false,
|
|
||||||
"BattleTextParser": false,
|
|
||||||
|
|
||||||
// Generic global variables
|
|
||||||
"Config": false, "BattleSearch": false, "Storage": false, "Dex": false, "DexSearch": false,
|
|
||||||
"app": false, "toID": false, "toRoomid": false, "toUserid": false, "toName": false, "PSUtils": false, "MD5": false,
|
|
||||||
"ChatHistory": false, "Topbar": false, "UserList": false,
|
|
||||||
|
|
||||||
// Rooms
|
|
||||||
"Room": false, "BattleRoom": false, "ChatRoom": false, "ConsoleRoom": false, "HTMLRoom": false, "LadderRoom": false, "MainMenuRoom": false, "RoomsRoom": false, "BattlesRoom": false, "TeambuilderRoom": false,
|
|
||||||
|
|
||||||
// Tons of popups
|
|
||||||
"Popup": false, "ForfeitPopup": false, "BracketPopup": false, "LoginPasswordPopup": false, "UserPopup": false, "UserOptionsPopup": false, "UserOptions": false, "TeamPopup": false,
|
|
||||||
"AvatarsPopup": false, "CreditsPopup": false, "FormatPopup": false, "FormattingPopup": false, "LoginPopup": false,
|
|
||||||
"MovePopup": false, "SoundsPopup": false, "OptionsPopup": false, "PromptPopup": false, "ProxyPopup": false, "ReconnectPopup": false,
|
|
||||||
"RegisterPopup": false, "ReplayUploadedPopup": false, "RulesPopup": false, "TabListPopup": false, "TournamentBox": false,
|
|
||||||
"CustomBackgroundPopup": false,
|
|
||||||
|
|
||||||
// Test client
|
|
||||||
"POKEMON_SHOWDOWN_TESTCLIENT_KEY": false
|
|
||||||
},
|
|
||||||
"extends": "eslint:recommended",
|
|
||||||
"rules": {
|
|
||||||
"no-cond-assign": ["error", "except-parens"],
|
|
||||||
"no-console": "off",
|
|
||||||
"no-constant-condition": "off",
|
|
||||||
"no-control-regex": "off",
|
|
||||||
"no-empty": ["error", {"allowEmptyCatch": true}],
|
|
||||||
"no-inner-declarations": ["error", "functions"],
|
|
||||||
"no-redeclare": "off",
|
|
||||||
"valid-jsdoc": "off",
|
|
||||||
|
|
||||||
// TODO: actually fix useless escapes
|
|
||||||
"no-useless-escape": "off",
|
|
||||||
|
|
||||||
"array-callback-return": "error",
|
|
||||||
"complexity": "off",
|
|
||||||
"consistent-return": "off",
|
|
||||||
"default-case": "off",
|
|
||||||
"dot-location": ["error", "property"],
|
|
||||||
"dot-notation": "off",
|
|
||||||
"eqeqeq": "off",
|
|
||||||
"guard-for-in": "off",
|
|
||||||
"no-caller": "error",
|
|
||||||
"no-case-declarations": "off",
|
|
||||||
"no-div-regex": "error",
|
|
||||||
"no-else-return": "off",
|
|
||||||
"no-labels": ["error", {"allowLoop": true, "allowSwitch": true}],
|
|
||||||
"no-eval": "off",
|
|
||||||
"no-implied-eval": "error",
|
|
||||||
"no-extend-native": "error",
|
|
||||||
"no-extra-bind": "warn",
|
|
||||||
"no-extra-label": "error",
|
|
||||||
"no-extra-parens": "off",
|
|
||||||
"no-implicit-coercion": "off",
|
|
||||||
"no-invalid-this": "off",
|
|
||||||
"no-iterator": "error",
|
|
||||||
"no-lone-blocks": "off",
|
|
||||||
"no-loop-func": "off",
|
|
||||||
"no-magic-numbers": "off",
|
|
||||||
"no-multi-spaces": "warn",
|
|
||||||
"no-multi-str": "error",
|
|
||||||
"no-new-func": "error",
|
|
||||||
"no-new-wrappers": "error",
|
|
||||||
"no-octal-escape": "error",
|
|
||||||
"no-param-reassign": "off",
|
|
||||||
"no-proto": "error",
|
|
||||||
"no-prototype-builtins": "error",
|
|
||||||
"no-return-assign": ["error", "except-parens"],
|
|
||||||
"no-self-compare": "error",
|
|
||||||
"no-sequences": "error",
|
|
||||||
"no-throw-literal": "error",
|
|
||||||
"no-unmodified-loop-condition": "error",
|
|
||||||
"no-unused-expressions": "error",
|
|
||||||
"no-useless-call": "error",
|
|
||||||
"no-useless-concat": "off",
|
|
||||||
"no-void": "off",
|
|
||||||
"no-warning-comments": "off",
|
|
||||||
"no-with": "error",
|
|
||||||
"radix": ["error", "always"],
|
|
||||||
"vars-on-top": "off",
|
|
||||||
"wrap-iife": ["error", "inside"],
|
|
||||||
"yoda": "off",
|
|
||||||
|
|
||||||
"init-declarations": "off",
|
|
||||||
"no-catch-shadow": "off",
|
|
||||||
"no-label-var": "error",
|
|
||||||
"no-restricted-globals": ["error", "Proxy", "Reflect", "Symbol", "WeakSet"],
|
|
||||||
"no-shadow-restricted-names": "error",
|
|
||||||
"no-shadow": "off",
|
|
||||||
"no-undef-init": "off",
|
|
||||||
"no-undef": ["error", {"typeof": true}],
|
|
||||||
"no-undefined": "off",
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
|
|
||||||
"no-mixed-requires": "error",
|
|
||||||
"no-new-require": "error",
|
|
||||||
"no-path-concat": "off",
|
|
||||||
"no-process-env": "off",
|
|
||||||
"no-process-exit": "off",
|
|
||||||
"no-restricted-modules": ["error", "moment", "request", "sugar"],
|
|
||||||
"no-sync": "off",
|
|
||||||
|
|
||||||
"array-bracket-spacing": ["error", "never"],
|
|
||||||
"block-spacing": "off",
|
|
||||||
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
|
|
||||||
"camelcase": "off",
|
|
||||||
"comma-spacing": ["error", {"before": false, "after": true}],
|
|
||||||
"comma-style": ["error", "last"],
|
|
||||||
"computed-property-spacing": ["error", "never"],
|
|
||||||
"consistent-this": "off",
|
|
||||||
"func-names": "off",
|
|
||||||
"func-style": "off",
|
|
||||||
"id-length": "off",
|
|
||||||
"id-match": "off",
|
|
||||||
"indent": ["error", "tab"],
|
|
||||||
"key-spacing": "off",
|
|
||||||
"lines-around-comment": "off",
|
|
||||||
"max-nested-callbacks": "off",
|
|
||||||
"max-statements-per-line": "off",
|
|
||||||
"new-parens": "error",
|
|
||||||
"newline-after-var": "off",
|
|
||||||
"newline-before-return": "off",
|
|
||||||
"no-array-constructor": "error",
|
|
||||||
"no-continue": "off",
|
|
||||||
"no-inline-comments": "off",
|
|
||||||
"no-lonely-if": "off",
|
|
||||||
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
|
|
||||||
"no-multiple-empty-lines": ["error", {"max": 2, "maxEOF": 1}],
|
|
||||||
"no-negated-condition": "off",
|
|
||||||
"no-nested-ternary": "off",
|
|
||||||
"no-new-object": "error",
|
|
||||||
"no-spaced-func": "error",
|
|
||||||
"no-ternary": "off",
|
|
||||||
"no-trailing-spaces": ["error", {"ignoreComments": false}],
|
|
||||||
"no-underscore-dangle": "off",
|
|
||||||
"no-unneeded-ternary": "error",
|
|
||||||
"object-curly-spacing": ["error", "never"],
|
|
||||||
"one-var": "off",
|
|
||||||
"operator-assignment": "off",
|
|
||||||
"operator-linebreak": ["error", "after"],
|
|
||||||
"quote-props": "off",
|
|
||||||
"quotes": "off",
|
|
||||||
"require-jsdoc": "off",
|
|
||||||
"semi-spacing": ["error", {"before": false, "after": true}],
|
|
||||||
"semi": ["error", "always"],
|
|
||||||
"sort-vars": "off",
|
|
||||||
"keyword-spacing": ["error", {"before": true, "after": true}],
|
|
||||||
"space-before-blocks": ["error", "always"],
|
|
||||||
"space-before-function-paren": ["error", {"anonymous": "always", "named": "never"}],
|
|
||||||
"space-in-parens": ["error", "never"],
|
|
||||||
"space-infix-ops": "error",
|
|
||||||
"space-unary-ops": ["error", {"words": true, "nonwords": false}],
|
|
||||||
"wrap-regex": "off",
|
|
||||||
|
|
||||||
"arrow-parens": ["error", "as-needed"],
|
|
||||||
"arrow-spacing": ["error", {"before": true, "after": true}],
|
|
||||||
"no-confusing-arrow": "off",
|
|
||||||
"no-useless-computed-key": "error",
|
|
||||||
"no-useless-rename": "error",
|
|
||||||
"prefer-arrow-callback": "off",
|
|
||||||
"rest-spread-spacing": ["error", "never"],
|
|
||||||
"template-curly-spacing": ["error", "never"],
|
|
||||||
"no-restricted-syntax": ["error", "TaggedTemplateExpression", "ObjectPattern", "ArrayPattern"],
|
|
||||||
|
|
||||||
// Rules enabled in the server code, but disabled here
|
|
||||||
/*
|
|
||||||
"block-scoped-var": "error",
|
|
||||||
"callback-return": [2, ["callback", "cb", "done"]],
|
|
||||||
"comma-dangle": ["error", "always-multiline"],
|
|
||||||
"curly": ["error", "multi-line", "consistent"],
|
|
||||||
"eqeqeq": "error",
|
|
||||||
"no-constant-condition": "error",
|
|
||||||
"no-floating-decimal": "error",
|
|
||||||
"no-new": "error",
|
|
||||||
"no-redeclare": "error",
|
|
||||||
"no-unused-vars": ["warn", {"args": "none"}],
|
|
||||||
"no-use-before-define": ["error", {"functions": false, "classes": false}],
|
|
||||||
"no-var": "error",
|
|
||||||
"new-cap": ["error", {"newIsCap": true, "capIsNew: false}],
|
|
||||||
"padded-blocks": ["error", "never"],
|
|
||||||
"strict": ["error", "global"]
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
};
|
|
||||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [14.x]
|
node-version: [22.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
|
||||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -2,6 +2,5 @@
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
"showdown.server": "", // e.g., "?~~localhost:8000"
|
"showdown.server": "", // e.g., "?~~localhost:8000"
|
||||||
"showdown.clientUrl": "http://localhost:8080",
|
"showdown.clientUrl": "http://localhost:8080",
|
||||||
"tslint.configFile": "tslint.json",
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const baseRules = Object.assign({}, require('./../.eslintrc.js').rules);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
"root": true,
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 6,
|
|
||||||
"sourceType": "script",
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"globalReturn": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"es6": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"rules": Object.assign(baseRules, {
|
|
||||||
"comma-dangle": [2, "always-multiline"],
|
|
||||||
"eqeqeq": 2,
|
|
||||||
"no-floating-decimal": 2,
|
|
||||||
"no-new": 2,
|
|
||||||
"no-redeclare": 2,
|
|
||||||
"radix": [1, "as-needed"],
|
|
||||||
"strict": [2, "global"],
|
|
||||||
"no-unused-vars": [1, {"args": "none"}],
|
|
||||||
"no-use-before-define": [2, {"functions": false, "classes": false}],
|
|
||||||
"new-cap": [2, {"newIsCap": true, "capIsNew": false}],
|
|
||||||
"padded-blocks": [2, "never"],
|
|
||||||
|
|
||||||
"arrow-parens": [2, "as-needed"],
|
|
||||||
"arrow-spacing": [2, {"before": true, "after": true}],
|
|
||||||
"constructor-super": 2,
|
|
||||||
"no-class-assign": 2,
|
|
||||||
"no-confusing-arrow": 0,
|
|
||||||
"no-const-assign": 2,
|
|
||||||
"no-dupe-class-members": 2,
|
|
||||||
"no-restricted-syntax": "off",
|
|
||||||
"no-this-before-super": 2,
|
|
||||||
"no-var": 2,
|
|
||||||
"require-yield": 2,
|
|
||||||
"template-curly-spacing": [2, "never"],
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
@ -15,8 +15,8 @@ if (!fs.existsSync('caches/pokemon-showdown')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
process.stdout.write("Syncing data from Git repository... ");
|
process.stdout.write("Syncing data from Git repository... ");
|
||||||
child_process.execSync('git pull', {cwd: 'caches/pokemon-showdown'});
|
child_process.execSync('git pull', { cwd: 'caches/pokemon-showdown' });
|
||||||
child_process.execSync('npm run build', {cwd: 'caches/pokemon-showdown'});
|
child_process.execSync('npm run build', { cwd: 'caches/pokemon-showdown' });
|
||||||
console.log("DONE");
|
console.log("DONE");
|
||||||
|
|
||||||
const Dex = require('../caches/pokemon-showdown/dist/sim/dex').Dex;
|
const Dex = require('../caches/pokemon-showdown/dist/sim/dex').Dex;
|
||||||
|
|
@ -27,7 +27,7 @@ console.log("DONE");
|
||||||
|
|
||||||
function es3stringify(obj) {
|
function es3stringify(obj) {
|
||||||
const buf = JSON.stringify(obj);
|
const buf = JSON.stringify(obj);
|
||||||
return buf.replace(/\"([A-Za-z][A-Za-z0-9]*)\"\:/g, (fullMatch, key) => (
|
return buf.replace(/"([A-Za-z][A-Za-z0-9]*)":/g, (fullMatch, key) => (
|
||||||
['return', 'new', 'delete'].includes(key) ? fullMatch : `${key}:`
|
['return', 'new', 'delete'].includes(key) ? fullMatch : `${key}:`
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -52,10 +52,14 @@ function requireNoCache(pathSpec) {
|
||||||
index = index.concat(Object.keys(Dex.data.Abilities).map(x => x + ' ability'));
|
index = index.concat(Object.keys(Dex.data.Abilities).map(x => x + ' ability'));
|
||||||
index = index.concat(Object.keys(Dex.data.TypeChart).map(x => toID(x) + ' type'));
|
index = index.concat(Object.keys(Dex.data.TypeChart).map(x => toID(x) + ' type'));
|
||||||
index = index.concat(['physical', 'special', 'status'].map(x => toID(x) + ' category'));
|
index = index.concat(['physical', 'special', 'status'].map(x => toID(x) + ' category'));
|
||||||
index = index.concat(['monster', 'water1', 'bug', 'flying', 'field', 'fairy', 'grass', 'humanlike', 'water3', 'mineral', 'amorphous', 'water2', 'ditto', 'dragon', 'undiscovered'].map(x => toID(x) + ' egggroup'));
|
index = index.concat([
|
||||||
index = index.concat(['ou', 'uu', 'ru', 'nu', 'pu', 'zu', 'lc', 'nfe', 'uber', 'uubl', 'rubl', 'nubl', 'publ', 'zubl', 'cap', 'caplc', 'capnfe'].map(x => toID(x) + ' tier'));
|
'monster', 'water1', 'bug', 'flying', 'field', 'fairy', 'grass', 'humanlike', 'water3', 'mineral', 'amorphous', 'water2', 'ditto', 'dragon', 'undiscovered',
|
||||||
|
].map(x => toID(x) + ' egggroup'));
|
||||||
|
index = index.concat([
|
||||||
|
'ou', 'uu', 'ru', 'nu', 'pu', 'zu', 'lc', 'nfe', 'uber', 'uubl', 'rubl', 'nubl', 'publ', 'zubl', 'cap', 'caplc', 'capnfe',
|
||||||
|
].map(x => toID(x) + ' tier'));
|
||||||
|
|
||||||
let BattleArticleTitles = {};
|
const BattleArticleTitles = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const file of fs.readdirSync('../dex.pokemonshowdown.com/articles/')) {
|
for (const file of fs.readdirSync('../dex.pokemonshowdown.com/articles/')) {
|
||||||
|
|
@ -71,7 +75,7 @@ function requireNoCache(pathSpec) {
|
||||||
index.push('' + id + ' article');
|
index.push('' + id + ' article');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
console.log('\n(WARNING: NO ARTICLES)');
|
console.log('\n(WARNING: NO ARTICLES)');
|
||||||
}
|
}
|
||||||
index.push('pokemon article');
|
index.push('pokemon article');
|
||||||
|
|
@ -79,8 +83,8 @@ function requireNoCache(pathSpec) {
|
||||||
|
|
||||||
// generate aliases
|
// generate aliases
|
||||||
function generateAlias(id, name, type) {
|
function generateAlias(id, name, type) {
|
||||||
let i = name.lastIndexOf(' ');
|
let offset = name.lastIndexOf(' ');
|
||||||
if (i < 0) i = name.lastIndexOf('-');
|
if (offset < 0) offset = name.lastIndexOf('-');
|
||||||
if (name.endsWith('-Mega-X') || name.endsWith('-Mega-Y')) {
|
if (name.endsWith('-Mega-X') || name.endsWith('-Mega-Y')) {
|
||||||
index.push('mega' + toID(name.slice(0, -7) + name.slice(-1)) + ' ' + type + ' ' + id + ' 0');
|
index.push('mega' + toID(name.slice(0, -7) + name.slice(-1)) + ' ' + type + ' ' + id + ' 0');
|
||||||
index.push('m' + toID(name.slice(0, -7) + name.slice(-1)) + ' ' + type + ' ' + id + ' 0');
|
index.push('m' + toID(name.slice(0, -7) + name.slice(-1)) + ' ' + type + ' ' + id + ' 0');
|
||||||
|
|
@ -96,86 +100,86 @@ function requireNoCache(pathSpec) {
|
||||||
index.push('alolan' + toID(name.slice(0, -6)) + ' ' + type + ' ' + id + ' 0');
|
index.push('alolan' + toID(name.slice(0, -6)) + ' ' + type + ' ' + id + ' 0');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let oldI = i;
|
const oldOffset = offset;
|
||||||
if (name === 'Alakazam') i = 5;
|
if (name === 'Alakazam') offset = 5;
|
||||||
if (name === 'Arctovish') i = 5;
|
if (name === 'Arctovish') offset = 5;
|
||||||
if (name === 'Arctozolt') i = 5;
|
if (name === 'Arctozolt') offset = 5;
|
||||||
if (name === 'Articuno') i = 5;
|
if (name === 'Articuno') offset = 5;
|
||||||
if (name === 'Breloom') i = 3;
|
if (name === 'Breloom') offset = 3;
|
||||||
if (name === 'Bronzong') i = 4;
|
if (name === 'Bronzong') offset = 4;
|
||||||
if (name === 'Celebi') i = 4;
|
if (name === 'Celebi') offset = 4;
|
||||||
if (name === 'Charizard') i = 5;
|
if (name === 'Charizard') offset = 5;
|
||||||
if (name === 'Donphan') i = 3;
|
if (name === 'Donphan') offset = 3;
|
||||||
if (name === 'Dracovish') i = 5;
|
if (name === 'Dracovish') offset = 5;
|
||||||
if (name === 'Dracozolt') i = 5;
|
if (name === 'Dracozolt') offset = 5;
|
||||||
if (name === 'Dragapult') i = 5;
|
if (name === 'Dragapult') offset = 5;
|
||||||
if (name === 'Dusclops') i = 3;
|
if (name === 'Dusclops') offset = 3;
|
||||||
if (name === 'Electabuzz') i = 6;
|
if (name === 'Electabuzz') offset = 6;
|
||||||
if (name === 'Exeggutor') i = 2;
|
if (name === 'Exeggutor') offset = 2;
|
||||||
if (name === 'Garchomp') i = 3;
|
if (name === 'Garchomp') offset = 3;
|
||||||
if (name === 'Hariyama') i = 4;
|
if (name === 'Hariyama') offset = 4;
|
||||||
if (name === 'Magearna') i = 2;
|
if (name === 'Magearna') offset = 2;
|
||||||
if (name === 'Magnezone') i = 5;
|
if (name === 'Magnezone') offset = 5;
|
||||||
if (name === 'Mamoswine') i = 4;
|
if (name === 'Mamoswine') offset = 4;
|
||||||
if (name === 'Moltres') i = 3;
|
if (name === 'Moltres') offset = 3;
|
||||||
if (name === 'Nidoking') i = 4;
|
if (name === 'Nidoking') offset = 4;
|
||||||
if (name === 'Nidoqueen') i = 4;
|
if (name === 'Nidoqueen') offset = 4;
|
||||||
if (name === 'Nidorina') i = 4;
|
if (name === 'Nidorina') offset = 4;
|
||||||
if (name === 'Nidorino') i = 4;
|
if (name === 'Nidorino') offset = 4;
|
||||||
if (name === 'Regice') i = 3;
|
if (name === 'Regice') offset = 3;
|
||||||
if (name === 'Regidrago') i = 4;
|
if (name === 'Regidrago') offset = 4;
|
||||||
if (name === 'Regieleki') i = 4;
|
if (name === 'Regieleki') offset = 4;
|
||||||
if (name === 'Regigigas') i = 4;
|
if (name === 'Regigigas') offset = 4;
|
||||||
if (name === 'Regirock') i = 4;
|
if (name === 'Regirock') offset = 4;
|
||||||
if (name === 'Registeel') i = 4;
|
if (name === 'Registeel') offset = 4;
|
||||||
if (name === 'Slowbro') i = 4;
|
if (name === 'Slowbro') offset = 4;
|
||||||
if (name === 'Slowking') i = 4;
|
if (name === 'Slowking') offset = 4;
|
||||||
if (name === 'Starmie') i = 4;
|
if (name === 'Starmie') offset = 4;
|
||||||
if (name === 'Tyranitar') i = 6;
|
if (name === 'Tyranitar') offset = 6;
|
||||||
if (name === 'Zapdos') i = 3;
|
if (name === 'Zapdos') offset = 3;
|
||||||
|
|
||||||
if (name === 'Acupressure') i = 3;
|
if (name === 'Acupressure') offset = 3;
|
||||||
if (name === 'Aromatherapy') i = 5;
|
if (name === 'Aromatherapy') offset = 5;
|
||||||
if (name === 'Boomburst') i = 4;
|
if (name === 'Boomburst') offset = 4;
|
||||||
if (name === 'Crabhammer') i = 4;
|
if (name === 'Crabhammer') offset = 4;
|
||||||
if (name === 'Discharge') i = 3;
|
if (name === 'Discharge') offset = 3;
|
||||||
if (name === 'Earthquake') i = 5;
|
if (name === 'Earthquake') offset = 5;
|
||||||
if (name === 'Extrasensory') i = 5;
|
if (name === 'Extrasensory') offset = 5;
|
||||||
if (name === 'Flamethrower') i = 5;
|
if (name === 'Flamethrower') offset = 5;
|
||||||
if (name === 'Headbutt') i = 4;
|
if (name === 'Headbutt') offset = 4;
|
||||||
if (name === 'Moonblast') i = 4;
|
if (name === 'Moonblast') offset = 4;
|
||||||
if (name === 'Moonlight') i = 4;
|
if (name === 'Moonlight') offset = 4;
|
||||||
if (name === 'Overheat') i = 4;
|
if (name === 'Overheat') offset = 4;
|
||||||
if (name === 'Outrage') i = 3;
|
if (name === 'Outrage') offset = 3;
|
||||||
if (name === 'Octazooka') i = 4;
|
if (name === 'Octazooka') offset = 4;
|
||||||
if (name === 'Payback') i = 3;
|
if (name === 'Payback') offset = 3;
|
||||||
if (name === 'Psyshock') i = 3;
|
if (name === 'Psyshock') offset = 3;
|
||||||
if (name === 'Psywave') i = 3;
|
if (name === 'Psywave') offset = 3;
|
||||||
if (name === 'Rototiller') i = 4;
|
if (name === 'Rototiller') offset = 4;
|
||||||
if (name === 'Rollout') i = 4;
|
if (name === 'Rollout') offset = 4;
|
||||||
if (name === 'Safeguard') i = 4;
|
if (name === 'Safeguard') offset = 4;
|
||||||
if (name === 'Sandstorm') i = 4;
|
if (name === 'Sandstorm') offset = 4;
|
||||||
if (name === 'Smokescreen') i = 5;
|
if (name === 'Smokescreen') offset = 5;
|
||||||
if (name === 'Stockpile') i = 5;
|
if (name === 'Stockpile') offset = 5;
|
||||||
if (name === 'Steamroller') i = 5;
|
if (name === 'Steamroller') offset = 5;
|
||||||
if (name === 'Superpower') i = 5;
|
if (name === 'Superpower') offset = 5;
|
||||||
if (name === 'Supersonic') i = 5;
|
if (name === 'Supersonic') offset = 5;
|
||||||
if (name === 'Synchronoise') i = 7;
|
if (name === 'Synchronoise') offset = 7;
|
||||||
if (name === 'Tailwind') i = 4;
|
if (name === 'Tailwind') offset = 4;
|
||||||
if (name === 'Telekinesis') i = 4;
|
if (name === 'Telekinesis') offset = 4;
|
||||||
if (name === 'Teleport') i = 4;
|
if (name === 'Teleport') offset = 4;
|
||||||
if (name === 'Thunderbolt') i = 7;
|
if (name === 'Thunderbolt') offset = 7;
|
||||||
if (name === 'Twineedle') i = 3;
|
if (name === 'Twineedle') offset = 3;
|
||||||
if (name === 'Uproar') i = 2;
|
if (name === 'Uproar') offset = 2;
|
||||||
if (name === 'Venoshock') i = 4;
|
if (name === 'Venoshock') offset = 4;
|
||||||
if (name === 'Whirlpool') i = 5;
|
if (name === 'Whirlpool') offset = 5;
|
||||||
if (name === 'Whirlwind') i = 5;
|
if (name === 'Whirlwind') offset = 5;
|
||||||
let acronym;
|
let acronym;
|
||||||
if (oldI < 0 && i > 0) {
|
if (oldOffset < 0 && offset > 0) {
|
||||||
acronym = toID(name.charAt(0) + name.slice(i));
|
acronym = toID(name.charAt(0) + name.slice(offset));
|
||||||
}
|
}
|
||||||
if (i < 0) return;
|
if (offset < 0) return;
|
||||||
index.push('' + toID(name.slice(i)) + ' ' + type + ' ' + id + ' ' + toID(name.slice(0, i)).length);
|
index.push('' + toID(name.slice(offset)) + ' ' + type + ' ' + id + ' ' + toID(name.slice(0, offset)).length);
|
||||||
if (name.startsWith('Hidden Power ')) {
|
if (name.startsWith('Hidden Power ')) {
|
||||||
acronym = 'hp' + toID(name.substr(13));
|
acronym = 'hp' + toID(name.substr(13));
|
||||||
index.push('' + acronym + ' ' + type + ' ' + id + ' 0');
|
index.push('' + acronym + ' ' + type + ' ' + id + ' 0');
|
||||||
|
|
@ -200,8 +204,8 @@ function requireNoCache(pathSpec) {
|
||||||
index.push('cuno ' + type + ' ' + id + ' 4');
|
index.push('cuno ' + type + ' ' + id + ' 4');
|
||||||
}
|
}
|
||||||
|
|
||||||
let i2 = name.lastIndexOf(' ', i - 1);
|
let i2 = name.lastIndexOf(' ', offset - 1);
|
||||||
if (i2 < 0) i2 = name.lastIndexOf('-', i - 1);
|
if (i2 < 0) i2 = name.lastIndexOf('-', offset - 1);
|
||||||
if (name === 'Zen Headbutt') i2 = 8;
|
if (name === 'Zen Headbutt') i2 = 8;
|
||||||
if (i2 >= 0) {
|
if (i2 >= 0) {
|
||||||
index.push('' + toID(name.slice(i2)) + ' ' + type + ' ' + id + ' ' + toID(name.slice(0, i2)).length);
|
index.push('' + toID(name.slice(i2)) + ' ' + type + ' ' + id + ' ' + toID(name.slice(0, i2)).length);
|
||||||
|
|
@ -248,8 +252,7 @@ function requireNoCache(pathSpec) {
|
||||||
index[index.indexOf('ditto pokemon')] = 'ditto egggroup';
|
index[index.indexOf('ditto pokemon')] = 'ditto egggroup';
|
||||||
index[index.indexOf('ditto egggroup')] = 'ditto pokemon';
|
index[index.indexOf('ditto egggroup')] = 'ditto pokemon';
|
||||||
|
|
||||||
|
const BattleSearchIndex = index.map(x => {
|
||||||
let BattleSearchIndex = index.map(x => {
|
|
||||||
x = x.split(' ');
|
x = x.split(' ');
|
||||||
if (x.length > 3) {
|
if (x.length > 3) {
|
||||||
x[3] = Number(x[3]);
|
x[3] = Number(x[3]);
|
||||||
|
|
@ -258,7 +261,7 @@ function requireNoCache(pathSpec) {
|
||||||
return x;
|
return x;
|
||||||
});
|
});
|
||||||
|
|
||||||
let BattleSearchIndexOffset = BattleSearchIndex.map((entry, i) => {
|
const BattleSearchIndexOffset = BattleSearchIndex.map(entry => {
|
||||||
const id = entry[0];
|
const id = entry[0];
|
||||||
let name = '';
|
let name = '';
|
||||||
switch (entry[1]) {
|
switch (entry[1]) {
|
||||||
|
|
@ -281,13 +284,15 @@ function requireNoCache(pathSpec) {
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
|
|
||||||
let BattleSearchCountIndex = {};
|
const BattleSearchCountIndex = {};
|
||||||
for (const type in Dex.data.TypeChart) {
|
for (const type in Dex.data.TypeChart) {
|
||||||
BattleSearchCountIndex[type + ' move'] = Object.keys(Dex.data.Moves).filter(id => (Dex.data.Moves[id].type === type)).length;
|
BattleSearchCountIndex[type + ' move'] = Object.keys(Dex.data.Moves)
|
||||||
|
.filter(id => (Dex.data.Moves[id].type === type)).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const type in Dex.data.TypeChart) {
|
for (const type in Dex.data.TypeChart) {
|
||||||
BattleSearchCountIndex[type + ' pokemon'] = Object.keys(Dex.data.Pokedex).filter(id => (Dex.data.Pokedex[id].types.indexOf(type) >= 0)).length;
|
BattleSearchCountIndex[type + ' pokemon'] = Object.keys(Dex.data.Pokedex)
|
||||||
|
.filter(id => (Dex.data.Pokedex[id].types.indexOf(type) >= 0)).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
let buf = '// DO NOT EDIT - automatically built with build-tools/build-indexes\n\n';
|
let buf = '// DO NOT EDIT - automatically built with build-tools/build-indexes\n\n';
|
||||||
|
|
@ -369,7 +374,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
const species = Dex.mod(gen).species.get(id);
|
const species = Dex.mod(gen).species.get(id);
|
||||||
const baseSpecies = Dex.mod(gen).species.get(species.baseSpecies);
|
const baseSpecies = Dex.mod(gen).species.get(species.baseSpecies);
|
||||||
if (species.gen > genNum) continue;
|
if (species.gen > genNum) continue;
|
||||||
const tier = (() => {
|
const speciesTier = (() => {
|
||||||
if (isMetBattle) {
|
if (isMetBattle) {
|
||||||
let tier = species.tier;
|
let tier = species.tier;
|
||||||
if (species.isNonstandard) {
|
if (species.isNonstandard) {
|
||||||
|
|
@ -416,7 +421,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
return tier;
|
return tier;
|
||||||
}
|
}
|
||||||
if (isLetsGo) {
|
if (isLetsGo) {
|
||||||
let validNum = (baseSpecies.num <= 151 && species.num >= 1) || [808, 809].includes(baseSpecies.num);
|
const validNum = (baseSpecies.num <= 151 && species.num >= 1) || [808, 809].includes(baseSpecies.num);
|
||||||
if (!validNum) return 'Illegal';
|
if (!validNum) return 'Illegal';
|
||||||
if (species.forme && !['Alola', 'Mega', 'Mega-X', 'Mega-Y', 'Starter'].includes(species.forme)) return 'Illegal';
|
if (species.forme && !['Alola', 'Mega', 'Mega-X', 'Mega-Y', 'Starter'].includes(species.forme)) return 'Illegal';
|
||||||
if (species.name === 'Pikachu-Alola') return 'Illegal';
|
if (species.name === 'Pikachu-Alola') return 'Illegal';
|
||||||
|
|
@ -442,7 +447,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
if (species.isNonstandard && ['LGPE', 'CAP', 'Future'].includes(species.isNonstandard)) return 'Illegal';
|
if (species.isNonstandard && ['LGPE', 'CAP', 'Future'].includes(species.isNonstandard)) return 'Illegal';
|
||||||
return species.tags.includes('Mythical') ? 'Mythical' :
|
return species.tags.includes('Mythical') ? 'Mythical' :
|
||||||
species.tags.includes('Restricted Legendary') ? 'Restricted Legendary' :
|
species.tags.includes('Restricted Legendary') ? 'Restricted Legendary' :
|
||||||
species.nfe ? (species.prevo ? 'NFE' : 'LC') : 'Regular';
|
species.nfe ? (species.prevo ? 'NFE' : 'LC') : 'Regular';
|
||||||
}
|
}
|
||||||
if (species.tier === 'CAP' || species.tier === 'CAP NFE' || species.tier === 'CAP LC') {
|
if (species.tier === 'CAP' || species.tier === 'CAP NFE' || species.tier === 'CAP LC') {
|
||||||
return species.tier;
|
return species.tier;
|
||||||
|
|
@ -455,7 +460,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
}
|
}
|
||||||
return species.tier;
|
return species.tier;
|
||||||
})();
|
})();
|
||||||
overrideTier[species.id] = tier;
|
overrideTier[species.id] = speciesTier;
|
||||||
if (species.forme) {
|
if (species.forme) {
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
|
|
@ -467,8 +472,8 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tierTable[tier]) tierTable[tier] = [];
|
if (!tierTable[speciesTier]) tierTable[speciesTier] = [];
|
||||||
tierTable[tier].push(id);
|
tierTable[speciesTier].push(id);
|
||||||
|
|
||||||
if (genNum === 9) {
|
if (genNum === 9) {
|
||||||
const ubersUU = Dex.formats.get(gen + 'ubersuu');
|
const ubersUU = Dex.formats.get(gen + 'ubersuu');
|
||||||
|
|
@ -610,11 +615,16 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
if (gen === 'gen4') {
|
if (gen === 'gen4') {
|
||||||
return ["CAP", "CAP NFE", "CAP LC", "AG", "Uber", "OU", "(OU)", "UUBL", "UU", "NUBL", "NU", "NFE", "LC"];
|
return ["CAP", "CAP NFE", "CAP LC", "AG", "Uber", "OU", "(OU)", "UUBL", "UU", "NUBL", "NU", "NFE", "LC"];
|
||||||
}
|
}
|
||||||
return ["CAP", "CAP NFE", "CAP LC", "AG", "Uber", "(Uber)", "OU", "(OU)", "UUBL", "UU", "RUBL", "RU", "NUBL", "NU", "PUBL", "PU", "ZUBL", "ZU", "New", "NFE", "LC", "Unreleased"];
|
return [
|
||||||
|
"CAP", "CAP NFE", "CAP LC", "AG", "Uber", "(Uber)", "OU", "(OU)", "UUBL", "UU", "RUBL", "RU", "NUBL", "NU", "PUBL", "PU", "ZUBL", "ZU", "New", "NFE", "LC", "Unreleased",
|
||||||
|
];
|
||||||
})();
|
})();
|
||||||
|
|
||||||
for (const tier of tierOrder) {
|
for (const tier of tierOrder) {
|
||||||
if (tier in {OU:1, AG:1, Uber:1, UU:1, RU:1, NU:1, PU:1, ZU: 1, NFE:1, LC:1, DOU:1, DUU:1, "(DUU)":1, New:1, Legal:1, Regular:1, "Restricted Legendary":1, "CAP LC":1}) {
|
if (tier in {
|
||||||
|
OU: 1, AG: 1, Uber: 1, UU: 1, RU: 1, NU: 1, PU: 1, ZU: 1, NFE: 1, LC: 1, DOU: 1, DUU: 1,
|
||||||
|
"(DUU)": 1, New: 1, Legal: 1, Regular: 1, "Restricted Legendary": 1, "CAP LC": 1,
|
||||||
|
}) {
|
||||||
let usedTier = tier;
|
let usedTier = tier;
|
||||||
if (usedTier === "(DUU)") usedTier = "DNU";
|
if (usedTier === "(DUU)") usedTier = "DNU";
|
||||||
formatSlices[usedTier] = tiers.length;
|
formatSlices[usedTier] = tiers.length;
|
||||||
|
|
@ -907,7 +917,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
}
|
}
|
||||||
if (available) available = !gen4HMs.has(moveid);
|
if (available) available = !gen4HMs.has(moveid);
|
||||||
|
|
||||||
let minUpperGen = available ? 5 : Math.min(
|
const minUpperGen = available ? 5 : Math.min(
|
||||||
...gens.filter(gen => gen > 4)
|
...gens.filter(gen => gen > 4)
|
||||||
);
|
);
|
||||||
legalGens += '0123456789'.slice(minUpperGen);
|
legalGens += '0123456789'.slice(minUpperGen);
|
||||||
|
|
@ -1001,7 +1011,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
}
|
}
|
||||||
if (available) available = !gen4HMs.has(moveid);
|
if (available) available = !gen4HMs.has(moveid);
|
||||||
|
|
||||||
let minUpperGen = available ? 5 : Math.min(
|
const minUpperGen = available ? 5 : Math.min(
|
||||||
...gens.filter(gen => gen > 4)
|
...gens.filter(gen => gen > 4)
|
||||||
);
|
);
|
||||||
legalGens += '012345678'.slice(minUpperGen);
|
legalGens += '012345678'.slice(minUpperGen);
|
||||||
|
|
@ -1044,7 +1054,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
}
|
}
|
||||||
if (available) available = !gen4HMs.has(moveid);
|
if (available) available = !gen4HMs.has(moveid);
|
||||||
|
|
||||||
let minUpperGen = available ? 5 : Math.min(
|
const minUpperGen = available ? 5 : Math.min(
|
||||||
...gens.filter(gen => gen > 4)
|
...gens.filter(gen => gen > 4)
|
||||||
);
|
);
|
||||||
legalGens += '0123456789'.slice(minUpperGen);
|
legalGens += '0123456789'.slice(minUpperGen);
|
||||||
|
|
@ -1090,7 +1100,7 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
}
|
}
|
||||||
if (available) available = !gen4HMs.has(moveid);
|
if (available) available = !gen4HMs.has(moveid);
|
||||||
|
|
||||||
let minUpperGen = available ? 5 : Math.min(
|
const minUpperGen = available ? 5 : Math.min(
|
||||||
...gens.filter(gen => gen > 4)
|
...gens.filter(gen => gen > 4)
|
||||||
);
|
);
|
||||||
legalGens += '0123456789'.slice(minUpperGen);
|
legalGens += '0123456789'.slice(minUpperGen);
|
||||||
|
|
@ -1113,8 +1123,12 @@ process.stdout.write("Building `data/teambuilder-tables.js`... ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client relevant data that should be overriden by past gens and mods
|
// Client relevant data that should be overriden by past gens and mods
|
||||||
const overrideSpeciesKeys = ['abilities', 'baseStats', 'cosmeticFormes', 'isNonstandard', 'requiredItems', 'types', 'unreleasedHidden'];
|
const overrideSpeciesKeys = [
|
||||||
const overrideMoveKeys = ['accuracy', 'basePower', 'category', 'desc', 'flags', 'isNonstandard', 'pp', 'priority', 'shortDesc', 'target', 'type'];
|
'abilities', 'baseStats', 'cosmeticFormes', 'isNonstandard', 'requiredItems', 'types', 'unreleasedHidden',
|
||||||
|
];
|
||||||
|
const overrideMoveKeys = [
|
||||||
|
'accuracy', 'basePower', 'category', 'desc', 'flags', 'isNonstandard', 'pp', 'priority', 'shortDesc', 'target', 'type',
|
||||||
|
];
|
||||||
const overrideAbilityKeys = ['desc', 'flags', 'isNonstandard', 'rating', 'shortDesc'];
|
const overrideAbilityKeys = ['desc', 'flags', 'isNonstandard', 'rating', 'shortDesc'];
|
||||||
const overrideItemKeys = ['desc', 'fling', 'isNonstandard', 'naturalGift', 'shortDesc'];
|
const overrideItemKeys = ['desc', 'fling', 'isNonstandard', 'naturalGift', 'shortDesc'];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,11 @@ function updateLearnsets(callback) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
newLearnsetsG6[speciesid] = {learnset: newLearnset};
|
newLearnsetsG6[speciesid] = { learnset: newLearnset };
|
||||||
}
|
}
|
||||||
|
|
||||||
const buf = [];
|
const buf = [];
|
||||||
const pokemonList = Object.keys(Pokedex).map(speciesId => Pokedex[speciesId]).sort(function (a, b) {
|
const pokemonList = Object.keys(Pokedex).map(speciesId => Pokedex[speciesId]).sort((a, b) => {
|
||||||
// Missingno. goes first (zeroth); afterwards, CAP in descending dex order (increasingly negative)
|
// Missingno. goes first (zeroth); afterwards, CAP in descending dex order (increasingly negative)
|
||||||
// Finally, standard Pokémon in ascending dex order
|
// Finally, standard Pokémon in ascending dex order
|
||||||
if (a.num <= 0 && b.num > 0) return -1;
|
if (a.num <= 0 && b.num > 0) return -1;
|
||||||
|
|
@ -89,11 +89,9 @@ try {
|
||||||
// It doesn't exist currently, but it will by the end of the script execution.
|
// It doesn't exist currently, but it will by the end of the script execution.
|
||||||
// Any other error is unacceptable and will throw.
|
// Any other error is unacceptable and will throw.
|
||||||
}
|
}
|
||||||
try {
|
{
|
||||||
updateStats = fs.statSync(thisFile);
|
updateStats = fs.statSync(thisFile);
|
||||||
updateMTime = updateStats.mtime.getTime();
|
updateMTime = updateStats.mtime.getTime();
|
||||||
} catch (err) {
|
|
||||||
throw err; // !!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update learnsets-g6
|
// update learnsets-g6
|
||||||
|
|
@ -105,7 +103,7 @@ let learnsetsG6ToUpdate = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
learnsetsStats = fs.statSync(path.join(rootDir, 'data', 'learnsets.js'));
|
learnsetsStats = fs.statSync(path.join(rootDir, 'data', 'learnsets.js'));
|
||||||
} catch (err) {
|
} catch {
|
||||||
// Couldn't find learnsets.js, but that's not the end of the world: skip to next task.
|
// Couldn't find learnsets.js, but that's not the end of the world: skip to next task.
|
||||||
console.error("Couldn't find `data/learnsets.js`. Task aborted.");
|
console.error("Couldn't find `data/learnsets.js`. Task aborted.");
|
||||||
learnsetsG6ToUpdate = false;
|
learnsetsG6ToUpdate = false;
|
||||||
|
|
@ -124,10 +122,13 @@ if (learnsetsG6ToUpdate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (learnsetsG6ToUpdate && (!indexStats || !learnsetsG6Stats || indexMTime < updateMTime || indexMTime < learnsetsStats.mtime.getTime() || indexMTime < learnsetsG6Stats.mtime.getTime())) {
|
if (learnsetsG6ToUpdate && (
|
||||||
|
!indexStats || !learnsetsG6Stats || indexMTime < updateMTime || indexMTime < learnsetsStats.mtime.getTime() ||
|
||||||
|
indexMTime < learnsetsG6Stats.mtime.getTime()
|
||||||
|
)) {
|
||||||
// Only merge learnsets.js with learnsets-g6.js if any of those files, or this one, have been modified recently (or if we don't know what "recently" means)
|
// Only merge learnsets.js with learnsets-g6.js if any of those files, or this one, have been modified recently (or if we don't know what "recently" means)
|
||||||
|
|
||||||
updateLearnsets(function (err) {
|
updateLearnsets(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
let stack = err.stack || '';
|
let stack = err.stack || '';
|
||||||
stack = "File `data/learnsets-g6` failed to update.\n" + stack;
|
stack = "File `data/learnsets-g6` failed to update.\n" + stack;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ exports.BattlePokemonSprites = {
|
||||||
substitute:{exists:false, front:{w:34, h:39}, back:{w:37, h:38}},
|
substitute:{exists:false, front:{w:34, h:39}, back:{w:37, h:38}},
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
let g5buf = `/*
|
let g5buf = `/*
|
||||||
DO NOT EDIT
|
DO NOT EDIT
|
||||||
|
|
||||||
|
|
@ -31,26 +30,26 @@ THIS FILE IS AUTOGENERATED BY ./build-tools/build-minidex
|
||||||
exports.BattlePokemonSpritesBW = {
|
exports.BattlePokemonSpritesBW = {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function sizeObj(path) {
|
function sizeObj(objPath) {
|
||||||
try {
|
try {
|
||||||
let size = imageSize(path);
|
const size = imageSize(objPath);
|
||||||
return {
|
return {
|
||||||
w: size.width,
|
w: size.width,
|
||||||
h: size.height,
|
h: size.height,
|
||||||
};
|
};
|
||||||
} catch (e) {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSizes() {
|
function updateSizes() {
|
||||||
for (let baseid in Dex.data.Pokedex) {
|
for (const baseid in Dex.data.Pokedex) {
|
||||||
let species = Dex.species.get(baseid);
|
const species = Dex.species.get(baseid);
|
||||||
for (let formeName of [''].concat(species.cosmeticFormes || [])) {
|
for (const formeName of [''].concat(species.cosmeticFormes || [])) {
|
||||||
let spriteid = species.spriteid;
|
let spriteid = species.spriteid;
|
||||||
if (formeName) spriteid += '-' + toID(formeName).slice(species.id.length);
|
if (formeName) spriteid += '-' + toID(formeName).slice(species.id.length);
|
||||||
let id = toID(spriteid);
|
const id = toID(spriteid);
|
||||||
|
|
||||||
{
|
{
|
||||||
let row = {num: species.num};
|
const row = { num: species.num };
|
||||||
const frontSize = sizeObj('sprites/ani/' + spriteid + '.gif');
|
const frontSize = sizeObj('sprites/ani/' + spriteid + '.gif');
|
||||||
if (frontSize) row.front = frontSize;
|
if (frontSize) row.front = frontSize;
|
||||||
const frontSizeF = sizeObj('sprites/ani/' + spriteid + '-f.gif');
|
const frontSizeF = sizeObj('sprites/ani/' + spriteid + '-f.gif');
|
||||||
|
|
@ -65,7 +64,7 @@ function updateSizes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let g5row = {num: species.num};
|
const g5row = { num: species.num };
|
||||||
const frontSize = sizeObj('sprites/gen5ani/' + spriteid + '.gif');
|
const frontSize = sizeObj('sprites/gen5ani/' + spriteid + '.gif');
|
||||||
if (frontSize) g5row.front = frontSize;
|
if (frontSize) g5row.front = frontSize;
|
||||||
const frontSizeF = sizeObj('sprites/gen5ani/' + spriteid + '-f.gif');
|
const frontSizeF = sizeObj('sprites/gen5ani/' + spriteid + '-f.gif');
|
||||||
|
|
@ -99,6 +98,6 @@ if (fs.existsSync('sprites/ani/')) {
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync('data/pokedex-mini.js');
|
fs.unlinkSync('data/pokedex-mini.js');
|
||||||
fs.unlinkSync('data/pokedex-mini-bw.js');
|
fs.unlinkSync('data/pokedex-mini-bw.js');
|
||||||
} catch (e) {}
|
} catch {}
|
||||||
console.log('SKIPPED');
|
console.log('SKIPPED');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,207 +18,204 @@ const sourceMap = require('source-map');
|
||||||
const VERBOSE = false;
|
const VERBOSE = false;
|
||||||
|
|
||||||
function outputFileSync(filePath, res, opts) {
|
function outputFileSync(filePath, res, opts) {
|
||||||
fs.mkdirSync(path.dirname(filePath), {recursive: true});
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||||
|
|
||||||
// we've requested explicit sourcemaps to be written to disk
|
// we've requested explicit sourcemaps to be written to disk
|
||||||
if (
|
if (
|
||||||
res.map &&
|
res.map &&
|
||||||
opts.sourceMaps &&
|
opts.sourceMaps &&
|
||||||
opts.sourceMaps !== "inline"
|
opts.sourceMaps !== "inline"
|
||||||
) {
|
) {
|
||||||
const mapLoc = filePath + ".map";
|
const mapLoc = filePath + ".map";
|
||||||
res.code += "\n//# sourceMappingURL=" + path.basename(mapLoc);
|
res.code += "\n//# sourceMappingURL=" + path.basename(mapLoc);
|
||||||
res.map.file = path.basename(filePath);
|
res.map.file = path.basename(filePath);
|
||||||
fs.writeFileSync(mapLoc, JSON.stringify(res.map));
|
fs.writeFileSync(mapLoc, JSON.stringify(res.map));
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(filePath, res.code);
|
|
||||||
}
|
|
||||||
|
|
||||||
function slash(path) {
|
|
||||||
const isExtendedLengthPath = /^\\\\\?\\/.test(path);
|
|
||||||
const hasNonAscii = /[^\u0000-\u0080]+/.test(path);
|
|
||||||
|
|
||||||
if (isExtendedLengthPath || hasNonAscii) {
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return path.replace(/\\/g, '/');
|
fs.writeFileSync(filePath, res.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function slash(filePath) {
|
||||||
|
const isExtendedLengthPath = /^\\\\\?\\/.test(filePath);
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
const hasNonAscii = /[^\u0000-\u0080]+/.test(filePath);
|
||||||
|
|
||||||
|
if (isExtendedLengthPath || hasNonAscii) {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePath.replace(/\\/g, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function combineResults(fileResults, sourceMapOptions, opts) {
|
async function combineResults(fileResults, sourceMapOptions, opts) {
|
||||||
let map = null;
|
let map = null;
|
||||||
if (fileResults.some(result => result?.map)) {
|
if (fileResults.some(result => result?.map)) {
|
||||||
map = new sourceMap.SourceMapGenerator(sourceMapOptions);
|
map = new sourceMap.SourceMapGenerator(sourceMapOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = "";
|
let code = "";
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
|
|
||||||
for (const result of fileResults) {
|
for (const result of fileResults) {
|
||||||
if (!result) continue;
|
if (!result) continue;
|
||||||
|
|
||||||
code += result.code + "\n";
|
code += result.code + "\n";
|
||||||
|
|
||||||
if (result.map) {
|
if (result.map) {
|
||||||
const consumer = await new sourceMap.SourceMapConsumer(result.map);
|
const consumer = await new sourceMap.SourceMapConsumer(result.map);
|
||||||
const sources = new Set();
|
const sources = new Set();
|
||||||
|
|
||||||
consumer.eachMapping(function (mapping) {
|
consumer.eachMapping(mapping => {
|
||||||
if (mapping.source != null) sources.add(mapping.source);
|
if (mapping.source != null) sources.add(mapping.source);
|
||||||
|
|
||||||
map.addMapping({
|
map.addMapping({
|
||||||
generated: {
|
generated: {
|
||||||
line: mapping.generatedLine + offset,
|
line: mapping.generatedLine + offset,
|
||||||
column: mapping.generatedColumn,
|
column: mapping.generatedColumn,
|
||||||
},
|
},
|
||||||
source: mapping.source,
|
source: mapping.source,
|
||||||
original:
|
original:
|
||||||
mapping.source == null
|
mapping.source == null ?
|
||||||
? null
|
null : {
|
||||||
: {
|
line: mapping.originalLine,
|
||||||
line: mapping.originalLine,
|
column: mapping.originalColumn,
|
||||||
column: mapping.originalColumn,
|
},
|
||||||
},
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
for (const source of sources) {
|
for (const source of sources) {
|
||||||
const content = consumer.sourceContentFor(source, true);
|
const content = consumer.sourceContentFor(source, true);
|
||||||
if (content !== null) {
|
if (content !== null) {
|
||||||
map.setSourceContent(source, content);
|
map.setSourceContent(source, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = code.split("\n").length - 1;
|
offset = code.split("\n").length - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.sourceMaps === "inline") {
|
if (opts.sourceMaps === "inline") {
|
||||||
const json = JSON.stringify(map);
|
const json = JSON.stringify(map);
|
||||||
const base64 = Buffer.from(json, 'utf8').toString('base64');
|
const base64 = Buffer.from(json, 'utf8').toString('base64');
|
||||||
code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + base64;
|
code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + base64;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { map, code };
|
||||||
map: map,
|
|
||||||
code: code,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function noRebuildNeeded(src, dest) {
|
function noRebuildNeeded(src, dest) {
|
||||||
try {
|
try {
|
||||||
const srcStat = fs.statSync(src, {throwIfNoEntry: false});
|
const srcStat = fs.statSync(src, { throwIfNoEntry: false });
|
||||||
if (!srcStat) return true;
|
if (!srcStat) return true;
|
||||||
const destStat = fs.statSync(dest);
|
const destStat = fs.statSync(dest);
|
||||||
if (srcStat.ctimeMs < destStat.ctimeMs) return true;
|
if (srcStat.ctimeMs < destStat.ctimeMs) return true;
|
||||||
} catch (e) {}
|
} catch {}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileToDir(srcDir, destDir, opts = {}) {
|
function compileToDir(srcDir, destDir, opts = {}) {
|
||||||
const incremental = opts.incremental;
|
const incremental = opts.incremental;
|
||||||
delete opts.incremental;
|
delete opts.incremental;
|
||||||
|
|
||||||
function handleFile(src, base) {
|
function handleFile(src, base) {
|
||||||
let relative = path.relative(base, src);
|
let relative = path.relative(base, src);
|
||||||
|
|
||||||
if (!relative.endsWith('.ts') && !relative.endsWith('.tsx')) {
|
if (!relative.endsWith('.ts') && !relative.endsWith('.tsx')) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (relative.endsWith('.d.ts')) return 0;
|
if (relative.endsWith('.d.ts')) return 0;
|
||||||
|
|
||||||
relative = relative.slice(0, relative.endsWith('.tsx') ? -4 : -3) + '.js';
|
relative = relative.slice(0, relative.endsWith('.tsx') ? -4 : -3) + '.js';
|
||||||
|
|
||||||
const dest = path.join(destDir, relative);
|
const dest = path.join(destDir, relative);
|
||||||
|
|
||||||
if (incremental && noRebuildNeeded(src, dest)) return 0;
|
if (incremental && noRebuildNeeded(src, dest)) return 0;
|
||||||
|
|
||||||
const res = babel.transformFileSync(src, {
|
const res = babel.transformFileSync(src, {
|
||||||
...opts,
|
...opts,
|
||||||
sourceFileName: slash(path.relative(dest + "/..", src)),
|
sourceFileName: slash(path.relative(dest + "/..", src)),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res) return 0;
|
if (!res) return 0;
|
||||||
|
|
||||||
outputFileSync(dest, res, opts);
|
outputFileSync(dest, res, opts);
|
||||||
fs.chmodSync(dest, fs.statSync(src).mode);
|
fs.chmodSync(dest, fs.statSync(src).mode);
|
||||||
|
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
console.log(src + " -> " + dest);
|
console.log(src + " -> " + dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle(src, base) {
|
function handle(src, base) {
|
||||||
const stat = fs.statSync(src, {throwIfNoEntry: false});
|
const stat = fs.statSync(src, { throwIfNoEntry: false });
|
||||||
|
|
||||||
if (!stat) return 0;
|
if (!stat) return 0;
|
||||||
|
|
||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
if (!base) base = src;
|
if (!base) base = src;
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
const files = fs.readdirSync(src);
|
const files = fs.readdirSync(src);
|
||||||
for (const filename of files) {
|
for (const filename of files) {
|
||||||
if (filename.startsWith('.')) continue;
|
if (filename.startsWith('.')) continue;
|
||||||
|
|
||||||
const srcFile = path.join(src, filename);
|
const srcFile = path.join(src, filename);
|
||||||
|
|
||||||
count += handle(srcFile, base);
|
count += handle(srcFile, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
} else {
|
} else {
|
||||||
if (!base) base = path.dirname(src);
|
if (!base) base = path.dirname(src);
|
||||||
return handleFile(src, base);
|
return handleFile(src, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let total = 0;
|
let total = 0;
|
||||||
fs.mkdirSync(destDir, {recursive: true});
|
fs.mkdirSync(destDir, { recursive: true });
|
||||||
const srcDirs = typeof srcDir === 'string' ? [srcDir] : srcDir;
|
const srcDirs = typeof srcDir === 'string' ? [srcDir] : srcDir;
|
||||||
for (const dir of srcDirs) total += handle(dir);
|
for (const dir of srcDirs) total += handle(dir);
|
||||||
if (incremental) opts.incremental = true; // incredibly dumb hack to preserve the option
|
if (incremental) opts.incremental = true; // incredibly dumb hack to preserve the option
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileToFile(srcFile, destFile, opts) {
|
function compileToFile(srcFile, destFile, opts) {
|
||||||
const incremental = opts.incremental;
|
const incremental = opts.incremental;
|
||||||
delete opts.incremental;
|
delete opts.incremental;
|
||||||
|
|
||||||
const srcFiles = typeof srcFile === 'string' ? [srcFile] : srcFile;
|
const srcFiles = typeof srcFile === 'string' ? [srcFile] : srcFile;
|
||||||
|
|
||||||
if (incremental && srcFiles.every(src => noRebuildNeeded(src, destFile))) {
|
if (incremental && srcFiles.every(src => noRebuildNeeded(src, destFile))) {
|
||||||
opts.incremental = true; // incredibly dumb hack to preserve the option
|
opts.incremental = true; // incredibly dumb hack to preserve the option
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
for (const src of srcFiles) {
|
for (const src of srcFiles) {
|
||||||
if (!fs.existsSync(src)) continue;
|
if (!fs.existsSync(src)) continue;
|
||||||
|
|
||||||
const res = babel.transformFileSync(src, opts);
|
const res = babel.transformFileSync(src, opts);
|
||||||
|
|
||||||
if (res) results.push(res);
|
if (res) results.push(res);
|
||||||
|
|
||||||
if (VERBOSE) console.log(src + " ->");
|
if (VERBOSE) console.log(src + " ->");
|
||||||
}
|
}
|
||||||
|
|
||||||
combineResults(results, {
|
combineResults(results, {
|
||||||
file: path.basename(destFile),
|
file: path.basename(destFile),
|
||||||
sourceRoot: opts.sourceRoot,
|
sourceRoot: opts.sourceRoot,
|
||||||
}, opts).then(combined => {
|
}, opts).then(combined => {
|
||||||
outputFileSync(destFile, combined, opts);
|
outputFileSync(destFile, combined, opts);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (VERBOSE) console.log("-> " + destFile);
|
if (VERBOSE) console.log("-> " + destFile);
|
||||||
if (incremental) opts.incremental = true; // incredibly dumb hack to preserve the option
|
if (incremental) opts.incremental = true; // incredibly dumb hack to preserve the option
|
||||||
return results.length;
|
return results.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.compileToDir = compileToDir;
|
exports.compileToDir = compileToDir;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,3 @@ cd "$(dirname "$0")"
|
||||||
cd ..
|
cd ..
|
||||||
cp config/config.js config/config.js.old
|
cp config/config.js config/config.js.old
|
||||||
cp config/head-custom.html config/head-custom.html.old
|
cp config/head-custom.html config/head-custom.html.old
|
||||||
mv -f config/config-test.js config/config.js
|
|
||||||
mv -f config/head-custom-test.html config/head-custom.html
|
|
||||||
mv -i config/config.js.old config/config-test.js
|
|
||||||
mv -i config/head-custom.html.old config/head-custom-test.html
|
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,10 @@ process.chdir(rootDir);
|
||||||
|
|
||||||
const AUTOCONFIG_START = '/*** Begin automatically generated configuration ***/';
|
const AUTOCONFIG_START = '/*** Begin automatically generated configuration ***/';
|
||||||
const AUTOCONFIG_END = '/*** End automatically generated configuration ***/';
|
const AUTOCONFIG_END = '/*** End automatically generated configuration ***/';
|
||||||
|
const UTF8 = { encoding: 'utf8' };
|
||||||
|
|
||||||
function escapeRegex(string) {
|
function escapeRegex(string) {
|
||||||
return string.replace(/[\/\*\.]/g, '\\$&');
|
return string.replace(/[/*.]/g, '\\$&');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************
|
/*********************************************************
|
||||||
|
|
@ -42,7 +43,7 @@ try {
|
||||||
});
|
});
|
||||||
const origin = ('' + commit).trim();
|
const origin = ('' + commit).trim();
|
||||||
version += ` (${head.slice(0, 8)}${head !== origin ? `/${origin.slice(0, 8)}` : ''})`;
|
version += ` (${head.slice(0, 8)}${head !== origin ? `/${origin.slice(0, 8)}` : ''})`;
|
||||||
} catch (e) {}
|
} catch {}
|
||||||
|
|
||||||
const routes = JSON.parse(fs.readFileSync('config/routes.json'));
|
const routes = JSON.parse(fs.readFileSync('config/routes.json'));
|
||||||
const autoconfigRegex = new RegExp(`${escapeRegex(AUTOCONFIG_START)}[^]+${escapeRegex(AUTOCONFIG_END)}`);
|
const autoconfigRegex = new RegExp(`${escapeRegex(AUTOCONFIG_START)}[^]+${escapeRegex(AUTOCONFIG_END)}`);
|
||||||
|
|
@ -59,7 +60,7 @@ Config.routes = {
|
||||||
${AUTOCONFIG_END}`;
|
${AUTOCONFIG_END}`;
|
||||||
|
|
||||||
// remove old automatically generated configuration and add the new one
|
// remove old automatically generated configuration and add the new one
|
||||||
let configBuf = fs.readFileSync('config/config.js', {encoding: 'utf8'});
|
let configBuf = fs.readFileSync('config/config.js', UTF8);
|
||||||
if (autoconfigRegex.test(configBuf)) {
|
if (autoconfigRegex.test(configBuf)) {
|
||||||
configBuf = configBuf.replace(autoconfigRegex, autoconfig);
|
configBuf = configBuf.replace(autoconfigRegex, autoconfig);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -74,11 +75,12 @@ console.log("DONE");
|
||||||
|
|
||||||
process.stdout.write("Compiling TS files... ");
|
process.stdout.write("Compiling TS files... ");
|
||||||
|
|
||||||
let compileStartTime = process.hrtime();
|
const compileStartTime = process.hrtime();
|
||||||
let compiledFiles = 0;
|
let compiledFiles = 0;
|
||||||
|
|
||||||
// Babel can't find babelrc if we try to compile stuff in caches/pokemon-showdown/ fsr
|
// Babel can't find babelrc if we try to compile stuff in caches/pokemon-showdown/ fsr
|
||||||
let compileOpts = Object.assign(eval('(' + fs.readFileSync('.babelrc') + ')'), {
|
// eslint-disable-next-line no-eval
|
||||||
|
const compileOpts = Object.assign(eval('(' + fs.readFileSync('.babelrc') + ')'), {
|
||||||
babelrc: false,
|
babelrc: false,
|
||||||
incremental: true,
|
incremental: true,
|
||||||
ignore: ['play.pokemonshowdown.com/src/battle-animations.js', 'play.pokemonshowdown.com/src/battle-animations-moves.js'],
|
ignore: ['play.pokemonshowdown.com/src/battle-animations.js', 'play.pokemonshowdown.com/src/battle-animations-moves.js'],
|
||||||
|
|
@ -95,7 +97,7 @@ if (process.argv[2] === 'full') {
|
||||||
fs.statSync('play.pokemonshowdown.com/data/graphics.js');
|
fs.statSync('play.pokemonshowdown.com/data/graphics.js');
|
||||||
// graphics.js exists, recompile it
|
// graphics.js exists, recompile it
|
||||||
delete compileOpts.ignore;
|
delete compileOpts.ignore;
|
||||||
} catch (e) {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
compiledFiles += compiler.compileToDir(`play.pokemonshowdown.com/src`, `play.pokemonshowdown.com/js`, compileOpts);
|
compiledFiles += compiler.compileToDir(`play.pokemonshowdown.com/src`, `play.pokemonshowdown.com/js`, compileOpts);
|
||||||
|
|
@ -148,18 +150,18 @@ function addCachebuster(_, attr, url, urlQuery) {
|
||||||
let hash = Math.random(); // just in case creating the hash fails
|
let hash = Math.random(); // just in case creating the hash fails
|
||||||
try {
|
try {
|
||||||
const filename = url.slice(1).replace('/' + routes.client + '/', '');
|
const filename = url.slice(1).replace('/' + routes.client + '/', '');
|
||||||
const fstr = fs.readFileSync(filename, {encoding: 'utf8'});
|
const fstr = fs.readFileSync(filename, UTF8);
|
||||||
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
|
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
|
||||||
} catch (e) {}
|
} catch {}
|
||||||
|
|
||||||
return attr + '="' + url + '?' + hash + '"';
|
return attr + '="' + url + '?' + hash + '"';
|
||||||
} else {
|
} else {
|
||||||
// hardcoded to Replays rn; TODO: generalize
|
// hardcoded to Replays rn; TODO: generalize
|
||||||
let hash;
|
let hash;
|
||||||
try {
|
try {
|
||||||
const fstr = fs.readFileSync('replay.pokemonshowdown.com/' + url, {encoding: 'utf8'});
|
const fstr = fs.readFileSync('replay.pokemonshowdown.com/' + url, UTF8);
|
||||||
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
|
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
|
||||||
} catch (e) {}
|
} catch {}
|
||||||
|
|
||||||
return attr + '="' + url + '?' + (hash || 'v1') + '"';
|
return attr + '="' + url + '?' + (hash || 'v1') + '"';
|
||||||
}
|
}
|
||||||
|
|
@ -169,13 +171,13 @@ function addCachebuster(_, attr, url, urlQuery) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add hashes to js and css files and rewrite URLs
|
// add hashes to js and css files and rewrite URLs
|
||||||
let indexContents = fs.readFileSync('play.pokemonshowdown.com/index.template.html', {encoding: 'utf8'});
|
let indexContents = fs.readFileSync('play.pokemonshowdown.com/index.template.html', UTF8);
|
||||||
indexContents = indexContents.replace(URL_REGEX, addCachebuster);
|
indexContents = indexContents.replace(URL_REGEX, addCachebuster);
|
||||||
let preactIndexContents = fs.readFileSync('play.pokemonshowdown.com/preactalpha.template.html', {encoding: 'utf8'});
|
let preactIndexContents = fs.readFileSync('play.pokemonshowdown.com/preactalpha.template.html', UTF8);
|
||||||
preactIndexContents = preactIndexContents.replace(URL_REGEX, addCachebuster);
|
preactIndexContents = preactIndexContents.replace(URL_REGEX, addCachebuster);
|
||||||
let crossprotocolContents = fs.readFileSync('play.pokemonshowdown.com/crossprotocol.template.html', {encoding: 'utf8'});
|
let crossprotocolContents = fs.readFileSync('play.pokemonshowdown.com/crossprotocol.template.html', UTF8);
|
||||||
crossprotocolContents = crossprotocolContents.replace(URL_REGEX, addCachebuster);
|
crossprotocolContents = crossprotocolContents.replace(URL_REGEX, addCachebuster);
|
||||||
let replayEmbedContents = fs.readFileSync('play.pokemonshowdown.com/js/replay-embed.template.js', {encoding: 'utf8'});
|
let replayEmbedContents = fs.readFileSync('play.pokemonshowdown.com/js/replay-embed.template.js', UTF8);
|
||||||
replayEmbedContents = replayEmbedContents.replace(/play\.pokemonshowdown\.com/g, routes.client);
|
replayEmbedContents = replayEmbedContents.replace(/play\.pokemonshowdown\.com/g, routes.client);
|
||||||
|
|
||||||
// add news, only if it's actually likely to exist
|
// add news, only if it's actually likely to exist
|
||||||
|
|
@ -199,11 +201,11 @@ indexContents = indexContents.replace(/<!-- news -->/g, news);
|
||||||
|
|
||||||
let indexContents2 = '';
|
let indexContents2 = '';
|
||||||
try {
|
try {
|
||||||
let indexContentsOld = indexContents;
|
const indexContentsOld = indexContents;
|
||||||
indexContents = indexContents.replace(/<!-- head custom -->/g, '' + fs.readFileSync('config/head-custom.html'));
|
indexContents = indexContents.replace(/<!-- head custom -->/g, '' + fs.readFileSync('config/head-custom.html'));
|
||||||
indexContents2 = indexContentsOld.replace(/<!-- head custom -->/g, '' + fs.readFileSync('config/head-custom-test.html'));
|
indexContents2 = indexContentsOld
|
||||||
indexContents2 = indexContents2.replace(/src="\/\/play.pokemonshowdown.com\/config\/config.js\?[a-z0-9]*"/, 'src="//play.pokemonshowdown.com/config/config-test.js?4"');
|
.replace(/<!-- head custom -->/g, '' + fs.readFileSync('config/head-custom-test.html'));
|
||||||
} catch (e) {}
|
} catch {}
|
||||||
|
|
||||||
fs.writeFileSync('play.pokemonshowdown.com/index.html', indexContents);
|
fs.writeFileSync('play.pokemonshowdown.com/index.html', indexContents);
|
||||||
if (indexContents2) {
|
if (indexContents2) {
|
||||||
|
|
@ -213,7 +215,7 @@ fs.writeFileSync('play.pokemonshowdown.com/preactalpha.html', preactIndexContent
|
||||||
fs.writeFileSync('play.pokemonshowdown.com/crossprotocol.html', crossprotocolContents);
|
fs.writeFileSync('play.pokemonshowdown.com/crossprotocol.html', crossprotocolContents);
|
||||||
fs.writeFileSync('play.pokemonshowdown.com/js/replay-embed.js', replayEmbedContents);
|
fs.writeFileSync('play.pokemonshowdown.com/js/replay-embed.js', replayEmbedContents);
|
||||||
|
|
||||||
let replaysContents = fs.readFileSync('replay.pokemonshowdown.com/index.template.php', {encoding: 'utf8'});
|
let replaysContents = fs.readFileSync('replay.pokemonshowdown.com/index.template.php', UTF8);
|
||||||
replaysContents = replaysContents.replace(URL_REGEX, addCachebuster);
|
replaysContents = replaysContents.replace(URL_REGEX, addCachebuster);
|
||||||
fs.writeFileSync('replay.pokemonshowdown.com/index.php', replaysContents);
|
fs.writeFileSync('replay.pokemonshowdown.com/index.php', replaysContents);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@ This directory is for caches. Everything here should be safe to delete.
|
||||||
Things cached here:
|
Things cached here:
|
||||||
|
|
||||||
- `pokemon-showdown` a checkout of the server repo, used in the build process (mostly for stuff in `play.pokemonshowdown.com/data/`)
|
- `pokemon-showdown` a checkout of the server repo, used in the build process (mostly for stuff in `play.pokemonshowdown.com/data/`)
|
||||||
- `eslint-*.json` eslint cache files
|
- `eslintcache.json` eslint cache files
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ var Config = Config || {};
|
||||||
Config.bannedHosts = ['cool.jit.su', 'pokeball-nixonserver.rhcloud.com'];
|
Config.bannedHosts = ['cool.jit.su', 'pokeball-nixonserver.rhcloud.com'];
|
||||||
|
|
||||||
Config.whitelist = [
|
Config.whitelist = [
|
||||||
'wikipedia.org',
|
'wikipedia.org'
|
||||||
|
|
||||||
// The full list is maintained outside of this repository so changes to it
|
// The full list is maintained outside of this repository so changes to it
|
||||||
// don't clutter the commit log. Feel free to copy our list for your own
|
// don't clutter the commit log. Feel free to copy our list for your own
|
||||||
|
|
|
||||||
435
eslint-ps-standard.mjs
Normal file
435
eslint-ps-standard.mjs
Normal file
|
|
@ -0,0 +1,435 @@
|
||||||
|
/**
|
||||||
|
* Pokemon Showdown standard style
|
||||||
|
*
|
||||||
|
* This is Showdown's shared ESLint configuration. Each project overrides
|
||||||
|
* at least a little of it here and there, but these are the rules we use
|
||||||
|
* unless there's a good reason otherwise.
|
||||||
|
*/
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import eslint from '@eslint/js';
|
||||||
|
import globals from 'globals';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import stylistic from '@stylistic/eslint-plugin';
|
||||||
|
|
||||||
|
/** @typedef {import('typescript-eslint').Config} ConfigFile */
|
||||||
|
/** @typedef {Awaited<ConfigFile>[number]} Config */
|
||||||
|
/** @typedef {NonNullable<Config['rules']>} Rules */
|
||||||
|
|
||||||
|
export { eslint, globals, tseslint, stylistic };
|
||||||
|
|
||||||
|
/** @type {Config} */
|
||||||
|
export const plugin = {
|
||||||
|
plugins: {
|
||||||
|
'@stylistic': stylistic,
|
||||||
|
'@typescript-eslint': tseslint.plugin,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {typeof tseslint.config} */
|
||||||
|
export const configure = (...args) => [
|
||||||
|
plugin,
|
||||||
|
...tseslint.config(...args),
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @type {NonNullable<Config['rules']>} */
|
||||||
|
export const defaultRules = {
|
||||||
|
...stylistic.configs.customize({
|
||||||
|
braceStyle: '1tbs',
|
||||||
|
indent: 'tab',
|
||||||
|
semi: true,
|
||||||
|
jsx: true,
|
||||||
|
// ...
|
||||||
|
}).rules,
|
||||||
|
|
||||||
|
// TODO rules to revisit
|
||||||
|
// =====================
|
||||||
|
|
||||||
|
// nice to have but we mostly know && || precedence so not urgent to fix
|
||||||
|
"@stylistic/no-mixed-operators": "off",
|
||||||
|
|
||||||
|
// test only (should never be committed, but useful when testing)
|
||||||
|
// ==============================================================
|
||||||
|
// do we want unused args/destructures to start with _? unsure
|
||||||
|
"no-unused-vars": ["warn", {
|
||||||
|
args: "all",
|
||||||
|
argsIgnorePattern: ".",
|
||||||
|
caughtErrors: "all",
|
||||||
|
destructuredArrayIgnorePattern: ".",
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
}],
|
||||||
|
// "no-unused-vars": ["warn", {
|
||||||
|
// args: "all",
|
||||||
|
// argsIgnorePattern: "^_",
|
||||||
|
// caughtErrors: "all",
|
||||||
|
// destructuredArrayIgnorePattern: "^_",
|
||||||
|
// ignoreRestSiblings: true
|
||||||
|
// }],
|
||||||
|
"@stylistic/max-len": ["warn", {
|
||||||
|
"code": 120, "tabWidth": 0,
|
||||||
|
// DO NOT EDIT DIRECTLY: see bottom of file for source
|
||||||
|
"ignorePattern": "^\\s*(?:\\/\\/ \\s*)?(?:(?:export )?(?:let |const |readonly )?[a-zA-Z0-9_$.]+(?: \\+?=>? )|[a-zA-Z0-9$]+: \\[?|(?:return |throw )?(?:new )?(?:[a-zA-Z0-9$.]+\\()?)?(?:Utils\\.html|(?:this\\.)?(?:room\\.)?tr|\\$\\()?['\"`/]",
|
||||||
|
}],
|
||||||
|
"prefer-const": ["warn", { "destructuring": "all" }],
|
||||||
|
|
||||||
|
// PS code (code specific to PS)
|
||||||
|
// =============================
|
||||||
|
"@stylistic/new-parens": "off", // used for the `new class {...}` pattern
|
||||||
|
"no-prototype-builtins": "off",
|
||||||
|
|
||||||
|
// defaults too strict
|
||||||
|
// ===================
|
||||||
|
"no-empty": ["error", { "allowEmptyCatch": true }],
|
||||||
|
"no-case-declarations": "off",
|
||||||
|
|
||||||
|
// probably bugs
|
||||||
|
// =============
|
||||||
|
"array-callback-return": "error",
|
||||||
|
"no-constructor-return": "error",
|
||||||
|
"no-dupe-class-members": "error",
|
||||||
|
"no-extend-native": "error",
|
||||||
|
"no-extra-bind": "warn",
|
||||||
|
"no-extra-label": "warn",
|
||||||
|
"no-eval": "error",
|
||||||
|
"no-implied-eval": "error",
|
||||||
|
"no-inner-declarations": ["error", "functions"],
|
||||||
|
"no-iterator": "error",
|
||||||
|
"no-fallthrough": ["error", { allowEmptyCase: true, reportUnusedFallthroughComment: true }],
|
||||||
|
"no-promise-executor-return": ["error", { allowVoid: true }],
|
||||||
|
"no-return-assign": "error",
|
||||||
|
"no-self-compare": "error",
|
||||||
|
"no-sequences": "error",
|
||||||
|
"no-shadow": "error",
|
||||||
|
"no-template-curly-in-string": "error",
|
||||||
|
"no-throw-literal": "warn",
|
||||||
|
"no-unmodified-loop-condition": "error",
|
||||||
|
// best way to read first key of object
|
||||||
|
// "no-unreachable-loop": "error",
|
||||||
|
// ternary is used to convert callbacks to Promises
|
||||||
|
// tagged templates are used for the SQL library
|
||||||
|
"no-unused-expressions": ["error", { allowTernary: true, allowTaggedTemplates: true, enforceForJSX: true }],
|
||||||
|
"no-useless-call": "error",
|
||||||
|
// "no-useless-assignment": "error",
|
||||||
|
"require-atomic-updates": "error",
|
||||||
|
|
||||||
|
// syntax style (local syntactical, usually autofixable formatting decisions)
|
||||||
|
// ===========================================================================
|
||||||
|
"@stylistic/member-delimiter-style": ["error", {
|
||||||
|
multiline: { delimiter: "comma", requireLast: true },
|
||||||
|
singleline: { delimiter: "comma", requireLast: false },
|
||||||
|
overrides: { interface: {
|
||||||
|
multiline: { delimiter: "semi", requireLast: true },
|
||||||
|
singleline: { delimiter: "semi", requireLast: false },
|
||||||
|
} },
|
||||||
|
}],
|
||||||
|
"default-case-last": "error",
|
||||||
|
"eqeqeq": ["error", "always", { null: "ignore" }],
|
||||||
|
"no-array-constructor": "error",
|
||||||
|
"no-duplicate-imports": "error",
|
||||||
|
"no-implicit-coercion": ["error", { allow: ["!!", "+"] }],
|
||||||
|
"no-multi-str": "error",
|
||||||
|
"no-object-constructor": "error",
|
||||||
|
"no-proto": "error",
|
||||||
|
"no-unneeded-ternary": "error",
|
||||||
|
"no-useless-computed-key": "error",
|
||||||
|
"no-useless-constructor": "error",
|
||||||
|
"no-useless-rename": "error",
|
||||||
|
"no-useless-return": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"object-shorthand": ["error", "always"],
|
||||||
|
"operator-assignment": ["error", "always"],
|
||||||
|
"prefer-arrow-callback": "error",
|
||||||
|
"prefer-exponentiation-operator": "error",
|
||||||
|
"prefer-numeric-literals": "error",
|
||||||
|
"prefer-object-has-own": "error",
|
||||||
|
"prefer-object-spread": "error",
|
||||||
|
"prefer-promise-reject-errors": "error",
|
||||||
|
"prefer-regex-literals": "error",
|
||||||
|
"prefer-rest-params": "error",
|
||||||
|
"prefer-spread": "error",
|
||||||
|
"radix": ["error", "as-needed"],
|
||||||
|
|
||||||
|
// syntax style, overriding base
|
||||||
|
// =============================
|
||||||
|
"@stylistic/quotes": "off",
|
||||||
|
"@stylistic/quote-props": "off",
|
||||||
|
"@stylistic/function-call-spacing": "error",
|
||||||
|
"@stylistic/arrow-parens": ["error", "as-needed"],
|
||||||
|
"@stylistic/comma-dangle": ["error", {
|
||||||
|
"arrays": "always-multiline",
|
||||||
|
"objects": "always-multiline",
|
||||||
|
"imports": "always-multiline",
|
||||||
|
"exports": "always-multiline",
|
||||||
|
"functions": "never",
|
||||||
|
"importAttributes": "always-multiline",
|
||||||
|
"dynamicImports": "always-multiline",
|
||||||
|
"enums": "always-multiline",
|
||||||
|
"generics": "always-multiline",
|
||||||
|
"tuples": "always-multiline",
|
||||||
|
}],
|
||||||
|
"@stylistic/jsx-wrap-multilines": "off",
|
||||||
|
"@stylistic/jsx-closing-bracket-location": ["error", "line-aligned"],
|
||||||
|
// "@stylistic/jsx-closing-tag-location": ["error", "line-aligned"],
|
||||||
|
"@stylistic/jsx-closing-tag-location": "off",
|
||||||
|
"@stylistic/jsx-one-expression-per-line": "off",
|
||||||
|
"@stylistic/jsx-max-props-per-line": "off",
|
||||||
|
"@stylistic/jsx-function-call-newline": "off",
|
||||||
|
"no-restricted-syntax": ["error",
|
||||||
|
{ selector: "CallExpression[callee.name='Symbol']", message: "Annoying to serialize, just use a string" },
|
||||||
|
],
|
||||||
|
|
||||||
|
// whitespace
|
||||||
|
// ==========
|
||||||
|
"@stylistic/block-spacing": "error",
|
||||||
|
"@stylistic/operator-linebreak": ["error", "after"],
|
||||||
|
"@stylistic/max-statements-per-line": ["error", { max: 3, ignoredNodes: ['BreakStatement'] }],
|
||||||
|
"@stylistic/lines-between-class-members": "off",
|
||||||
|
"@stylistic/multiline-ternary": "off",
|
||||||
|
"@stylistic/object-curly-spacing": ["error", "always"],
|
||||||
|
"@stylistic/indent": ["error", "tab", { "flatTernaryExpressions": true }],
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {NonNullable<Config['rules']>} */
|
||||||
|
export const defaultRulesTS = {
|
||||||
|
...defaultRules,
|
||||||
|
|
||||||
|
// TODO: revisit
|
||||||
|
// we should do this someday but it'd have to be a gradual manual process
|
||||||
|
// "@typescript-eslint/explicit-module-boundary-types": "off",
|
||||||
|
// like above but slightly harder, so do that one first
|
||||||
|
// "@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
// probably we should settle on a standard someday
|
||||||
|
// "@typescript-eslint/member-ordering": "off",
|
||||||
|
// "@typescript-eslint/no-extraneous-class": "error",
|
||||||
|
// maybe we should consider this
|
||||||
|
"@typescript-eslint/consistent-indexed-object-style": "off",
|
||||||
|
|
||||||
|
// typescript-eslint specific
|
||||||
|
// ==========================
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": defaultRules["no-unused-vars"],
|
||||||
|
"no-shadow": "off",
|
||||||
|
"@typescript-eslint/no-shadow": defaultRules["no-shadow"],
|
||||||
|
"no-dupe-class-members": "off",
|
||||||
|
"@typescript-eslint/no-dupe-class-members": defaultRules["no-dupe-class-members"],
|
||||||
|
"no-unused-expressions": "off",
|
||||||
|
"@typescript-eslint/no-unused-expressions": defaultRules["no-unused-expressions"],
|
||||||
|
|
||||||
|
// defaults too strict
|
||||||
|
// ===================
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
|
|
||||||
|
// probably bugs
|
||||||
|
// =============
|
||||||
|
"@typescript-eslint/no-empty-object-type": "error",
|
||||||
|
"@typescript-eslint/no-extra-non-null-assertion": "error",
|
||||||
|
"@typescript-eslint/no-misused-new": "error",
|
||||||
|
// no way to get it to be less strict unfortunately
|
||||||
|
// "@typescript-eslint/no-misused-spread": "error",
|
||||||
|
"@typescript-eslint/no-non-null-asserted-optional-chain": "error",
|
||||||
|
|
||||||
|
// naming style
|
||||||
|
// ============
|
||||||
|
"@typescript-eslint/naming-convention": ["error", {
|
||||||
|
"selector": ["class", "interface", "typeAlias"],
|
||||||
|
"format": ["PascalCase"],
|
||||||
|
}],
|
||||||
|
|
||||||
|
// syntax style (local syntactical, usually autofixable formatting decisions)
|
||||||
|
// ===========================================================================
|
||||||
|
"@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }],
|
||||||
|
"@typescript-eslint/prefer-namespace-keyword": "error",
|
||||||
|
"@typescript-eslint/adjacent-overload-signatures": "error",
|
||||||
|
"@typescript-eslint/array-type": "error",
|
||||||
|
"@typescript-eslint/consistent-type-assertions": ["error", { "assertionStyle": "as" }],
|
||||||
|
"@typescript-eslint/consistent-type-definitions": "off",
|
||||||
|
"@typescript-eslint/consistent-type-imports": ["error", { fixStyle: "inline-type-imports" }],
|
||||||
|
"@typescript-eslint/explicit-member-accessibility": ["error", { "accessibility": "no-public" }],
|
||||||
|
"@typescript-eslint/parameter-properties": "error",
|
||||||
|
// `source` and `target` are frequently used as variables that may point to `this`
|
||||||
|
// or to another `Pokemon` object, depending on how the given method is invoked
|
||||||
|
"@typescript-eslint/no-this-alias": ["error", { "allowedNames": ["source", "target"] }],
|
||||||
|
// unfortunately this has lots of false positives without strict array/object property access
|
||||||
|
// "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
|
||||||
|
"@typescript-eslint/prefer-as-const": "error",
|
||||||
|
"@typescript-eslint/prefer-for-of": "error",
|
||||||
|
"@typescript-eslint/prefer-function-type": "error",
|
||||||
|
"@typescript-eslint/prefer-return-this-type": "error",
|
||||||
|
"@typescript-eslint/triple-slash-reference": "error",
|
||||||
|
"@typescript-eslint/unified-signatures": "error",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {NonNullable<Config['rules']>} */
|
||||||
|
export const defaultRulesTSChecked = {
|
||||||
|
...defaultRulesTS,
|
||||||
|
|
||||||
|
// style
|
||||||
|
// =====
|
||||||
|
"@typescript-eslint/no-unnecessary-type-arguments": "error",
|
||||||
|
"@typescript-eslint/restrict-plus-operands": ["error", {
|
||||||
|
allowBoolean: false, allowNullish: false, allowNumberAndString: false, allowRegExp: false,
|
||||||
|
}],
|
||||||
|
"@typescript-eslint/restrict-template-expressions": ["error", {
|
||||||
|
allow: [{ name: ['Error', 'URL', 'URLSearchParams'], from: 'lib' }],
|
||||||
|
allowBoolean: false, allowNever: false, allowNullish: false, allowRegExp: false,
|
||||||
|
}],
|
||||||
|
|
||||||
|
// we use `any`
|
||||||
|
// ============
|
||||||
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-call": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-return": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-argument": "off",
|
||||||
|
|
||||||
|
// yes-types syntax style, overriding base
|
||||||
|
// =======================================
|
||||||
|
"@typescript-eslint/prefer-includes": "error",
|
||||||
|
"@typescript-eslint/prefer-nullish-coalescing": "off",
|
||||||
|
"@typescript-eslint/dot-notation": "off",
|
||||||
|
"@typescript-eslint/no-confusing-non-null-assertion": "off",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {NonNullable<Config['rules']>} */
|
||||||
|
export const defaultRulesES3 = {
|
||||||
|
...defaultRules,
|
||||||
|
// required in ES3
|
||||||
|
// ================
|
||||||
|
"no-var": "off",
|
||||||
|
"object-shorthand": ["error", "never"],
|
||||||
|
"prefer-arrow-callback": "off",
|
||||||
|
"prefer-exponentiation-operator": "off",
|
||||||
|
"prefer-object-has-own": "off",
|
||||||
|
"prefer-object-spread": "off",
|
||||||
|
"prefer-rest-params": "off",
|
||||||
|
"prefer-spread": "off",
|
||||||
|
"radix": "off",
|
||||||
|
"@stylistic/comma-dangle": "error",
|
||||||
|
"no-unused-vars": ["warn", {
|
||||||
|
args: "all",
|
||||||
|
argsIgnorePattern: ".",
|
||||||
|
caughtErrors: "all",
|
||||||
|
caughtErrorsIgnorePattern: "^e(rr)?$",
|
||||||
|
destructuredArrayIgnorePattern: ".",
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
}],
|
||||||
|
"no-restricted-syntax": ["error",
|
||||||
|
{ selector: "TaggedTemplateExpression", message: "Hard to compile down to ES3" },
|
||||||
|
{ selector: "CallExpression[callee.name='Symbol']", message: "Annoying to serialize, just use a string" },
|
||||||
|
],
|
||||||
|
|
||||||
|
// with no block scoping, coming up with original variable names is too hard
|
||||||
|
"no-redeclare": "off",
|
||||||
|
|
||||||
|
// treat var as let
|
||||||
|
// unfortunately doesn't actually let me redeclare
|
||||||
|
// "block-scoped-var": "error",
|
||||||
|
"no-caller": "error",
|
||||||
|
"no-invalid-this": "error",
|
||||||
|
"no-new-wrappers": "error",
|
||||||
|
// Map/Set can be polyfilled but it's nontrivial and it's easier just to use bare objects
|
||||||
|
"no-restricted-globals": ["error", "Proxy", "Reflect", "Symbol", "WeakSet", "WeakMap", "Set", "Map"],
|
||||||
|
"unicode-bom": "error",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually very different from defaultRulesES3, because we don't have to
|
||||||
|
* worry about syntax that's easy to transpile to ES3 (which is basically
|
||||||
|
* all syntax).
|
||||||
|
* @type {NonNullable<Config['rules']>}
|
||||||
|
*/
|
||||||
|
export const defaultRulesES3TSChecked = {
|
||||||
|
...defaultRulesTSChecked,
|
||||||
|
"radix": "off",
|
||||||
|
"no-restricted-globals": ["error", "Proxy", "Reflect", "Symbol", "WeakSet", "WeakMap", "Set", "Map"],
|
||||||
|
"no-restricted-syntax": ["error", "TaggedTemplateExpression", "YieldExpression", "AwaitExpression", "BigIntLiteral"],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Config[]} configs
|
||||||
|
* @returns {Config}
|
||||||
|
*/
|
||||||
|
function extractPlugin(configs) {
|
||||||
|
return configs.find(config => !config.rules) ||
|
||||||
|
(() => { throw new Error('No plugin found'); })();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Config[]} configs
|
||||||
|
* @returns {Rules}
|
||||||
|
*/
|
||||||
|
function extractRules(configs) {
|
||||||
|
const rules = {};
|
||||||
|
for (const config of configs.filter(c => c.rules)) {
|
||||||
|
Object.assign(rules, config.rules);
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
const tseslintPlugin = extractPlugin(tseslint.configs.stylisticTypeChecked);
|
||||||
|
|
||||||
|
/** @type {{[k: string]: Config[]}} */
|
||||||
|
export const configs = {
|
||||||
|
js: [{
|
||||||
|
rules: {
|
||||||
|
...eslint.configs.recommended.rules,
|
||||||
|
...defaultRules,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
ts: [tseslintPlugin, {
|
||||||
|
rules: {
|
||||||
|
...eslint.configs.recommended.rules,
|
||||||
|
...extractRules(tseslint.configs.recommendedTypeChecked),
|
||||||
|
...extractRules(tseslint.configs.stylisticTypeChecked),
|
||||||
|
...defaultRulesTSChecked,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
es3: [{
|
||||||
|
rules: {
|
||||||
|
...eslint.configs.recommended.rules,
|
||||||
|
...defaultRulesES3,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
es3ts: [tseslintPlugin, {
|
||||||
|
rules: {
|
||||||
|
...eslint.configs.recommended.rules,
|
||||||
|
...extractRules(tseslint.configs.recommendedTypeChecked),
|
||||||
|
...extractRules(tseslint.configs.stylisticTypeChecked),
|
||||||
|
...defaultRulesES3TSChecked,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
SOURCE FOR IGNOREPATTERN (compile with https://regexfree.k55.io/ )
|
||||||
|
|
||||||
|
# indentation
|
||||||
|
^\s*
|
||||||
|
# possibly commented out
|
||||||
|
(\/\/\ \s*)?
|
||||||
|
|
||||||
|
(
|
||||||
|
# define a variable, append to a variable, or define a single-arg arrow function
|
||||||
|
(export\ )? (let\ |const\ |readonly\ )? [a-zA-Z0-9_$.]+ (\ \+?=>?\ )
|
||||||
|
|
|
||||||
|
# define a property (oversize arrays are only allowed in properties)
|
||||||
|
[a-zA-Z0-9$]+:\ \[?
|
||||||
|
|
|
||||||
|
# optionally return or throw
|
||||||
|
(return\ |throw\ )?
|
||||||
|
# call a function or constructor
|
||||||
|
(new\ )?([a-zA-Z0-9$.]+\()?
|
||||||
|
)?
|
||||||
|
|
||||||
|
(
|
||||||
|
Utils\.html
|
||||||
|
|
|
||||||
|
(this\.)?(room\.)?tr
|
||||||
|
|
|
||||||
|
\$\(
|
||||||
|
)?
|
||||||
|
|
||||||
|
# start of string or regex
|
||||||
|
['"`\/]
|
||||||
|
|
||||||
|
*/
|
||||||
134
eslint.config.mjs
Normal file
134
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import { configs, configure, globals } from './eslint-ps-standard.mjs';
|
||||||
|
|
||||||
|
export default configure([
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
'caches/**',
|
||||||
|
'play.pokemonshowdown.com/config/config-test.js',
|
||||||
|
'play.pokemonshowdown.com/src/battle-log-misc.js',
|
||||||
|
'play.pokemonshowdown.com/js/replay-embed.js',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JavaScript for browsers (ES3)",
|
||||||
|
files: [
|
||||||
|
'play.pokemonshowdown.com/js/client-battle.js',
|
||||||
|
'play.pokemonshowdown.com/js/client-chat-tournament.js',
|
||||||
|
'play.pokemonshowdown.com/js/client-chat.js',
|
||||||
|
'play.pokemonshowdown.com/js/client-ladder.js',
|
||||||
|
'play.pokemonshowdown.com/js/client-mainmenu.js',
|
||||||
|
'play.pokemonshowdown.com/js/client-rooms.js',
|
||||||
|
'play.pokemonshowdown.com/js/client-teambuilder.js',
|
||||||
|
'play.pokemonshowdown.com/js/client-topbar.js',
|
||||||
|
'play.pokemonshowdown.com/js/client.js',
|
||||||
|
'play.pokemonshowdown.com/js/replay-embed.template.js',
|
||||||
|
'play.pokemonshowdown.com/js/search.js',
|
||||||
|
'play.pokemonshowdown.com/js/storage.js',
|
||||||
|
'config/config-example.js',
|
||||||
|
],
|
||||||
|
extends: [configs.es3],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 3,
|
||||||
|
sourceType: "script",
|
||||||
|
globals: {
|
||||||
|
...globals.builtin,
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node,
|
||||||
|
// Libraries
|
||||||
|
"_": false, "$": false, "Backbone": false, "d3": false, "html": false, "html4": false, "jQuery": false, "SockJS": false, "ColorThief": false,
|
||||||
|
|
||||||
|
// Environment-specific
|
||||||
|
"fs": false, "gui": false, "ga": false, "macgap": false, "nwWindow": false, "webkitNotifications": false, "nw": false,
|
||||||
|
|
||||||
|
// Battle stuff
|
||||||
|
"Battle": true, "Pokemon": true, "BattleSound": true, "BattleTooltips": true, "BattleLog": true,
|
||||||
|
"BattleAbilities": false, "BattleAliases": false, "BattleBackdrops": false, "BattleBackdropsFive": false, "BattleBackdropsFour": false, "BattleBackdropsThree": false, "BattleEffects": false,
|
||||||
|
"BattleFormats": false, "BattleFormatsData": false, "BattleLearnsets": false, "BattleItems": false, "BattleMoveAnims": false, "BattleMovedex": false, "BattleNatures": false,
|
||||||
|
"BattleOtherAnims": false, "BattlePokedex": false, "BattlePokemonSprites": false, "BattlePokemonSpritesBW": false, "BattleSearchCountIndex": false, "BattleSearchIndex": false, "BattleArticleTitles": false,
|
||||||
|
"BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStatusAnims": false, "BattleStatuses": false, "BattleTeambuilderTable": false,
|
||||||
|
"ModifiableValue": false, "BattleStatGuesser": false, "BattleStatOptimizer": false, "BattleText": true, "BattleTextAFD": false, "BattleTextNotAFD": false,
|
||||||
|
"BattleTextParser": false,
|
||||||
|
|
||||||
|
// Generic global variables
|
||||||
|
"Config": false, "BattleSearch": false, "Storage": false, "Dex": false, "DexSearch": false,
|
||||||
|
"app": false, "toID": false, "toRoomid": false, "toUserid": false, "toName": false, "PSUtils": false, "MD5": false,
|
||||||
|
"ChatHistory": false, "Topbar": false, "UserList": false,
|
||||||
|
|
||||||
|
// Rooms
|
||||||
|
"Room": false, "BattleRoom": false, "ChatRoom": false, "ConsoleRoom": false, "HTMLRoom": false, "LadderRoom": false, "MainMenuRoom": false, "RoomsRoom": false, "BattlesRoom": false, "TeambuilderRoom": false,
|
||||||
|
|
||||||
|
// Tons of popups
|
||||||
|
"Popup": false, "ForfeitPopup": false, "BracketPopup": false, "LoginPasswordPopup": false, "UserPopup": false, "UserOptionsPopup": false, "UserOptions": false, "TeamPopup": false,
|
||||||
|
"AvatarsPopup": false, "CreditsPopup": false, "FormatPopup": false, "FormattingPopup": false, "LoginPopup": false,
|
||||||
|
"MovePopup": false, "SoundsPopup": false, "OptionsPopup": false, "PromptPopup": false, "ProxyPopup": false, "ReconnectPopup": false,
|
||||||
|
"RegisterPopup": false, "ReplayUploadedPopup": false, "RulesPopup": false, "TabListPopup": false, "TournamentBox": false,
|
||||||
|
"CustomBackgroundPopup": false,
|
||||||
|
|
||||||
|
// Test client
|
||||||
|
"POKEMON_SHOWDOWN_TESTCLIENT_KEY": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"@stylistic/max-len": "off",
|
||||||
|
// we use these for the big IIFEs that wrap entire files
|
||||||
|
"@stylistic/padded-blocks": "off",
|
||||||
|
// TODO: actually fix useless escapes
|
||||||
|
"no-useless-escape": "off",
|
||||||
|
"no-shadow-restricted-names": "error",
|
||||||
|
"no-shadow": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JavaScript for Node",
|
||||||
|
files: [
|
||||||
|
'*.mjs', // look mom I'm linting myself!
|
||||||
|
'build-tools/*.js',
|
||||||
|
'build-tools/update',
|
||||||
|
'build-tools/build-*',
|
||||||
|
],
|
||||||
|
extends: [configs.js],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.builtin,
|
||||||
|
...globals.node,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TypeScript",
|
||||||
|
files: [
|
||||||
|
'play.pokemonshowdown.com/src/*.ts',
|
||||||
|
'play.pokemonshowdown.com/src/*.tsx',
|
||||||
|
'replay.pokemonshowdown.com/src/*.ts',
|
||||||
|
'replay.pokemonshowdown.com/src/*.tsx',
|
||||||
|
],
|
||||||
|
extends: [configs.es3ts],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// temporary
|
||||||
|
"prefer-const": "off",
|
||||||
|
// we use these for grouping
|
||||||
|
"@stylistic/padded-blocks": "off",
|
||||||
|
// too many of these on client
|
||||||
|
"@typescript-eslint/no-floating-promises": "off",
|
||||||
|
// we use these for animations
|
||||||
|
"@typescript-eslint/unbound-method": "off",
|
||||||
|
"@typescript-eslint/restrict-template-expressions": ["error", {
|
||||||
|
allow: [
|
||||||
|
{ name: ['Error', 'URL', 'URLSearchParams'], from: 'lib' },
|
||||||
|
{ name: ['ModifiableValue'], from: 'file' },
|
||||||
|
],
|
||||||
|
allowBoolean: false, allowNever: false, allowNullish: false, allowRegExp: false,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
5172
package-lock.json
generated
5172
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
|
|
@ -9,9 +9,9 @@
|
||||||
"url": "https://github.com/Zarel/Pokemon-Showdown-Client.git"
|
"url": "https://github.com/Zarel/Pokemon-Showdown-Client.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --config=.eslintrc.js --cache --cache-file=caches/eslint-base.json play.pokemonshowdown.com/js/ && eslint --config=build-tools/.eslintrc.js --cache --cache-file=caches/eslint-build.json build-tools/update build-tools/build-indexes && tslint --project .",
|
"lint": "eslint --cache --cache-location caches/eslintcache.json",
|
||||||
"test": "npm run lint && tsc && node build && mocha test/*.js",
|
"test": "node build && tsc && eslint --cache --cache-location caches/eslintcache.json --max-warnings 0 && mocha test/*.js",
|
||||||
"fix": "eslint --config=.eslintrc.js --fix js/ && eslint --config=build-tools/.eslintrc.js --fix build-tools/update build-tools/build-indexes",
|
"fix": "eslint --cache --cache-location caches/eslintcache.json --fix",
|
||||||
"build": "node build",
|
"build": "node build",
|
||||||
"build-full": "node build full"
|
"build-full": "node build full"
|
||||||
},
|
},
|
||||||
|
|
@ -26,14 +26,16 @@
|
||||||
"image-size": "^0.7.5"
|
"image-size": "^0.7.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@stylistic/eslint-plugin": "^4.0.1",
|
||||||
"@types/jquery": "^3.5.3",
|
"@types/jquery": "^3.5.3",
|
||||||
"@types/mocha": "^5.2.6",
|
"@types/mocha": "^5.2.6",
|
||||||
"eslint": "^5.16.0",
|
"eslint": "^9.20.1",
|
||||||
|
"globals": "^16.0.0",
|
||||||
"mocha": "^6.0.2",
|
"mocha": "^6.0.2",
|
||||||
"preact": "^8.3.1",
|
"preact": "^8.3.1",
|
||||||
"source-map": "^0.7.3",
|
"source-map": "^0.7.3",
|
||||||
"tslint": "^5.20.1",
|
"typescript": "^5.7.3",
|
||||||
"typescript": "^5.7.3"
|
"typescript-eslint": "^8.24.1"
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
this.battle.subscribe(function () { self.updateControls(); });
|
this.battle.subscribe(function () { self.updateControls(); });
|
||||||
|
|
||||||
this.users = {};
|
this.users = {};
|
||||||
this.userCount = {users: 0};
|
this.userCount = { users: 0 };
|
||||||
this.$userList = this.$('.userlist');
|
this.$userList = this.$('.userlist');
|
||||||
this.userList = new UserList({
|
this.userList = new UserList({
|
||||||
el: this.$userList,
|
el: this.$userList,
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
},
|
},
|
||||||
requestLeave: function (e) {
|
requestLeave: function (e) {
|
||||||
if ((this.side || this.requireForfeit) && this.battle && !this.battleEnded && !this.expired && !this.battle.forfeitPending) {
|
if ((this.side || this.requireForfeit) && this.battle && !this.battleEnded && !this.expired && !this.battle.forfeitPending) {
|
||||||
app.addPopup(ForfeitPopup, {room: this, sourceEl: e && e.currentTarget, gameType: 'battle'});
|
app.addPopup(ForfeitPopup, { room: this, sourceEl: e && e.currentTarget, gameType: 'battle' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -217,7 +217,8 @@
|
||||||
this.battle.stepQueue.push('|' + args.join('|'));
|
this.battle.stepQueue.push('|' + args.join('|'));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (logLine.substr(0, 7) === '|title|') { // eslint-disable-line no-empty
|
} else if (logLine.substr(0, 7) === '|title|') {
|
||||||
|
// empty
|
||||||
} else if (logLine.substr(0, 5) === '|win|' || logLine === '|tie') {
|
} else if (logLine.substr(0, 5) === '|win|' || logLine === '|tie') {
|
||||||
this.battleEnded = true;
|
this.battleEnded = true;
|
||||||
this.battle.stepQueue.push(logLine);
|
this.battle.stepQueue.push(logLine);
|
||||||
|
|
@ -410,8 +411,8 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.request.forceSwitch !== true) {
|
if (this.request.forceSwitch !== true) {
|
||||||
var faintedLength = _.filter(this.request.forceSwitch, function (fainted) {return fainted;}).length;
|
var faintedLength = _.filter(this.request.forceSwitch, function (fainted) { return fainted; }).length;
|
||||||
var freedomDegrees = faintedLength - _.filter(switchables.slice(this.battle.pokemonControlled), function (mon) {return !mon.fainted;}).length;
|
var freedomDegrees = faintedLength - _.filter(switchables.slice(this.battle.pokemonControlled), function (mon) { return !mon.fainted; }).length;
|
||||||
this.choice.freedomDegrees = Math.max(freedomDegrees, 0);
|
this.choice.freedomDegrees = Math.max(freedomDegrees, 0);
|
||||||
this.choice.canSwitch = faintedLength - this.choice.freedomDegrees;
|
this.choice.canSwitch = faintedLength - this.choice.freedomDegrees;
|
||||||
}
|
}
|
||||||
|
|
@ -542,7 +543,7 @@
|
||||||
this.$('.timerbutton').replaceWith(this.getTimerHTML());
|
this.$('.timerbutton').replaceWith(this.getTimerHTML());
|
||||||
},
|
},
|
||||||
openTimer: function () {
|
openTimer: function () {
|
||||||
app.addPopup(TimerPopup, {room: this});
|
app.addPopup(TimerPopup, { room: this });
|
||||||
},
|
},
|
||||||
updateMoveControls: function (type) {
|
updateMoveControls: function (type) {
|
||||||
var switchables = this.request && this.request.side ? this.battle.myPokemon : [];
|
var switchables = this.request && this.request.side ? this.battle.myPokemon : [];
|
||||||
|
|
@ -577,7 +578,7 @@
|
||||||
var canTerastallize = curActive.canTerastallize || switchables[pos].canTerastallize;
|
var canTerastallize = curActive.canTerastallize || switchables[pos].canTerastallize;
|
||||||
if (canZMove && typeof canZMove[0] === 'string') {
|
if (canZMove && typeof canZMove[0] === 'string') {
|
||||||
canZMove = _.map(canZMove, function (move) {
|
canZMove = _.map(canZMove, function (move) {
|
||||||
return {move: move, target: Dex.moves.get(move).target};
|
return { move: move, target: Dex.moves.get(move).target };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (gigantamax) gigantamax = Dex.moves.get(gigantamax);
|
if (gigantamax) gigantamax = Dex.moves.get(gigantamax);
|
||||||
|
|
@ -641,7 +642,7 @@
|
||||||
} else if (moveTarget === 'normal' || moveTarget === 'adjacentAlly' || moveTarget === 'adjacentAllyOrSelf') {
|
} else if (moveTarget === 'normal' || moveTarget === 'adjacentAlly' || moveTarget === 'adjacentAllyOrSelf') {
|
||||||
if (Math.abs(activePos - i) > 1) disabled = true;
|
if (Math.abs(activePos - i) > 1) disabled = true;
|
||||||
}
|
}
|
||||||
if (moveTarget !== 'adjacentAllyOrSelf' && activePos == i) disabled = true;
|
if (moveTarget !== 'adjacentAllyOrSelf' && activePos === i) disabled = true;
|
||||||
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
targetMenus[1] += '<button disabled style="visibility:hidden"></button> ';
|
targetMenus[1] += '<button disabled style="visibility:hidden"></button> ';
|
||||||
|
|
@ -840,7 +841,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var switchables = this.request && this.request.side ? this.battle.myPokemon : [];
|
var switchables = this.request && this.request.side ? this.battle.myPokemon : [];
|
||||||
var nearActive = this.battle.nearSide.active;
|
// var nearActive = this.battle.nearSide.active;
|
||||||
var isReviving = !!switchables[pos].reviving;
|
var isReviving = !!switchables[pos].reviving;
|
||||||
|
|
||||||
var requestTitle = '';
|
var requestTitle = '';
|
||||||
|
|
@ -1106,7 +1107,7 @@
|
||||||
request.requestType = 'wait';
|
request.requestType = 'wait';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.choice = choiceText ? {waiting: true} : null;
|
this.choice = choiceText ? { waiting: true } : null;
|
||||||
this.finalDecision = this.finalDecisionMove = this.finalDecisionSwitch = false;
|
this.finalDecision = this.finalDecisionMove = this.finalDecisionSwitch = false;
|
||||||
this.request = request;
|
this.request = request;
|
||||||
if (request.side) {
|
if (request.side) {
|
||||||
|
|
@ -1184,7 +1185,7 @@
|
||||||
this.send('/savereplay');
|
this.send('/savereplay');
|
||||||
},
|
},
|
||||||
openBattleOptions: function () {
|
openBattleOptions: function () {
|
||||||
app.addPopup(BattleOptionsPopup, {battle: this.battle, room: this});
|
app.addPopup(BattleOptionsPopup, { battle: this.battle, room: this });
|
||||||
},
|
},
|
||||||
clickReplayDownloadButton: function (e) {
|
clickReplayDownloadButton: function (e) {
|
||||||
var filename = (this.battle.tier || 'Battle').replace(/[^A-Za-z0-9]/g, '');
|
var filename = (this.battle.tier || 'Battle').replace(/[^A-Za-z0-9]/g, '');
|
||||||
|
|
@ -1277,7 +1278,7 @@
|
||||||
var isTerastal = !!(this.$('input[name=terastallize]')[0] || '').checked;
|
var isTerastal = !!(this.$('input[name=terastallize]')[0] || '').checked;
|
||||||
|
|
||||||
var target = e.getAttribute('data-target');
|
var target = e.getAttribute('data-target');
|
||||||
var choosableTargets = {normal: 1, any: 1, adjacentAlly: 1, adjacentAllyOrSelf: 1, adjacentFoe: 1};
|
var choosableTargets = { normal: 1, any: 1, adjacentAlly: 1, adjacentAllyOrSelf: 1, adjacentFoe: 1 };
|
||||||
if (this.battle.gameType === 'freeforall') delete choosableTargets['adjacentAllyOrSelf'];
|
if (this.battle.gameType === 'freeforall') delete choosableTargets['adjacentAllyOrSelf'];
|
||||||
|
|
||||||
this.choice.choices.push('move ' + pos + (isMega ? ' mega' : '') + (isMegaX ? ' megax' : isMegaY ? ' megay' : '') + (isZMove ? ' zmove' : '') + (isUltraBurst ? ' ultra' : '') + (isDynamax ? ' dynamax' : '') + (isTerastal ? ' terastallize' : ''));
|
this.choice.choices.push('move ' + pos + (isMega ? ' mega' : '') + (isMegaX ? ' megax' : isMegaY ? ' megay' : '') + (isZMove ? ' zmove' : '') + (isUltraBurst ? ' ultra' : '') + (isDynamax ? ' dynamax' : '') + (isTerastal ? ' terastallize' : ''));
|
||||||
|
|
@ -1328,8 +1329,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// After choosing the position to which a pokemon will switch in (Doubles/Triples end-game).
|
// After choosing the position to which a pokemon will switch in (Doubles/Triples end-game).
|
||||||
if (!this.request || this.request.requestType !== 'switch') return false; //??
|
if (!this.request || this.request.requestType !== 'switch') return false; // ??
|
||||||
if (this.choice.canSwitch > _.filter(this.choice.choices, function (choice) {return choice;}).length) {
|
if (this.choice.canSwitch > _.filter(this.choice.choices, function (choice) { return choice; }).length) {
|
||||||
// More switches are pending.
|
// More switches are pending.
|
||||||
this.choice.type = 'switch2';
|
this.choice.type = 'switch2';
|
||||||
this.updateControlsForPlayer();
|
this.updateControlsForPlayer();
|
||||||
|
|
@ -1414,8 +1415,10 @@
|
||||||
}
|
}
|
||||||
} else if (this.request.requestType === 'move') {
|
} else if (this.request.requestType === 'move') {
|
||||||
var requestDetails = this.request && this.request.side ? this.battle.myPokemon : [];
|
var requestDetails = this.request && this.request.side ? this.battle.myPokemon : [];
|
||||||
while (choices.length < this.battle.pokemonControlled &&
|
while (
|
||||||
(!nearActive[choices.length] || requestDetails[choices.length].commanding)) {
|
choices.length < this.battle.pokemonControlled &&
|
||||||
|
(!nearActive[choices.length] || requestDetails[choices.length].commanding)
|
||||||
|
) {
|
||||||
choices.push('pass');
|
choices.push('pass');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,30 +88,30 @@
|
||||||
|
|
||||||
$wrapper.html(
|
$wrapper.html(
|
||||||
'<div class="tournament-title">' +
|
'<div class="tournament-title">' +
|
||||||
'<span class="tournament-format"></span> <span class="tournament-generator"></span> Tournament' +
|
' <span class="tournament-format"></span> <span class="tournament-generator"></span> Tournament' +
|
||||||
'<div class="tournament-status"></div>' +
|
' <div class="tournament-status"></div>' +
|
||||||
'<div class="tournament-toggle">Toggle</div>' +
|
' <div class="tournament-toggle">Toggle</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div class="tournament-box">' +
|
'<div class="tournament-box">' +
|
||||||
'<div class="tournament-bracket"></div>' +
|
' <div class="tournament-bracket"></div>' +
|
||||||
'<div class="tournament-tools">' +
|
' <div class="tournament-tools">' +
|
||||||
'<div class="tournament-team"></div>' +
|
' <div class="tournament-team"></div>' +
|
||||||
'<button class="button tournament-join">Join</button><button class="button tournament-validate"><i class="fa fa-check"></i> Validate</button> <button class="button tournament-leave">Leave</button>' +
|
' <button class="button tournament-join">Join</button><button class="button tournament-validate"><i class="fa fa-check"></i> Validate</button> <button class="button tournament-leave">Leave</button>' +
|
||||||
'<div class="tournament-nomatches">Waiting for battles to become available...</div>' +
|
' <div class="tournament-nomatches">Waiting for battles to become available...</div>' +
|
||||||
'<div class="tournament-challenge">' +
|
' <div class="tournament-challenge">' +
|
||||||
'<div class="tournament-challenge-user"></div>' +
|
' <div class="tournament-challenge-user"></div>' +
|
||||||
'<button class="button tournament-challenge-challenge"><strong>Ready!</strong></button><span class="tournament-challenge-user-menu"></span>' +
|
' <button class="button tournament-challenge-challenge"><strong>Ready!</strong></button><span class="tournament-challenge-user-menu"></span>' +
|
||||||
'</div>' +
|
' </div>' +
|
||||||
'<div class="tournament-challengeby"></div>' +
|
' <div class="tournament-challengeby"></div>' +
|
||||||
'<div class="tournament-challenging">' +
|
' <div class="tournament-challenging">' +
|
||||||
'<div class="tournament-challenging-message"></div>' +
|
' <div class="tournament-challenging-message"></div>' +
|
||||||
'<button class="button tournament-challenge-cancel">Cancel</button>' +
|
' <button class="button tournament-challenge-cancel">Cancel</button>' +
|
||||||
'</div>' +
|
' </div>' +
|
||||||
'<div class="tournament-challenged">' +
|
' <div class="tournament-challenged">' +
|
||||||
'<div class="tournament-challenged-message"></div>' +
|
' <div class="tournament-challenged-message"></div>' +
|
||||||
'<button class="button tournament-challenge-accept"><strong>Ready!</strong></button>' +
|
' <button class="button tournament-challenge-accept"><strong>Ready!</strong></button>' +
|
||||||
'</div>' +
|
' </div>' +
|
||||||
'</div>' +
|
' </div>' +
|
||||||
'</div>');
|
'</div>');
|
||||||
|
|
||||||
this.$title = $wrapper.find('.tournament-title');
|
this.$title = $wrapper.find('.tournament-title');
|
||||||
|
|
@ -609,7 +609,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var nodesByDepth = [];
|
var nodesByDepth = [];
|
||||||
var stack = [{node: data.rootNode, depth: 0}];
|
var stack = [{ node: data.rootNode, depth: 0 }];
|
||||||
while (stack.length > 0) {
|
while (stack.length > 0) {
|
||||||
var frame = stack.pop();
|
var frame = stack.pop();
|
||||||
|
|
||||||
|
|
@ -619,7 +619,7 @@
|
||||||
|
|
||||||
if (!frame.node.children) frame.node.children = [];
|
if (!frame.node.children) frame.node.children = [];
|
||||||
frame.node.children.forEach(function (child) {
|
frame.node.children.forEach(function (child) {
|
||||||
stack.push({node: child, depth: frame.depth + 1});
|
stack.push({ node: child, depth: frame.depth + 1 });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var maxDepth = nodesByDepth.length;
|
var maxDepth = nodesByDepth.length;
|
||||||
|
|
@ -653,10 +653,10 @@
|
||||||
|
|
||||||
var link = d3.svg.diagonal()
|
var link = d3.svg.diagonal()
|
||||||
.source(function (link) {
|
.source(function (link) {
|
||||||
return {x: link.source.x, y: link.source.y + nodeSize.realWidth / 2};
|
return { x: link.source.x, y: link.source.y + nodeSize.realWidth / 2 };
|
||||||
})
|
})
|
||||||
.target(function (link) {
|
.target(function (link) {
|
||||||
return {x: link.target.x, y: link.target.y - nodeSize.realWidth / 2};
|
return { x: link.target.x, y: link.target.y - nodeSize.realWidth / 2 };
|
||||||
})
|
})
|
||||||
.projection(function (link) {
|
.projection(function (link) {
|
||||||
return [size.width - link.y, link.x];
|
return [size.width - link.y, link.x];
|
||||||
|
|
@ -793,20 +793,20 @@
|
||||||
};
|
};
|
||||||
TournamentBox.prototype.showBracketPopup = function (data, isStandalone) {
|
TournamentBox.prototype.showBracketPopup = function (data, isStandalone) {
|
||||||
if (isStandalone)
|
if (isStandalone)
|
||||||
app.addPopup(BracketPopup, {$bracket: this.generateBracket(data)});
|
app.addPopup(BracketPopup, { $bracket: this.generateBracket(data) });
|
||||||
else
|
else
|
||||||
this.bracketPopup = app.addPopup(BracketPopup, {parent: this, $bracket: this.generateBracket(data)});
|
this.bracketPopup = app.addPopup(BracketPopup, { parent: this, $bracket: this.generateBracket(data) });
|
||||||
};
|
};
|
||||||
|
|
||||||
TournamentBox.prototype.renderChallengeUsers = function () {
|
TournamentBox.prototype.renderChallengeUsers = function () {
|
||||||
return ' <button class="button" value="' + toID(this.info.challenges[0]) + '" name="tournamentButton" data-type="challengeUser">Change opponent</button>';
|
return ' <button class="button" value="' + toID(this.info.challenges[0]) + '" name="tournamentButton" data-type="challengeUser">Change opponent</button>';
|
||||||
};
|
};
|
||||||
TournamentBox.prototype.challengeUser = function (user, button) {
|
TournamentBox.prototype.challengeUser = function (user, button) {
|
||||||
app.addPopup(UserPopup, {user: user, users: this.info.challenges, sourceEl: button});
|
app.addPopup(UserPopup, { user: user, users: this.info.challenges, sourceEl: button });
|
||||||
};
|
};
|
||||||
|
|
||||||
TournamentBox.prototype.teamSelect = function (team, button) {
|
TournamentBox.prototype.teamSelect = function (team, button) {
|
||||||
app.addPopup(TeamPopup, {team: team, format: this.info.teambuilderFormat, sourceEl: button, room: this.room.id, isMoreTeams: false, folderToggleOn: true, folderNotExpanded: []});
|
app.addPopup(TeamPopup, { team: team, format: this.info.teambuilderFormat, sourceEl: button, room: this.room.id, isMoreTeams: false, folderToggleOn: true, folderNotExpanded: [] });
|
||||||
};
|
};
|
||||||
|
|
||||||
return TournamentBox;
|
return TournamentBox;
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@
|
||||||
var name = $(e.currentTarget).data('name') || $(e.currentTarget).text();
|
var name = $(e.currentTarget).data('name') || $(e.currentTarget).text();
|
||||||
var away = $(e.currentTarget).data('away') || false;
|
var away = $(e.currentTarget).data('away') || false;
|
||||||
var status = $(e.currentTarget).data('status');
|
var status = $(e.currentTarget).data('status');
|
||||||
app.addPopup(UserPopup, {roomGroup: roomGroup, name: name, away: away, status: status, sourceEl: e.currentTarget, position: position});
|
app.addPopup(UserPopup, { roomGroup: roomGroup, name: name, away: away, status: status, sourceEl: e.currentTarget, position: position });
|
||||||
},
|
},
|
||||||
openPM: function (e) {
|
openPM: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -236,10 +236,10 @@
|
||||||
app.addPopup(AvatarsPopup);
|
app.addPopup(AvatarsPopup);
|
||||||
},
|
},
|
||||||
openSounds: function () {
|
openSounds: function () {
|
||||||
app.addPopup(SoundsPopup, {type: 'semimodal'});
|
app.addPopup(SoundsPopup, { type: 'semimodal' });
|
||||||
},
|
},
|
||||||
openOptions: function () {
|
openOptions: function () {
|
||||||
app.addPopup(OptionsPopup, {type: 'semimodal'});
|
app.addPopup(OptionsPopup, { type: 'semimodal' });
|
||||||
},
|
},
|
||||||
|
|
||||||
// highlight
|
// highlight
|
||||||
|
|
@ -247,7 +247,7 @@
|
||||||
getHighlight: function (message) {
|
getHighlight: function (message) {
|
||||||
var highlights = Dex.prefs('highlights') || {};
|
var highlights = Dex.prefs('highlights') || {};
|
||||||
if (Array.isArray(highlights)) {
|
if (Array.isArray(highlights)) {
|
||||||
highlights = {global: highlights};
|
highlights = { global: highlights };
|
||||||
// Migrate from the old highlight system
|
// Migrate from the old highlight system
|
||||||
Storage.prefs('highlights', highlights);
|
Storage.prefs('highlights', highlights);
|
||||||
}
|
}
|
||||||
|
|
@ -362,7 +362,7 @@
|
||||||
var currentLine = prefix.substr(prefix.lastIndexOf('\n') + 1);
|
var currentLine = prefix.substr(prefix.lastIndexOf('\n') + 1);
|
||||||
var shouldSearchCommands = !cmds || (cmds.length ? !!cmds.length && !cmds.filter(function (x) {
|
var shouldSearchCommands = !cmds || (cmds.length ? !!cmds.length && !cmds.filter(function (x) {
|
||||||
return x.startsWith(currentLine);
|
return x.startsWith(currentLine);
|
||||||
}).length : prefix != this.tabComplete.prefix);
|
}).length : prefix !== this.tabComplete.prefix);
|
||||||
var isCommandSearch = (currentLine.startsWith('/') && !currentLine.startsWith('//')) || currentLine.startsWith('!');
|
var isCommandSearch = (currentLine.startsWith('/') && !currentLine.startsWith('//')) || currentLine.startsWith('!');
|
||||||
var resultsExist = this.tabComplete.lastSearch === text && this.tabComplete.commands;
|
var resultsExist = this.tabComplete.lastSearch === text && this.tabComplete.commands;
|
||||||
if (isCommandSearch && shouldSearchCommands && !resultsExist) {
|
if (isCommandSearch && shouldSearchCommands && !resultsExist) {
|
||||||
|
|
@ -417,7 +417,7 @@
|
||||||
return bidx - aidx;
|
return bidx - aidx;
|
||||||
}
|
}
|
||||||
return -1; // a comes first
|
return -1; // a comes first
|
||||||
} else if (bidx != -1) {
|
} else if (bidx !== -1) {
|
||||||
return 1; // b comes first
|
return 1; // b comes first
|
||||||
}
|
}
|
||||||
return (a[0] < b[0]) ? -1 : 1; // alphabetical order
|
return (a[0] < b[0]) ? -1 : 1; // alphabetical order
|
||||||
|
|
@ -502,7 +502,7 @@
|
||||||
var self = this;
|
var self = this;
|
||||||
var challenge = function (targets) {
|
var challenge = function (targets) {
|
||||||
target = toID(targets[0]);
|
target = toID(targets[0]);
|
||||||
self.challengeData = {userid: target, format: targets.length > 1 ? targets.slice(1).join(',') : '', team: ''};
|
self.challengeData = { userid: target, format: targets.length > 1 ? targets.slice(1).join(',') : '', team: '' };
|
||||||
app.on('response:userdetails', self.challengeUserdetails, self);
|
app.on('response:userdetails', self.challengeUserdetails, self);
|
||||||
app.send('/cmd userdetails ' + target);
|
app.send('/cmd userdetails ' + target);
|
||||||
};
|
};
|
||||||
|
|
@ -575,7 +575,7 @@
|
||||||
case 'open':
|
case 'open':
|
||||||
if (this.checkBroadcast(cmd, text)) return false;
|
if (this.checkBroadcast(cmd, text)) return false;
|
||||||
var openUser = function (target) {
|
var openUser = function (target) {
|
||||||
app.addPopup(UserPopup, {name: target});
|
app.addPopup(UserPopup, { name: target });
|
||||||
};
|
};
|
||||||
target = toName(target);
|
target = toName(target);
|
||||||
if (!target) {
|
if (!target) {
|
||||||
|
|
@ -710,7 +710,7 @@
|
||||||
if (!userid) {
|
if (!userid) {
|
||||||
var newsId = $(this).data('newsid');
|
var newsId = $(this).data('newsid');
|
||||||
if (newsId) {
|
if (newsId) {
|
||||||
$.cookie('showdown_readnews', '' + newsId, {expires: 365});
|
$.cookie('showdown_readnews', '' + newsId, { expires: 365 });
|
||||||
}
|
}
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
return;
|
return;
|
||||||
|
|
@ -772,7 +772,7 @@
|
||||||
}
|
}
|
||||||
this.add('Join/leave messages on room ' + room + ': ALWAYS ON');
|
this.add('Join/leave messages on room ' + room + ': ALWAYS ON');
|
||||||
} else {
|
} else {
|
||||||
serverShowjoins = {global: 1};
|
serverShowjoins = { global: 1 };
|
||||||
this.add('Join/leave messages: ALWAYS ON');
|
this.add('Join/leave messages: ALWAYS ON');
|
||||||
}
|
}
|
||||||
showjoins[Config.server.id] = serverShowjoins;
|
showjoins[Config.server.id] = serverShowjoins;
|
||||||
|
|
@ -791,7 +791,7 @@
|
||||||
}
|
}
|
||||||
this.add('Join/leave messages on room ' + room + ': AUTOMATIC');
|
this.add('Join/leave messages on room ' + room + ': AUTOMATIC');
|
||||||
} else {
|
} else {
|
||||||
serverShowjoins = {global: 0};
|
serverShowjoins = { global: 0 };
|
||||||
this.add('Join/leave messages: AUTOMATIC');
|
this.add('Join/leave messages: AUTOMATIC');
|
||||||
}
|
}
|
||||||
showjoins[Config.server.id] = serverShowjoins;
|
showjoins[Config.server.id] = serverShowjoins;
|
||||||
|
|
@ -1373,10 +1373,10 @@
|
||||||
},
|
},
|
||||||
requestLeave: function (e) {
|
requestLeave: function (e) {
|
||||||
if (app.rooms[''].games && app.rooms[''].games[this.id]) {
|
if (app.rooms[''].games && app.rooms[''].games[this.id]) {
|
||||||
app.addPopup(ForfeitPopup, {room: this, sourceEl: e && e.currentTarget, gameType: (this.id.substring(0, 5) === 'help-' ? 'help' : 'game')});
|
app.addPopup(ForfeitPopup, { room: this, sourceEl: e && e.currentTarget, gameType: (this.id.substring(0, 5) === 'help-' ? 'help' : 'game') });
|
||||||
return false;
|
return false;
|
||||||
} else if (Dex.prefs('leavePopupRoom')) {
|
} else if (Dex.prefs('leavePopupRoom')) {
|
||||||
app.addPopup(ForfeitPopup, {room: this, sourceEl: e && e.currentTarget, gameType: 'room'});
|
app.addPopup(ForfeitPopup, { room: this, sourceEl: e && e.currentTarget, gameType: 'room' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1385,7 +1385,7 @@
|
||||||
this.add(data);
|
this.add(data);
|
||||||
},
|
},
|
||||||
getUserGroup: function (userid) {
|
getUserGroup: function (userid) {
|
||||||
return (app.rooms[this.id].users[userid] || {group: ' '}).group;
|
return (app.rooms[this.id].users[userid] || { group: ' ' }).group;
|
||||||
},
|
},
|
||||||
add: function (log) {
|
add: function (log) {
|
||||||
if (typeof log === 'string') log = log.split('\n');
|
if (typeof log === 'string') log = log.split('\n');
|
||||||
|
|
@ -1500,7 +1500,6 @@
|
||||||
this.addJoinLeave('rename', row[1], row[2], true);
|
this.addJoinLeave('rename', row[1], row[2], true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 'users':
|
case 'users':
|
||||||
this.parseUserList(row[1]);
|
this.parseUserList(row[1]);
|
||||||
break;
|
break;
|
||||||
|
|
@ -1738,9 +1737,9 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (j > 0) {
|
if (j > 0) {
|
||||||
if (j == 1 && list.length == 2) {
|
if (j === 1 && list.length === 2) {
|
||||||
message += ' and ';
|
message += ' and ';
|
||||||
} else if (j == list.length - 1) {
|
} else if (j === list.length - 1) {
|
||||||
message += ', and ';
|
message += ', and ';
|
||||||
} else {
|
} else {
|
||||||
message += ', ';
|
message += ', ';
|
||||||
|
|
@ -1900,7 +1899,7 @@
|
||||||
buf += this.getNoNamedUsersOnline();
|
buf += this.getNoNamedUsersOnline();
|
||||||
}
|
}
|
||||||
if (this.room.userCount.guests) {
|
if (this.room.userCount.guests) {
|
||||||
buf += '<li id="' + this.room.id + '-userlist-guests" style="text-align:center;padding:2px 0"><small>(<span id="' + this.room.id + '-usercount-guests">' + this.room.userCount.guests + '</span> guest' + (this.room.userCount.guests == 1 ? '' : 's') + ')</small></li>';
|
buf += '<li id="' + this.room.id + '-userlist-guests" style="text-align:center;padding:2px 0"><small>(<span id="' + this.room.id + '-usercount-guests">' + this.room.userCount.guests + '</span> guest' + (this.room.userCount.guests === 1 ? '' : 's') + ')</small></li>';
|
||||||
}
|
}
|
||||||
this.$el.html(buf);
|
this.$el.html(buf);
|
||||||
},
|
},
|
||||||
|
|
@ -1960,7 +1959,7 @@
|
||||||
text += '<button class="userbutton username" data-roomgroup="' + BattleLog.escapeHTML(user.group) + '" data-name="' + BattleLog.escapeHTML(user.name) + '"';
|
text += '<button class="userbutton username" data-roomgroup="' + BattleLog.escapeHTML(user.group) + '" data-name="' + BattleLog.escapeHTML(user.name) + '"';
|
||||||
text += (user.away ? ' data-away=true' : '') + (user.status ? ' data-status="' + BattleLog.escapeHTML(user.status) + '"' : '') + '>';
|
text += (user.away ? ' data-away=true' : '') + (user.status ? ' data-status="' + BattleLog.escapeHTML(user.status) + '"' : '') + '>';
|
||||||
var group = user.group;
|
var group = user.group;
|
||||||
var details = Config.groups[group] || {type: 'user'};
|
var details = Config.groups[group] || { type: 'user' };
|
||||||
var color = user.away ? 'color:#888;' : BattleLog.hashColor(userid);
|
var color = user.away ? 'color:#888;' : BattleLog.hashColor(userid);
|
||||||
text += '<em class="group' + (details.group === 2 ? ' staffgroup' : '') + '">' + BattleLog.escapeHTML(group) + '</em>';
|
text += '<em class="group' + (details.group === 2 ? ' staffgroup' : '') + '">' + BattleLog.escapeHTML(group) + '</em>';
|
||||||
if (details.type === 'leadership') {
|
if (details.type === 'leadership') {
|
||||||
|
|
@ -1992,16 +1991,16 @@
|
||||||
comparator: function (a, b) {
|
comparator: function (a, b) {
|
||||||
if (a === b) return 0;
|
if (a === b) return 0;
|
||||||
|
|
||||||
var aUser = this.room.users[a] || {group: Config.defaultGroup, away: false};
|
var aUser = this.room.users[a] || { group: Config.defaultGroup, away: false };
|
||||||
var bUser = this.room.users[b] || {group: Config.defaultGroup, away: false};
|
var bUser = this.room.users[b] || { group: Config.defaultGroup, away: false };
|
||||||
|
|
||||||
var aRank = (
|
var aRank = (
|
||||||
Config.groups[aUser.group || ' '] ||
|
Config.groups[aUser.group || ' '] ||
|
||||||
{order: (Config.defaultOrder || 10006.5)}
|
{ order: (Config.defaultOrder || 10006.5) }
|
||||||
).order;
|
).order;
|
||||||
var bRank = (
|
var bRank = (
|
||||||
Config.groups[bUser.group || ' '] ||
|
Config.groups[bUser.group || ' '] ||
|
||||||
{order: (Config.defaultOrder || 10006.5)}
|
{ order: (Config.defaultOrder || 10006.5) }
|
||||||
).order;
|
).order;
|
||||||
|
|
||||||
if (aRank !== bRank) return aRank - bRank;
|
if (aRank !== bRank) return aRank - bRank;
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,8 @@
|
||||||
clickUsername: function (e) {
|
clickUsername: function (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
var name = $(e.currentTarget).data('name') || $(e.currentTarget).text();
|
var name = $(e.currentTarget).data('name') || $(e.currentTarget).text();
|
||||||
app.addPopup(UserPopup, {name: name, sourceEl: e.currentTarget});
|
app.addPopup(UserPopup, { name: name, sourceEl: e.currentTarget });
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.LadderRoom = HTMLRoom.extend({
|
this.LadderRoom = HTMLRoom.extend({
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
update: function () {
|
update: function () {
|
||||||
if (!this.curFormat) {
|
if (!this.curFormat) {
|
||||||
var buf = '<div class="ladder pad"><p>See a user\'s ranking with <a class="button" href="//' + Config.routes.users + '/" target="_blank">User lookup</a></p>' +
|
var buf = '<div class="ladder pad"><p>See a user\'s ranking with <a class="button" href="//' + Config.routes.users + '/" target="_blank">User lookup</a></p>' +
|
||||||
//'<p><strong style="color:red">I\'m really really sorry, but as a warning: we\'re going to reset the ladder again soon to fix some more ladder bugs.</strong></p>' +
|
// '<p><strong style="color:red">I\'m really really sorry, but as a warning: we\'re going to reset the ladder again soon to fix some more ladder bugs.</strong></p>' +
|
||||||
'<p>(btw if you couldn\'t tell the ladder screens aren\'t done yet; they\'ll look nicer than this once I\'m done.)</p>' +
|
'<p>(btw if you couldn\'t tell the ladder screens aren\'t done yet; they\'ll look nicer than this once I\'m done.)</p>' +
|
||||||
'<p><button name="selectFormat" value="help" class="button"><i class="fa fa-info-circle"></i> How the ladder works</button></p><ul>';
|
'<p><button name="selectFormat" value="help" class="button"><i class="fa fa-info-circle"></i> How the ladder works</button></p><ul>';
|
||||||
if (!window.BattleFormats) {
|
if (!window.BattleFormats) {
|
||||||
|
|
@ -196,7 +196,7 @@
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
COIL_B: {},
|
COIL_B: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
}).call(this, jQuery);
|
}).call(this, jQuery);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
'click .closebutton': 'closePM',
|
'click .closebutton': 'closePM',
|
||||||
'click .minimizebutton': 'minimizePM',
|
'click .minimizebutton': 'minimizePM',
|
||||||
'click .pm-challenge': 'clickPMButtonBarChallenge',
|
'click .pm-challenge': 'clickPMButtonBarChallenge',
|
||||||
'click .pm-userOptions':'clickPMButtonBarUserOptions',
|
'click .pm-userOptions': 'clickPMButtonBarUserOptions',
|
||||||
'click .pm-window': 'clickPMBackground',
|
'click .pm-window': 'clickPMBackground',
|
||||||
'dblclick .pm-window h3': 'dblClickPMHeader',
|
'dblclick .pm-window h3': 'dblClickPMHeader',
|
||||||
'focus textarea': 'onFocusPM',
|
'focus textarea': 'onFocusPM',
|
||||||
|
|
@ -347,7 +347,7 @@
|
||||||
$pmWindow = $(e.currentTarget).closest('.pm-window');
|
$pmWindow = $(e.currentTarget).closest('.pm-window');
|
||||||
var newsId = $pmWindow.data('newsid');
|
var newsId = $pmWindow.data('newsid');
|
||||||
if (newsId) {
|
if (newsId) {
|
||||||
$.cookie('showdown_readnews', '' + newsId, {expires: 365});
|
$.cookie('showdown_readnews', '' + newsId, { expires: 365 });
|
||||||
}
|
}
|
||||||
$pmWindow.remove();
|
$pmWindow.remove();
|
||||||
return;
|
return;
|
||||||
|
|
@ -414,7 +414,7 @@
|
||||||
clickUsername: function (e) {
|
clickUsername: function (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
var name = $(e.currentTarget).data('name') || $(e.currentTarget).text();
|
var name = $(e.currentTarget).data('name') || $(e.currentTarget).text();
|
||||||
app.addPopup(UserPopup, {name: name, sourceEl: e.currentTarget});
|
app.addPopup(UserPopup, { name: name, sourceEl: e.currentTarget });
|
||||||
},
|
},
|
||||||
clickPMButtonBarChallenge: function (e) {
|
clickPMButtonBarChallenge: function (e) {
|
||||||
var name = $(e.currentTarget).closest('.pm-window').data('name');
|
var name = $(e.currentTarget).closest('.pm-window').data('name');
|
||||||
|
|
@ -426,7 +426,7 @@
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
var name = $(e.currentTarget).closest('.pm-window').data('name');
|
var name = $(e.currentTarget).closest('.pm-window').data('name');
|
||||||
var userid = toID($(e.currentTarget).closest('.pm-window').data('name'));
|
var userid = toID($(e.currentTarget).closest('.pm-window').data('name'));
|
||||||
app.addPopup(UserOptions, {name: name, userid: userid, sourceEl: e.currentTarget});
|
app.addPopup(UserOptions, { name: name, userid: userid, sourceEl: e.currentTarget });
|
||||||
},
|
},
|
||||||
focusPM: function (name) {
|
focusPM: function (name) {
|
||||||
this.openPM(name).prependTo(this.$pmBox).find('textarea[name=message]').focus();
|
this.openPM(name).prependTo(this.$pmBox).find('textarea[name=message]').focus();
|
||||||
|
|
@ -668,10 +668,10 @@
|
||||||
app.addPopup(AvatarsPopup);
|
app.addPopup(AvatarsPopup);
|
||||||
},
|
},
|
||||||
openSounds: function () {
|
openSounds: function () {
|
||||||
app.addPopup(SoundsPopup, {type: 'semimodal'});
|
app.addPopup(SoundsPopup, { type: 'semimodal' });
|
||||||
},
|
},
|
||||||
openOptions: function () {
|
openOptions: function () {
|
||||||
app.addPopup(OptionsPopup, {type: 'semimodal'});
|
app.addPopup(OptionsPopup, { type: 'semimodal' });
|
||||||
},
|
},
|
||||||
|
|
||||||
// challenges and searching
|
// challenges and searching
|
||||||
|
|
@ -1002,7 +1002,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
format: function (format, button) {
|
format: function (format, button) {
|
||||||
if (window.BattleFormats) app.addPopup(FormatPopup, {format: format, sourceEl: button});
|
if (window.BattleFormats) app.addPopup(FormatPopup, { format: format, sourceEl: button });
|
||||||
},
|
},
|
||||||
adjustPrivacy: function (disallowSpectators) {
|
adjustPrivacy: function (disallowSpectators) {
|
||||||
Storage.prefs('disallowspectators', disallowSpectators);
|
Storage.prefs('disallowspectators', disallowSpectators);
|
||||||
|
|
@ -1012,7 +1012,7 @@
|
||||||
},
|
},
|
||||||
team: function (team, button) {
|
team: function (team, button) {
|
||||||
var format = $(button).closest('form').find('button[name=format]').val();
|
var format = $(button).closest('form').find('button[name=format]').val();
|
||||||
app.addPopup(TeamPopup, {team: team, format: format, sourceEl: button, folderToggleOn: true, folderNotExpanded: []});
|
app.addPopup(TeamPopup, { team: team, format: format, sourceEl: button, folderToggleOn: true, folderNotExpanded: [] });
|
||||||
},
|
},
|
||||||
|
|
||||||
// format/team selection
|
// format/team selection
|
||||||
|
|
@ -1142,7 +1142,7 @@
|
||||||
app.addPopupPrompt("Username", "Open", function (target) {
|
app.addPopupPrompt("Username", "Open", function (target) {
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
if (toID(target) === 'zarel') {
|
if (toID(target) === 'zarel') {
|
||||||
app.addPopup(Popup, {htmlMessage: "Zarel is very busy; please don't contact him this way. If you're looking for help, try <a href=\"/help\">joining the Help room</a>?"});
|
app.addPopup(Popup, { htmlMessage: "Zarel is very busy; please don't contact him this way. If you're looking for help, try <a href=\"/help\">joining the Help room</a>?" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (target === '~') {
|
if (target === '~') {
|
||||||
|
|
@ -1150,7 +1150,7 @@
|
||||||
app.rooms[''].focusPM('~');
|
app.rooms[''].focusPM('~');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app.addPopup(UserPopup, {name: target});
|
app.addPopup(UserPopup, { name: target });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
|
@ -1202,14 +1202,14 @@
|
||||||
case 'data-move':
|
case 'data-move':
|
||||||
return '[outdated message type not supported]';
|
return '[outdated message type not supported]';
|
||||||
case 'text':
|
case 'text':
|
||||||
return {message: '<div class="chat">' + BattleLog.parseMessage(target) + '</div>', noNotify: true};
|
return { message: '<div class="chat">' + BattleLog.parseMessage(target) + '</div>', noNotify: true };
|
||||||
case 'error':
|
case 'error':
|
||||||
return '<div class="chat message-error">' + BattleLog.escapeHTML(target) + '</div>';
|
return '<div class="chat message-error">' + BattleLog.escapeHTML(target) + '</div>';
|
||||||
case 'html':
|
case 'html':
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return {message: '<div class="chat' + hlClass + '">' + timestamp + '<em>' + BattleLog.sanitizeHTML(target) + '</em></div>', noNotify: isNotPM};
|
return { message: '<div class="chat' + hlClass + '">' + timestamp + '<em>' + BattleLog.sanitizeHTML(target) + '</em></div>', noNotify: isNotPM };
|
||||||
}
|
}
|
||||||
return {message: '<div class="chat chatmessage-' + toID(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>' + BattleLog.sanitizeHTML(target) + '</em></div>', noNotify: isNotPM};
|
return { message: '<div class="chat chatmessage-' + toID(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>' + BattleLog.sanitizeHTML(target) + '</em></div>', noNotify: isNotPM };
|
||||||
case 'uhtml':
|
case 'uhtml':
|
||||||
case 'uhtmlchange':
|
case 'uhtmlchange':
|
||||||
var parts = target.split(',');
|
var parts = target.split(',');
|
||||||
|
|
@ -1225,13 +1225,13 @@
|
||||||
$elements.remove();
|
$elements.remove();
|
||||||
$chatElem.append('<div class="chat uhtml-' + toID(parts[0]) + ' chatmessage-' + toID(name) + '">' + BattleLog.sanitizeHTML(html) + '</div>');
|
$chatElem.append('<div class="chat uhtml-' + toID(parts[0]) + ' chatmessage-' + toID(name) + '">' + BattleLog.sanitizeHTML(html) + '</div>');
|
||||||
}
|
}
|
||||||
return {message: '', noNotify: isNotPM};
|
return { message: '', noNotify: isNotPM };
|
||||||
case 'raw':
|
case 'raw':
|
||||||
return {message: '<div class="chat chatmessage-' + toID(name) + '">' + BattleLog.sanitizeHTML(target) + '</div>', noNotify: isNotPM};
|
return { message: '<div class="chat chatmessage-' + toID(name) + '">' + BattleLog.sanitizeHTML(target) + '</div>', noNotify: isNotPM };
|
||||||
case 'nonotify':
|
case 'nonotify':
|
||||||
return {message: '<div class="chat">' + timestamp + BattleLog.sanitizeHTML(target) + '</div>', noNotify: true};
|
return { message: '<div class="chat">' + timestamp + BattleLog.sanitizeHTML(target) + '</div>', noNotify: true };
|
||||||
case 'challenge':
|
case 'challenge':
|
||||||
return {challenge: target};
|
return { challenge: target };
|
||||||
default:
|
default:
|
||||||
// Not a command or unsupported. Parsed as a normal chat message.
|
// Not a command or unsupported. Parsed as a normal chat message.
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
|
@ -1246,7 +1246,7 @@
|
||||||
events: {
|
events: {
|
||||||
'keyup input[name=search]': 'updateSearch',
|
'keyup input[name=search]': 'updateSearch',
|
||||||
'click details': 'updateOpen',
|
'click details': 'updateOpen',
|
||||||
'click i.fa': 'updateStar',
|
'click i.fa': 'updateStar'
|
||||||
},
|
},
|
||||||
initialize: function (data) {
|
initialize: function (data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
@ -1258,7 +1258,7 @@
|
||||||
"S/V Singles": true, "S/V Doubles": true, "Unofficial Metagames": true, "National Dex": true, "OM of the Month": true,
|
"S/V Singles": true, "S/V Doubles": true, "Unofficial Metagames": true, "National Dex": true, "OM of the Month": true,
|
||||||
"Other Metagames": true, "Randomized Format Spotlight": true, "RoA Spotlight": true,
|
"Other Metagames": true, "Randomized Format Spotlight": true, "RoA Spotlight": true,
|
||||||
// For AFD
|
// For AFD
|
||||||
"Random Meta of the Decade": true,
|
"Random Meta of the Decade": true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!this.starred) this.starred = Storage.prefs('starredformats') || {};
|
if (!this.starred) this.starred = Storage.prefs('starredformats') || {};
|
||||||
|
|
@ -1387,7 +1387,7 @@
|
||||||
if (!format.isTeambuilderFormat) return false;
|
if (!format.isTeambuilderFormat) return false;
|
||||||
} else {
|
} else {
|
||||||
if (format.effectType !== 'Format' || format.battleFormat) return false;
|
if (format.effectType !== 'Format' || format.battleFormat) return false;
|
||||||
if (this.selectType != 'watch' && !format[this.selectType + 'Show']) return false;
|
if (this.selectType !== 'watch' && !format[this.selectType + 'Show']) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
@ -1531,7 +1531,7 @@
|
||||||
} else {
|
} else {
|
||||||
bufs[curBuf] += '<li><button name="selectFolder" class="button" value="(No Folder)"><i class="fa fa-folder" style="margin-right: 7px; margin-left: 4px;"></i>(No Folder)</button></li>';
|
bufs[curBuf] += '<li><button name="selectFolder" class="button" value="(No Folder)"><i class="fa fa-folder" style="margin-right: 7px; margin-left: 4px;"></i>(No Folder)</button></li>';
|
||||||
count++;
|
count++;
|
||||||
if (count % bufBoundary === 0 && count != 0 && curBuf < 4) curBuf++;
|
if (count % bufBoundary === 0 && count !== 0 && curBuf < 4) curBuf++;
|
||||||
}
|
}
|
||||||
if (!isNoFolder) {
|
if (!isNoFolder) {
|
||||||
for (var i = 0; i < teams.length; i++) {
|
for (var i = 0; i < teams.length; i++) {
|
||||||
|
|
@ -1578,11 +1578,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
'click input[type=checkbox]': 'foldersToggle',
|
'click input[type=checkbox]': 'foldersToggle'
|
||||||
},
|
},
|
||||||
moreTeams: function () {
|
moreTeams: function () {
|
||||||
this.close();
|
this.close();
|
||||||
app.addPopup(TeamPopup, {team: this.team, format: this.format, sourceEl: this.sourceEl, room: this.room, isMoreTeams: true, folderToggleOn: this.folderToggleOn, folderNotExpanded: this.folderNotExpanded});
|
app.addPopup(TeamPopup, { team: this.team, format: this.format, sourceEl: this.sourceEl, room: this.room, isMoreTeams: true, folderToggleOn: this.folderToggleOn, folderNotExpanded: this.folderNotExpanded });
|
||||||
},
|
},
|
||||||
teambuilder: function () {
|
teambuilder: function () {
|
||||||
var teamFormat = this.teamFormat;
|
var teamFormat = this.teamFormat;
|
||||||
|
|
@ -1599,19 +1599,18 @@
|
||||||
if (folder === key) {
|
if (folder === key) {
|
||||||
keyExists = true;
|
keyExists = true;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
if (!keyExists) {
|
if (!keyExists) {
|
||||||
folderNotExpanded.push(key);
|
folderNotExpanded.push(key);
|
||||||
}
|
}
|
||||||
this.close();
|
this.close();
|
||||||
app.addPopup(TeamPopup, {team: this.team, format: this.format, sourceEl: this.sourceEl, room: this.room, isMoreTeams: this.isMoreTeams, folderToggleOn: this.folderToggleOn, folderNotExpanded: folderNotExpanded});
|
app.addPopup(TeamPopup, { team: this.team, format: this.format, sourceEl: this.sourceEl, room: this.room, isMoreTeams: this.isMoreTeams, folderToggleOn: this.folderToggleOn, folderNotExpanded: folderNotExpanded });
|
||||||
},
|
},
|
||||||
foldersToggle: function () {
|
foldersToggle: function () {
|
||||||
this.close();
|
this.close();
|
||||||
app.addPopup(TeamPopup, {team: this.team, format: this.format, sourceEl: this.sourceEl, room: this.room, isMoreTeams: this.isMoreTeams, folderToggleOn: !this.folderToggleOn, folderNotExpanded: this.folderNotExpanded});
|
app.addPopup(TeamPopup, { team: this.team, format: this.format, sourceEl: this.sourceEl, room: this.room, isMoreTeams: this.isMoreTeams, folderToggleOn: !this.folderToggleOn, folderNotExpanded: this.folderNotExpanded });
|
||||||
},
|
},
|
||||||
selectTeam: function (i) {
|
selectTeam: function (i) {
|
||||||
i = +i;
|
i = +i;
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,8 @@
|
||||||
if (rooms.userCount) {
|
if (rooms.userCount) {
|
||||||
var userCount = Number(rooms.userCount);
|
var userCount = Number(rooms.userCount);
|
||||||
var battleCount = Number(rooms.battleCount);
|
var battleCount = Number(rooms.battleCount);
|
||||||
var leftSide = '<button class="button" name="finduser" title="Find an online user"><span class="pixelated usercount" title="Meloetta is PS\'s mascot! The Aria forme is about using its voice, and represents our chatrooms." ></span><strong>' + userCount + '</strong> ' + (userCount == 1 ? 'user' : 'users') + ' online</button> ';
|
var leftSide = '<button class="button" name="finduser" title="Find an online user"><span class="pixelated usercount" title="Meloetta is PS\'s mascot! The Aria forme is about using its voice, and represents our chatrooms." ></span><strong>' + userCount + '</strong> ' + (userCount === 1 ? 'user' : 'users') + ' online</button> ';
|
||||||
var rightSide = '<button class="button" name="roomlist" title="Watch an active battle"><span class="pixelated battlecount" title="Meloetta is PS\'s mascot! The Pirouette forme is Fighting-type, and represents our battles." ></span><strong>' + battleCount + '</strong> active ' + (battleCount == 1 ? 'battle' : 'battles') + '</button>';
|
var rightSide = '<button class="button" name="roomlist" title="Watch an active battle"><span class="pixelated battlecount" title="Meloetta is PS\'s mascot! The Pirouette forme is Fighting-type, and represents our battles." ></span><strong>' + battleCount + '</strong> active ' + (battleCount === 1 ? 'battle' : 'battles') + '</button>';
|
||||||
this.$('.roomlisttop').html('<div class="roomcounters">' + leftSide + '</td><td>' + rightSide + '</div>');
|
this.$('.roomlisttop').html('<div class="roomcounters">' + leftSide + '</td><td>' + rightSide + '</div>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,9 +174,11 @@
|
||||||
);
|
);
|
||||||
this.$('.roomlist').last().html(
|
this.$('.roomlist').last().html(
|
||||||
(otherRooms.length ?
|
(otherRooms.length ?
|
||||||
'<h2 class="rooms-chatrooms">Chat rooms</h2>' + otherRooms.sort(this.compareRooms).map(this.renderRoomBtn).join("") : '') +
|
'<h2 class="rooms-chatrooms">Chat rooms</h2>' + otherRooms.sort(this.compareRooms).map(this.renderRoomBtn).join("") : ''
|
||||||
|
) +
|
||||||
(hiddenRooms.length && this.showMoreRooms ?
|
(hiddenRooms.length && this.showMoreRooms ?
|
||||||
'<h2 class="rooms-chatrooms">Hidden rooms</h2>' + hiddenRooms.sort(this.compareRooms).map(this.renderRoomBtn).join("") : '')
|
'<h2 class="rooms-chatrooms">Hidden rooms</h2>' + hiddenRooms.sort(this.compareRooms).map(this.renderRoomBtn).join("") : ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
roomlist: function () {
|
roomlist: function () {
|
||||||
|
|
@ -196,10 +198,10 @@
|
||||||
app.addPopupPrompt("Username", "Open", function (target) {
|
app.addPopupPrompt("Username", "Open", function (target) {
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
if (toID(target) === 'zarel') {
|
if (toID(target) === 'zarel') {
|
||||||
app.addPopup(Popup, {htmlMessage: "Zarel is very busy; please don't contact him this way. If you're looking for help, try <a href=\"/help\">joining the Help room</a>?"});
|
app.addPopup(Popup, { htmlMessage: "Zarel is very busy; please don't contact him this way. If you're looking for help, try <a href=\"/help\">joining the Help room</a>?" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app.addPopup(UserPopup, {name: target});
|
app.addPopup(UserPopup, { name: target });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refresh: function () {
|
refresh: function () {
|
||||||
|
|
@ -243,9 +245,9 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var self = this;
|
var self = this;
|
||||||
app.addPopup(FormatPopup, {format: format, sourceEl: button, selectType: 'watch', onselect: function (newFormat) {
|
app.addPopup(FormatPopup, { format: format, sourceEl: button, selectType: 'watch', onselect: function (newFormat) {
|
||||||
self.changeFormat(newFormat);
|
self.changeFormat(newFormat);
|
||||||
}});
|
} });
|
||||||
},
|
},
|
||||||
changeFormat: function (format) {
|
changeFormat: function (format) {
|
||||||
this.format = format;
|
this.format = format;
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,8 @@
|
||||||
'change .detailsform input': 'detailsChange',
|
'change .detailsform input': 'detailsChange',
|
||||||
'change .detailsform select': 'detailsChange',
|
'change .detailsform select': 'detailsChange',
|
||||||
'submit .detailsform': 'detailsChange',
|
'submit .detailsform': 'detailsChange',
|
||||||
'click .changeform' : 'altForm',
|
'click .changeform': 'altForm',
|
||||||
'click .altform' : 'altForm',
|
'click .altform': 'altForm',
|
||||||
|
|
||||||
// stats
|
// stats
|
||||||
'keyup .statform input.numform': 'statChange',
|
'keyup .statform input.numform': 'statChange',
|
||||||
|
|
@ -544,7 +544,7 @@
|
||||||
this.teamScrollPos = 0;
|
this.teamScrollPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//reset focus to searchbar
|
// reset focus to searchbar
|
||||||
var teamSearchBar = this.$("#teamSearchBar");
|
var teamSearchBar = this.$("#teamSearchBar");
|
||||||
var strLength = teamSearchBar.val().length;
|
var strLength = teamSearchBar.val().length;
|
||||||
if (strLength) {
|
if (strLength) {
|
||||||
|
|
@ -555,7 +555,6 @@
|
||||||
updatePersistence: function (state) {
|
updatePersistence: function (state) {
|
||||||
if (state) {
|
if (state) {
|
||||||
this.$('.storage-warning').html('');
|
this.$('.storage-warning').html('');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
greeting: function (answer, button) {
|
greeting: function (answer, button) {
|
||||||
|
|
@ -640,9 +639,9 @@
|
||||||
if (format === '+') {
|
if (format === '+') {
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
var self = this;
|
var self = this;
|
||||||
app.addPopup(FormatPopup, {format: '', sourceEl: e.currentTarget, selectType: 'teambuilder', onselect: function (newFormat) {
|
app.addPopup(FormatPopup, { format: '', sourceEl: e.currentTarget, selectType: 'teambuilder', onselect: function (newFormat) {
|
||||||
self.selectFolder(newFormat);
|
self.selectFolder(newFormat);
|
||||||
}});
|
} });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (format === '++') {
|
if (format === '++') {
|
||||||
|
|
@ -651,7 +650,7 @@
|
||||||
// app.addPopupPrompt("Folder name:", "Create folder", function (newFormat) {
|
// app.addPopupPrompt("Folder name:", "Create folder", function (newFormat) {
|
||||||
// self.selectFolder(newFormat + '/');
|
// self.selectFolder(newFormat + '/');
|
||||||
// });
|
// });
|
||||||
app.addPopup(PromptPopup, {message: "Folder name:", button: "Create folder", sourceEl: e.currentTarget, callback: function (name) {
|
app.addPopup(PromptPopup, { message: "Folder name:", button: "Create folder", sourceEl: e.currentTarget, callback: function (name) {
|
||||||
name = $.trim(name);
|
name = $.trim(name);
|
||||||
if (name.indexOf('/') >= 0 || name.indexOf('\\') >= 0) {
|
if (name.indexOf('/') >= 0 || name.indexOf('\\') >= 0) {
|
||||||
app.addPopupMessage("Names can't contain slashes, since they're used as a folder separator.");
|
app.addPopupMessage("Names can't contain slashes, since they're used as a folder separator.");
|
||||||
|
|
@ -663,7 +662,7 @@
|
||||||
}
|
}
|
||||||
if (!name) return;
|
if (!name) return;
|
||||||
self.selectFolder(name + '/');
|
self.selectFolder(name + '/');
|
||||||
}});
|
} });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -678,7 +677,7 @@
|
||||||
if (this.curFolder.slice(-1) !== '/') return;
|
if (this.curFolder.slice(-1) !== '/') return;
|
||||||
var oldFolder = this.curFolder.slice(0, -1);
|
var oldFolder = this.curFolder.slice(0, -1);
|
||||||
var self = this;
|
var self = this;
|
||||||
app.addPopup(PromptPopup, {message: "Folder name:", button: "Rename folder", value: oldFolder, callback: function (name) {
|
app.addPopup(PromptPopup, { message: "Folder name:", button: "Rename folder", value: oldFolder, callback: function (name) {
|
||||||
name = $.trim(name);
|
name = $.trim(name);
|
||||||
if (name.indexOf('/') >= 0 || name.indexOf('\\') >= 0) {
|
if (name.indexOf('/') >= 0 || name.indexOf('\\') >= 0) {
|
||||||
app.addPopupMessage("Names can't contain slashes, since they're used as a folder separator.");
|
app.addPopupMessage("Names can't contain slashes, since they're used as a folder separator.");
|
||||||
|
|
@ -698,10 +697,10 @@
|
||||||
}
|
}
|
||||||
if (!window.nodewebkit) Storage.saveTeams();
|
if (!window.nodewebkit) Storage.saveTeams();
|
||||||
self.selectFolder(name + '/');
|
self.selectFolder(name + '/');
|
||||||
}});
|
} });
|
||||||
},
|
},
|
||||||
promptDeleteFolder: function () {
|
promptDeleteFolder: function () {
|
||||||
app.addPopup(DeleteFolderPopup, {folder: this.curFolder, room: this});
|
app.addPopup(DeleteFolderPopup, { folder: this.curFolder, room: this });
|
||||||
},
|
},
|
||||||
deleteFolder: function (format, addName) {
|
deleteFolder: function (format, addName) {
|
||||||
if (format.slice(-1) !== '/') return;
|
if (format.slice(-1) !== '/') return;
|
||||||
|
|
@ -1397,7 +1396,7 @@
|
||||||
evBuf += '<small>−</small>';
|
evBuf += '<small>−</small>';
|
||||||
}
|
}
|
||||||
var width = stats[j] * 75 / 504;
|
var width = stats[j] * 75 / 504;
|
||||||
if (j == 'hp') width = stats[j] * 75 / 704;
|
if (j === 'hp') width = stats[j] * 75 / 704;
|
||||||
if (width > 75) width = 75;
|
if (width > 75) width = 75;
|
||||||
var color = Math.floor(stats[j] * 180 / 714);
|
var color = Math.floor(stats[j] * 180 / 714);
|
||||||
if (color > 360) color = 360;
|
if (color > 360) color = 360;
|
||||||
|
|
@ -1430,7 +1429,7 @@
|
||||||
if (format) self.changeFormat(format.id);
|
if (format) self.changeFormat(format.id);
|
||||||
notes.shift();
|
notes.shift();
|
||||||
}
|
}
|
||||||
var teamNotes = notes.join('\n'); // Not implemented yet
|
// var teamNotes = notes.join('\n'); // Not implemented yet
|
||||||
|
|
||||||
var title = data.title;
|
var title = data.title;
|
||||||
if (title && !title.startsWith('Untitled')) {
|
if (title && !title.startsWith('Untitled')) {
|
||||||
|
|
@ -1595,9 +1594,9 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var self = this;
|
var self = this;
|
||||||
app.addPopup(FormatPopup, {format: format, sourceEl: button, selectType: 'teambuilder', onselect: function (newFormat) {
|
app.addPopup(FormatPopup, { format: format, sourceEl: button, selectType: 'teambuilder', onselect: function (newFormat) {
|
||||||
self.changeFormat(newFormat);
|
self.changeFormat(newFormat);
|
||||||
}});
|
} });
|
||||||
},
|
},
|
||||||
changeFormat: function (format) {
|
changeFormat: function (format) {
|
||||||
this.curTeam.format = format;
|
this.curTeam.format = format;
|
||||||
|
|
@ -1683,7 +1682,7 @@
|
||||||
clipboardExpanded: false,
|
clipboardExpanded: false,
|
||||||
clipboardExpand: function () {
|
clipboardExpand: function () {
|
||||||
var $clipboard = $('.teambuilder-clipboard-data');
|
var $clipboard = $('.teambuilder-clipboard-data');
|
||||||
$clipboard.animate({height: this.clipboardCount() * 34}, 500, function () {
|
$clipboard.animate({ height: this.clipboardCount() * 34 }, 500, function () {
|
||||||
setTimeout(function () { $clipboard.focus(); }, 100);
|
setTimeout(function () { $clipboard.focus(); }, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1693,7 +1692,7 @@
|
||||||
},
|
},
|
||||||
clipboardShrink: function () {
|
clipboardShrink: function () {
|
||||||
var $clipboard = $('.teambuilder-clipboard-data');
|
var $clipboard = $('.teambuilder-clipboard-data');
|
||||||
$clipboard.animate({height: 32}, 500);
|
$clipboard.animate({ height: 32 }, 500);
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
this.clipboardExpanded = false;
|
this.clipboardExpanded = false;
|
||||||
|
|
@ -1725,7 +1724,7 @@
|
||||||
if (this.clipboardCount() === 1) {
|
if (this.clipboardCount() === 1) {
|
||||||
var $clipboard = $('.teambuilder-clipboard-container').css('opacity', 0);
|
var $clipboard = $('.teambuilder-clipboard-container').css('opacity', 0);
|
||||||
$clipboard.slideDown(250, function () {
|
$clipboard.slideDown(250, function () {
|
||||||
$clipboard.animate({opacity: 1}, 250);
|
$clipboard.animate({ opacity: 1 }, 250);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1734,7 +1733,7 @@
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var $clipboard = $('.teambuilder-clipboard-container');
|
var $clipboard = $('.teambuilder-clipboard-container');
|
||||||
$clipboard.animate({opacity: 0}, 250, function () {
|
$clipboard.animate({ opacity: 0 }, 250, function () {
|
||||||
$clipboard.slideUp(250, function () {
|
$clipboard.slideUp(250, function () {
|
||||||
self.clipboardUpdate();
|
self.clipboardUpdate();
|
||||||
});
|
});
|
||||||
|
|
@ -2016,7 +2015,7 @@
|
||||||
if (!set.species) {
|
if (!set.species) {
|
||||||
buf += '<button disabled class="addpokemon" aria-label="Add Pokémon"><i class="fa fa-plus"></i></button> ';
|
buf += '<button disabled class="addpokemon" aria-label="Add Pokémon"><i class="fa fa-plus"></i></button> ';
|
||||||
isAdd = true;
|
isAdd = true;
|
||||||
} else if (i == this.curSetLoc) {
|
} else if (i === this.curSetLoc) {
|
||||||
buf += '<button disabled class="pokemon">' + pokemonicon + BattleLog.escapeHTML(set.name || this.curTeam.dex.species.get(set.species).baseSpecies || '<i class="fa fa-plus"></i>') + '</button> ';
|
buf += '<button disabled class="pokemon">' + pokemonicon + BattleLog.escapeHTML(set.name || this.curTeam.dex.species.get(set.species).baseSpecies || '<i class="fa fa-plus"></i>') + '</button> ';
|
||||||
} else {
|
} else {
|
||||||
buf += '<button name="selectPokemon" value="' + i + '" class="pokemon">' + pokemonicon + BattleLog.escapeHTML(set.name || this.curTeam.dex.species.get(set.species).baseSpecies) + '</button> ';
|
buf += '<button name="selectPokemon" value="' + i + '" class="pokemon">' + pokemonicon + BattleLog.escapeHTML(set.name || this.curTeam.dex.species.get(set.species).baseSpecies) + '</button> ';
|
||||||
|
|
@ -2048,7 +2047,7 @@
|
||||||
var set = this.curSet;
|
var set = this.curSet;
|
||||||
if (!set) return;
|
if (!set) return;
|
||||||
|
|
||||||
var stats = {hp:'', atk:'', def:'', spa:'', spd:'', spe:''};
|
var stats = { hp: '', atk: '', def: '', spa: '', spd: '', spe: '' };
|
||||||
|
|
||||||
var supportsEVs = !this.curTeam.format.includes('letsgo');
|
var supportsEVs = !this.curTeam.format.includes('letsgo');
|
||||||
|
|
||||||
|
|
@ -2066,7 +2065,7 @@
|
||||||
evBuf += '<small>−</small>';
|
evBuf += '<small>−</small>';
|
||||||
}
|
}
|
||||||
var width = stats[stat] * 75 / 504;
|
var width = stats[stat] * 75 / 504;
|
||||||
if (stat == 'hp') width = stats[stat] * 75 / 704;
|
if (stat === 'hp') width = stats[stat] * 75 / 704;
|
||||||
if (width > 75) width = 75;
|
if (width > 75) width = 75;
|
||||||
var color = Math.floor(stats[stat] * 180 / 714);
|
var color = Math.floor(stats[stat] * 180 / 714);
|
||||||
if (color > 360) color = 360;
|
if (color > 360) color = 360;
|
||||||
|
|
@ -2089,7 +2088,7 @@
|
||||||
for (var stat in stats) {
|
for (var stat in stats) {
|
||||||
if (stat === 'spd' && this.curTeam.gen === 1) continue;
|
if (stat === 'spd' && this.curTeam.gen === 1) continue;
|
||||||
var width = stats[stat] * 180 / 504;
|
var width = stats[stat] * 180 / 504;
|
||||||
if (stat == 'hp') width = stats[stat] * 180 / 704;
|
if (stat === 'hp') width = stats[stat] * 180 / 704;
|
||||||
if (width > 179) width = 179;
|
if (width > 179) width = 179;
|
||||||
var color = Math.floor(stats[stat] * 180 / 714);
|
var color = Math.floor(stats[stat] * 180 / 714);
|
||||||
if (color > 360) color = 360;
|
if (color > 360) color = 360;
|
||||||
|
|
@ -2311,7 +2310,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stats = {hp:'', atk:'', def:'', spa:'', spd:'', spe:''};
|
var stats = { hp: '', atk: '', def: '', spa: '', spd: '', spe: '' };
|
||||||
if (this.curTeam.gen === 1) delete stats.spd;
|
if (this.curTeam.gen === 1) delete stats.spd;
|
||||||
if (!set) return;
|
if (!set) return;
|
||||||
var nature = BattleNatures[set.nature || 'Serious'];
|
var nature = BattleNatures[set.nature || 'Serious'];
|
||||||
|
|
@ -2344,7 +2343,7 @@
|
||||||
for (var i in stats) {
|
for (var i in stats) {
|
||||||
stats[i] = this.getStat(i);
|
stats[i] = this.getStat(i);
|
||||||
var width = stats[i] * 180 / 504;
|
var width = stats[i] * 180 / 504;
|
||||||
if (i == 'hp') width = Math.floor(stats[i] * 180 / 704);
|
if (i === 'hp') width = Math.floor(stats[i] * 180 / 704);
|
||||||
if (width > 179) width = 179;
|
if (width > 179) width = 179;
|
||||||
var color = Math.floor(stats[i] * 180 / 714);
|
var color = Math.floor(stats[i] * 180 / 714);
|
||||||
if (color > 360) color = 360;
|
if (color > 360) color = 360;
|
||||||
|
|
@ -2709,7 +2708,7 @@
|
||||||
} else {
|
} else {
|
||||||
var hpTypeX = 0;
|
var hpTypeX = 0;
|
||||||
var i = 1;
|
var i = 1;
|
||||||
var stats = {hp: 31, atk: 31, def: 31, spe: 31, spa: 31, spd: 31};
|
var stats = { hp: 31, atk: 31, def: 31, spe: 31, spa: 31, spd: 31 };
|
||||||
for (var s in stats) {
|
for (var s in stats) {
|
||||||
if (set.ivs[s] === undefined) set.ivs[s] = 31;
|
if (set.ivs[s] === undefined) set.ivs[s] = 31;
|
||||||
hpTypeX += i * (set.ivs[s] % 2);
|
hpTypeX += i * (set.ivs[s] % 2);
|
||||||
|
|
@ -2735,7 +2734,7 @@
|
||||||
var supportsEVs = !this.curTeam.format.includes('letsgo');
|
var supportsEVs = !this.curTeam.format.includes('letsgo');
|
||||||
var supportsAVs = !supportsEVs && this.curTeam.format.endsWith('norestrictions');
|
var supportsAVs = !supportsEVs && this.curTeam.format.endsWith('norestrictions');
|
||||||
if (supportsEVs) {
|
if (supportsEVs) {
|
||||||
while (val > 0 && this.getStat(stat, set, val - 4) == result) val -= 4;
|
while (val > 0 && this.getStat(stat, set, val - 4) === result) val -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportsEVs && !this.ignoreEVLimits && set.evs) {
|
if (supportsEVs && !this.ignoreEVLimits && set.evs) {
|
||||||
|
|
@ -2848,7 +2847,7 @@
|
||||||
if (this.curTeam.gen > 1) {
|
if (this.curTeam.gen > 1) {
|
||||||
buf += '<div class="formrow"><label class="formlabel">Gender:</label><div>';
|
buf += '<div class="formrow"><label class="formlabel">Gender:</label><div>';
|
||||||
if (species.gender && !isHackmons) {
|
if (species.gender && !isHackmons) {
|
||||||
var genderTable = {'M': "Male", 'F': "Female", 'N': "Genderless"};
|
var genderTable = { 'M': "Male", 'F': "Female", 'N': "Genderless" };
|
||||||
buf += genderTable[species.gender];
|
buf += genderTable[species.gender];
|
||||||
} else {
|
} else {
|
||||||
buf += '<label class="checkbox inline"><input type="radio" name="gender" value="M"' + (set.gender === 'M' ? ' checked' : '') + ' /> Male</label> ';
|
buf += '<label class="checkbox inline"><input type="radio" name="gender" value="M"' + (set.gender === 'M' ? ' checked' : '') + ' /> Male</label> ';
|
||||||
|
|
@ -3050,7 +3049,7 @@
|
||||||
i = +$(e.currentTarget).closest('li').attr('value');
|
i = +$(e.currentTarget).closest('li').attr('value');
|
||||||
set = this.curSetList[i];
|
set = this.curSetList[i];
|
||||||
}
|
}
|
||||||
app.addPopup(AltFormPopup, {curSet: set, index: i, room: this});
|
app.addPopup(AltFormPopup, { curSet: set, index: i, room: this });
|
||||||
},
|
},
|
||||||
|
|
||||||
/*********************************************************
|
/*********************************************************
|
||||||
|
|
@ -3125,7 +3124,6 @@
|
||||||
var entry = $firstResult.data('entry');
|
var entry = $firstResult.data('entry');
|
||||||
var val = entry.slice(entry.indexOf("|") + 1);
|
var val = entry.slice(entry.indexOf("|") + 1);
|
||||||
this.chartSet(val, true);
|
this.chartSet(val, true);
|
||||||
return;
|
|
||||||
} else if (e.keyCode === 38) { // up
|
} else if (e.keyCode === 38) { // up
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -3153,7 +3151,6 @@
|
||||||
} else if (e.keyCode === 27 || e.keyCode === 8) { // esc, backspace
|
} else if (e.keyCode === 27 || e.keyCode === 8) { // esc, backspace
|
||||||
if (!e.currentTarget.value && this.search.removeFilter()) {
|
if (!e.currentTarget.value && this.search.removeFilter()) {
|
||||||
this.search.find('');
|
this.search.find('');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else if (e.keyCode === 188) {
|
} else if (e.keyCode === 188) {
|
||||||
var $firstResult = this.$chart.find('a').first();
|
var $firstResult = this.$chart.find('a').first();
|
||||||
|
|
@ -3163,7 +3160,6 @@
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
$(e.currentTarget).val('').select();
|
$(e.currentTarget).val('').select();
|
||||||
this.search.find('');
|
this.search.find('');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -3294,7 +3290,7 @@
|
||||||
set.item = 'Starf Berry';
|
set.item = 'Starf Berry';
|
||||||
set.ability = 'Harvest';
|
set.ability = 'Harvest';
|
||||||
set.moves = ['Substitute', 'Horn Leech', 'Earthquake', 'Phantom Force'];
|
set.moves = ['Substitute', 'Horn Leech', 'Earthquake', 'Phantom Force'];
|
||||||
set.evs = {hp: 36, atk: 252, def: 0, spa: 0, spd: 0, spe: 220};
|
set.evs = { hp: 36, atk: 252, def: 0, spa: 0, spd: 0, spe: 220 };
|
||||||
set.ivs = {};
|
set.ivs = {};
|
||||||
set.nature = 'Jolly';
|
set.nature = 'Jolly';
|
||||||
this.updateSetTop();
|
this.updateSetTop();
|
||||||
|
|
@ -3328,7 +3324,7 @@
|
||||||
set.item = 'Leftovers';
|
set.item = 'Leftovers';
|
||||||
set.ability = 'Battle Armor';
|
set.ability = 'Battle Armor';
|
||||||
set.moves = ['Acupressure', 'Knock Off', 'Rest', 'Sleep Talk'];
|
set.moves = ['Acupressure', 'Knock Off', 'Rest', 'Sleep Talk'];
|
||||||
set.evs = {hp: 248, atk: 0, def: 96, spa: 0, spd: 108, spe: 56};
|
set.evs = { hp: 248, atk: 0, def: 96, spa: 0, spd: 108, spe: 56 };
|
||||||
set.ivs = {};
|
set.ivs = {};
|
||||||
set.nature = 'Impish';
|
set.nature = 'Impish';
|
||||||
this.updateSetTop();
|
this.updateSetTop();
|
||||||
|
|
@ -3428,7 +3424,7 @@
|
||||||
if (!this.canHyperTrain(set)) {
|
if (!this.canHyperTrain(set)) {
|
||||||
var hpType = moveName.substr(13);
|
var hpType = moveName.substr(13);
|
||||||
|
|
||||||
set.ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31};
|
set.ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 };
|
||||||
if (this.curTeam.gen > 2) {
|
if (this.curTeam.gen > 2) {
|
||||||
var HPivs = this.curTeam.dex.types.get(hpType).HPivs;
|
var HPivs = this.curTeam.dex.types.get(hpType).HPivs;
|
||||||
for (var i in HPivs) {
|
for (var i in HPivs) {
|
||||||
|
|
@ -3736,7 +3732,7 @@
|
||||||
buf += '<div style="clear:both"></div>';
|
buf += '<div style="clear:both"></div>';
|
||||||
buf += '</div>';
|
buf += '</div>';
|
||||||
|
|
||||||
this.$el.html(buf).css({'max-width': (4 + spriteSize) * 7});
|
this.$el.html(buf).css({ 'max-width': (4 + spriteSize) * 7 });
|
||||||
},
|
},
|
||||||
setForm: function (form) {
|
setForm: function (form) {
|
||||||
var species = Dex.species.get(this.curSet.species);
|
var species = Dex.species.get(this.curSet.species);
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
var name = $(e.currentTarget).data('name');
|
var name = $(e.currentTarget).data('name');
|
||||||
app.addPopup(UserPopup, {name: name, sourceEl: e.currentTarget});
|
app.addPopup(UserPopup, { name: name, sourceEl: e.currentTarget });
|
||||||
},
|
},
|
||||||
toggleMute: function () {
|
toggleMute: function () {
|
||||||
var muted = !Dex.prefs('mute');
|
var muted = !Dex.prefs('mute');
|
||||||
|
|
@ -224,7 +224,7 @@
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var $target = $(e.currentTarget);
|
var $target = $(e.currentTarget);
|
||||||
if ($target.hasClass('minilogo')) {
|
if ($target.hasClass('minilogo')) {
|
||||||
app.addPopup(TabListPopup, {sourceEl: e.currentTarget});
|
app.addPopup(TabListPopup, { sourceEl: e.currentTarget });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var id = $target.attr('href');
|
var id = $target.attr('href');
|
||||||
|
|
@ -543,7 +543,7 @@
|
||||||
"हिंदी": 'hindi',
|
"हिंदी": 'hindi',
|
||||||
"日本語": 'japanese',
|
"日本語": 'japanese',
|
||||||
"简体中文": 'simplifiedchinese',
|
"简体中文": 'simplifiedchinese',
|
||||||
"中文": 'traditionalchinese',
|
"中文": 'traditionalchinese'
|
||||||
};
|
};
|
||||||
buf += '<p><label class="optlabel">Language: <select name="language" class="button">';
|
buf += '<p><label class="optlabel">Language: <select name="language" class="button">';
|
||||||
for (var name in possibleLanguages) {
|
for (var name in possibleLanguages) {
|
||||||
|
|
@ -877,7 +877,7 @@
|
||||||
popup.$('.cur').removeClass('cur');
|
popup.$('.cur').removeClass('cur');
|
||||||
Storage.bg.set(e.target.result, 'custom');
|
Storage.bg.set(e.target.result, 'custom');
|
||||||
} else {
|
} else {
|
||||||
app.addPopup(ConfirmBackgroundPopup, {bgUrl: e.target.result});
|
app.addPopup(ConfirmBackgroundPopup, { bgUrl: e.target.result });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* exported toId */
|
||||||
function toId() {
|
function toId() {
|
||||||
// toId has been renamed toID
|
// toId has been renamed toID
|
||||||
alert("You have an old extension/script for Pokemon Showdown which is incompatible with this client. It needs to be removed or updated.");
|
alert("You have an old extension/script for Pokemon Showdown which is incompatible with this client. It needs to be removed or updated.");
|
||||||
|
|
@ -20,7 +21,7 @@ function toId() {
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).on('keydown', function (e) {
|
$(document).on('keydown', function (e) {
|
||||||
if (e.keyCode == 27) { // Esc
|
if (e.keyCode === 27) { // Esc
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
|
|
@ -178,8 +179,8 @@ function toId() {
|
||||||
Storage.prefs('serversettings', self.get('settings'));
|
Storage.prefs('serversettings', self.get('settings'));
|
||||||
});
|
});
|
||||||
|
|
||||||
var replaceList = {'A': 'AⱯȺ', 'B': 'BƂƁɃ', 'C': 'CꜾȻ', 'D': 'DĐƋƊƉꝹ', 'E': 'EƐƎ', 'F': 'FƑꝻ', 'G': 'GꞠꝽꝾ', 'H': 'HĦⱧⱵꞍ', 'I': 'IƗ', 'J': 'JɈ', 'K': 'KꞢ', 'L': 'LꝆꞀ', 'M': 'MⱮƜ', 'N': 'NȠƝꞐꞤ', 'O': 'OǪǬØǾƆƟꝊꝌ', 'P': 'PƤⱣꝐꝒꝔ', 'Q': 'QꝖꝘɊ', 'R': 'RɌⱤꝚꞦꞂ', 'S': 'SẞꞨꞄ', 'T': 'TŦƬƮȾꞆ', 'U': 'UɄ', 'V': 'VƲꝞɅ', 'W': 'WⱲ', 'X': 'X', 'Y': 'YɎỾ', 'Z': 'ZƵȤⱿⱫꝢ', 'a': 'aąⱥɐ', 'b': 'bƀƃɓ', 'c': 'cȼꜿↄ', 'd': 'dđƌɖɗꝺ', 'e': 'eɇɛǝ', 'f': 'fḟƒꝼ', 'g': 'gɠꞡᵹꝿ', 'h': 'hħⱨⱶɥ', 'i': 'iɨı', 'j': 'jɉ', 'k': 'kƙⱪꝁꝃꝅꞣ', 'l': 'lſłƚɫⱡꝉꞁꝇ', 'm': 'mɱɯ', 'n': 'nƞɲʼnꞑꞥ', 'o': 'oǫǭøǿɔꝋꝍɵ', 'p': 'pƥᵽꝑꝓꝕ', 'q': 'qɋꝗꝙ', 'r': 'rɍɽꝛꞧꞃ', 's': 'sꞩꞅẛ', 't': 'tŧƭʈⱦꞇ', 'u': 'uưừứữửựųṷṵʉ', 'v': 'vʋꝟʌ', 'w': 'wⱳ', 'x': 'x', 'y': 'yɏỿ', 'z': 'zƶȥɀⱬꝣ', 'AA': 'Ꜳ', 'AE': 'ÆǼǢ', 'AO': 'Ꜵ', 'AU': 'Ꜷ', 'AV': 'ꜸꜺ', 'AY': 'Ꜽ', 'DZ': 'DZDŽ', 'Dz': 'DzDž', 'LJ': 'LJ', 'Lj': 'Lj', 'NJ': 'NJ', 'Nj': 'Nj', 'OI': 'Ƣ', 'OO': 'Ꝏ', 'OU': 'Ȣ', 'TZ': 'Ꜩ', 'VY': 'Ꝡ', 'aa': 'ꜳ', 'ae': 'æǽǣ', 'ao': 'ꜵ', 'au': 'ꜷ', 'av': 'ꜹꜻ', 'ay': 'ꜽ', 'dz': 'dzdž', 'hv': 'ƕ', 'lj': 'lj', 'nj': 'nj', 'oi': 'ƣ', 'ou': 'ȣ', 'oo': 'ꝏ', 'ss': 'ß', 'tz': 'ꜩ', 'vy': 'ꝡ'};
|
var replaceList = { 'A': 'AⱯȺ', 'B': 'BƂƁɃ', 'C': 'CꜾȻ', 'D': 'DĐƋƊƉꝹ', 'E': 'EƐƎ', 'F': 'FƑꝻ', 'G': 'GꞠꝽꝾ', 'H': 'HĦⱧⱵꞍ', 'I': 'IƗ', 'J': 'JɈ', 'K': 'KꞢ', 'L': 'LꝆꞀ', 'M': 'MⱮƜ', 'N': 'NȠƝꞐꞤ', 'O': 'OǪǬØǾƆƟꝊꝌ', 'P': 'PƤⱣꝐꝒꝔ', 'Q': 'QꝖꝘɊ', 'R': 'RɌⱤꝚꞦꞂ', 'S': 'SẞꞨꞄ', 'T': 'TŦƬƮȾꞆ', 'U': 'UɄ', 'V': 'VƲꝞɅ', 'W': 'WⱲ', 'X': 'X', 'Y': 'YɎỾ', 'Z': 'ZƵȤⱿⱫꝢ', 'a': 'aąⱥɐ', 'b': 'bƀƃɓ', 'c': 'cȼꜿↄ', 'd': 'dđƌɖɗꝺ', 'e': 'eɇɛǝ', 'f': 'fḟƒꝼ', 'g': 'gɠꞡᵹꝿ', 'h': 'hħⱨⱶɥ', 'i': 'iɨı', 'j': 'jɉ', 'k': 'kƙⱪꝁꝃꝅꞣ', 'l': 'lſłƚɫⱡꝉꞁꝇ', 'm': 'mɱɯ', 'n': 'nƞɲʼnꞑꞥ', 'o': 'oǫǭøǿɔꝋꝍɵ', 'p': 'pƥᵽꝑꝓꝕ', 'q': 'qɋꝗꝙ', 'r': 'rɍɽꝛꞧꞃ', 's': 'sꞩꞅẛ', 't': 'tŧƭʈⱦꞇ', 'u': 'uưừứữửựųṷṵʉ', 'v': 'vʋꝟʌ', 'w': 'wⱳ', 'x': 'x', 'y': 'yɏỿ', 'z': 'zƶȥɀⱬꝣ', 'AA': 'Ꜳ', 'AE': 'ÆǼǢ', 'AO': 'Ꜵ', 'AU': 'Ꜷ', 'AV': 'ꜸꜺ', 'AY': 'Ꜽ', 'DZ': 'DZDŽ', 'Dz': 'DzDž', 'LJ': 'LJ', 'Lj': 'Lj', 'NJ': 'NJ', 'Nj': 'Nj', 'OI': 'Ƣ', 'OO': 'Ꝏ', 'OU': 'Ȣ', 'TZ': 'Ꜩ', 'VY': 'Ꝡ', 'aa': 'ꜳ', 'ae': 'æǽǣ', 'ao': 'ꜵ', 'au': 'ꜷ', 'av': 'ꜹꜻ', 'ay': 'ꜽ', 'dz': 'dzdž', 'hv': 'ƕ', 'lj': 'lj', 'nj': 'nj', 'oi': 'ƣ', 'ou': 'ȣ', 'oo': 'ꝏ', 'ss': 'ß', 'tz': 'ꜩ', 'vy': 'ꝡ' };
|
||||||
var normalizeList = {'A': 'ÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄ', 'B': 'ḂḄḆ', 'C': 'ĆĈĊČÇḈƇ', 'D': 'ḊĎḌḐḒḎ', 'E': 'ÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚ', 'F': 'Ḟ', 'G': 'ǴĜḠĞĠǦĢǤƓ', 'H': 'ĤḢḦȞḤḨḪ', 'I': 'ÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬ', 'J': 'Ĵ', 'K': 'ḰǨḲĶḴƘⱩꝀꝂꝄ', 'L': 'ĿĹĽḶḸĻḼḺŁȽⱢⱠꝈ', 'M': 'ḾṀṂ', 'N': 'ǸŃÑṄŇṆŅṊṈ', 'O': 'ÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘ', 'P': 'ṔṖ', 'Q': '', 'R': 'ŔṘŘȐȒṚṜŖṞ', 'S': 'ŚṤŜṠŠṦṢṨȘŞⱾ', 'T': 'ṪŤṬȚŢṰṮ', 'U': 'ÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴ', 'V': 'ṼṾ', 'W': 'ẀẂŴẆẄẈ', 'X': 'ẊẌ', 'Y': 'ỲÝŶỸȲẎŸỶỴƳ', 'Z': 'ŹẐŻŽẒẔ', 'a': 'ẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁ', 'b': 'ḃḅḇ', 'c': 'ćĉċčçḉƈ', 'd': 'ḋďḍḑḓḏ', 'e': 'èéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛ', 'f': '', 'g': 'ǵĝḡğġǧģǥ', 'h': 'ĥḣḧȟḥḩḫẖ', 'i': 'ìíîĩīĭïḯỉǐȉȋịįḭ', 'j': 'ĵǰ', 'k': 'ḱǩḳķḵ', 'l': 'ŀĺľḷḹļḽḻ', 'm': 'ḿṁṃ', 'n': 'ǹńñṅňṇņṋṉ', 'o': 'òóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộ', 'p': 'ṕṗ', 'q': '', 'r': 'ŕṙřȑȓṛṝŗṟ', 's': 'śṥŝṡšṧṣṩșşȿ', 't': 'ṫẗťṭțţṱṯ', 'u': 'ùúûũṹūṻŭüǜǘǖǚủůűǔȕȗụṳ', 'v': 'ṽṿ', 'w': 'ẁẃŵẇẅẘẉ', 'x': 'ẋẍ', 'y': 'ỳýŷỹȳẏÿỷẙỵƴ', 'z': 'źẑżžẓẕ'};
|
var normalizeList = { 'A': 'ÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄ', 'B': 'ḂḄḆ', 'C': 'ĆĈĊČÇḈƇ', 'D': 'ḊĎḌḐḒḎ', 'E': 'ÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚ', 'F': 'Ḟ', 'G': 'ǴĜḠĞĠǦĢǤƓ', 'H': 'ĤḢḦȞḤḨḪ', 'I': 'ÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬ', 'J': 'Ĵ', 'K': 'ḰǨḲĶḴƘⱩꝀꝂꝄ', 'L': 'ĿĹĽḶḸĻḼḺŁȽⱢⱠꝈ', 'M': 'ḾṀṂ', 'N': 'ǸŃÑṄŇṆŅṊṈ', 'O': 'ÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘ', 'P': 'ṔṖ', 'Q': '', 'R': 'ŔṘŘȐȒṚṜŖṞ', 'S': 'ŚṤŜṠŠṦṢṨȘŞⱾ', 'T': 'ṪŤṬȚŢṰṮ', 'U': 'ÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴ', 'V': 'ṼṾ', 'W': 'ẀẂŴẆẄẈ', 'X': 'ẊẌ', 'Y': 'ỲÝŶỸȲẎŸỶỴƳ', 'Z': 'ŹẐŻŽẒẔ', 'a': 'ẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁ', 'b': 'ḃḅḇ', 'c': 'ćĉċčçḉƈ', 'd': 'ḋďḍḑḓḏ', 'e': 'èéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛ', 'f': '', 'g': 'ǵĝḡğġǧģǥ', 'h': 'ĥḣḧȟḥḩḫẖ', 'i': 'ìíîĩīĭïḯỉǐȉȋịįḭ', 'j': 'ĵǰ', 'k': 'ḱǩḳķḵ', 'l': 'ŀĺľḷḹļḽḻ', 'm': 'ḿṁṃ', 'n': 'ǹńñṅňṇņṋṉ', 'o': 'òóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộ', 'p': 'ṕṗ', 'q': '', 'r': 'ŕṙřȑȓṛṝŗṟ', 's': 'śṥŝṡšṧṣṩșşȿ', 't': 'ṫẗťṭțţṱṯ', 'u': 'ùúûũṹūṻŭüǜǘǖǚủůűǔȕȗụṳ', 'v': 'ṽṿ', 'w': 'ẁẃŵẇẅẘẉ', 'x': 'ẋẍ', 'y': 'ỳýŷỹȳẏÿỷẙỵƴ', 'z': 'źẑżžẓẕ' };
|
||||||
for (var i in replaceList) {
|
for (var i in replaceList) {
|
||||||
replaceList[i] = new RegExp('[' + replaceList[i] + ']', 'g');
|
replaceList[i] = new RegExp('[' + replaceList[i] + ']', 'g');
|
||||||
}
|
}
|
||||||
|
|
@ -409,7 +410,7 @@ function toId() {
|
||||||
// this.down = true;
|
// this.down = true;
|
||||||
|
|
||||||
this.addRoom('');
|
this.addRoom('');
|
||||||
this.topbar = new Topbar({el: $('#header')});
|
this.topbar = new Topbar({ el: $('#header') });
|
||||||
if (this.down) {
|
if (this.down) {
|
||||||
this.isDisconnected = true;
|
this.isDisconnected = true;
|
||||||
// } else if (location.origin === 'http://smogtours.psim.us') {
|
// } else if (location.origin === 'http://smogtours.psim.us') {
|
||||||
|
|
@ -427,7 +428,7 @@ function toId() {
|
||||||
Storage.whenPrefsLoaded(function () {
|
Storage.whenPrefsLoaded(function () {
|
||||||
if (!Config.server.registered) {
|
if (!Config.server.registered) {
|
||||||
app.send('/autojoin');
|
app.send('/autojoin');
|
||||||
Backbone.history.start({pushState: !Config.testclient});
|
Backbone.history.start({ pushState: !Config.testclient });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Support legacy tournament setting and migrate to new pref
|
// Support legacy tournament setting and migrate to new pref
|
||||||
|
|
@ -459,7 +460,7 @@ function toId() {
|
||||||
if (Object.keys(settings).length) app.user.set('settings', settings);
|
if (Object.keys(settings).length) app.user.set('settings', settings);
|
||||||
// HTML5 history throws exceptions when running on file://
|
// HTML5 history throws exceptions when running on file://
|
||||||
var useHistory = !Config.testclient && (location.pathname.slice(-5) !== '.html');
|
var useHistory = !Config.testclient && (location.pathname.slice(-5) !== '.html');
|
||||||
Backbone.history.start({pushState: useHistory});
|
Backbone.history.start({ pushState: useHistory });
|
||||||
app.ignore = app.loadIgnore();
|
app.ignore = app.loadIgnore();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -539,21 +540,21 @@ function toId() {
|
||||||
$('.battle-log-add').html('<small>You are disconnected and cannot chat.</small>');
|
$('.battle-log-add').html('<small>You are disconnected and cannot chat.</small>');
|
||||||
|
|
||||||
self.reconnectPending = (message || true);
|
self.reconnectPending = (message || true);
|
||||||
if (!self.popups.length) self.addPopup(ReconnectPopup, {message: message});
|
if (!self.popups.length) self.addPopup(ReconnectPopup, { message: message });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('init:connectionerror', function () {
|
this.on('init:connectionerror', function () {
|
||||||
self.isDisconnected = true;
|
self.isDisconnected = true;
|
||||||
self.rooms[''].updateFormats();
|
self.rooms[''].updateFormats();
|
||||||
self.addPopup(ReconnectPopup, {cantconnect: true});
|
self.addPopup(ReconnectPopup, { cantconnect: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.user.on('login:invalidname', function (name, reason) {
|
this.user.on('login:invalidname', function (name, reason) {
|
||||||
self.addPopup(LoginPopup, {name: name, reason: reason});
|
self.addPopup(LoginPopup, { name: name, reason: reason });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.user.on('login:authrequired', function (name, special) {
|
this.user.on('login:authrequired', function (name, special) {
|
||||||
self.addPopup(LoginPasswordPopup, {username: name, special: special});
|
self.addPopup(LoginPasswordPopup, { username: name, special: special });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('loggedin', function () {
|
this.on('loggedin', function () {
|
||||||
|
|
@ -790,7 +791,7 @@ function toId() {
|
||||||
}
|
}
|
||||||
return new SockJS(
|
return new SockJS(
|
||||||
protocol + '://' + Config.server.host + ':' + Config.server.port + Config.sockjsprefix,
|
protocol + '://' + Config.server.host + ':' + Config.server.port + Config.sockjsprefix,
|
||||||
[], {timeout: 5 * 60 * 1000}
|
[], { timeout: 5 * 60 * 1000 }
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// The most common case this happens is if an HTTPS connection fails,
|
// The most common case this happens is if an HTTPS connection fails,
|
||||||
|
|
@ -942,7 +943,7 @@ function toId() {
|
||||||
this.loadingTeam = true;
|
this.loadingTeam = true;
|
||||||
$.get(app.user.getActionPHP(), {
|
$.get(app.user.getActionPHP(), {
|
||||||
act: 'getteam',
|
act: 'getteam',
|
||||||
teamid: team.teamid,
|
teamid: team.teamid
|
||||||
}, Storage.safeJSON(function (data) {
|
}, Storage.safeJSON(function (data) {
|
||||||
app.loadingTeam = false;
|
app.loadingTeam = false;
|
||||||
if (data.actionerror) {
|
if (data.actionerror) {
|
||||||
|
|
@ -1041,7 +1042,7 @@ function toId() {
|
||||||
var replayid = roomid.slice(7);
|
var replayid = roomid.slice(7);
|
||||||
if (Config.server.id !== 'showdown') replayid = Config.server.id + '-' + replayid;
|
if (Config.server.id !== 'showdown') replayid = Config.server.id + '-' + replayid;
|
||||||
var replayLink = 'https://' + Config.routes.replays + '/' + replayid;
|
var replayLink = 'https://' + Config.routes.replays + '/' + replayid;
|
||||||
$.ajax(replayLink + '.json', {dataType: 'json'}).done(function (replay) {
|
$.ajax(replayLink + '.json', { dataType: 'json' }).done(function (replay) {
|
||||||
if (replay) {
|
if (replay) {
|
||||||
var title = replay.players[0] + ' vs. ' + replay.players[1];
|
var title = replay.players[0] + ' vs. ' + replay.players[1];
|
||||||
app.receive('>battle-' + replayid + '\n|init|battle\n|title|' + title + '\n' + replay.log);
|
app.receive('>battle-' + replayid + '\n|init|battle\n|title|' + title + '\n' + replay.log);
|
||||||
|
|
@ -1169,7 +1170,7 @@ function toId() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'nametaken':
|
case 'nametaken':
|
||||||
app.addPopup(LoginPopup, {name: parts[1] || '', error: parts[2] || ''});
|
app.addPopup(LoginPopup, { name: parts[1] || '', error: parts[2] || '' });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'queryresponse':
|
case 'queryresponse':
|
||||||
|
|
@ -1258,7 +1259,7 @@ function toId() {
|
||||||
case 'chat':
|
case 'chat':
|
||||||
if (parts[1] === '~') {
|
if (parts[1] === '~') {
|
||||||
if (parts[2].substr(0, 6) === '/warn ') {
|
if (parts[2].substr(0, 6) === '/warn ') {
|
||||||
app.addPopup(RulesPopup, {warning: parts[2].substr(6)});
|
app.addPopup(RulesPopup, { warning: parts[2].substr(6) });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1322,8 +1323,8 @@ function toId() {
|
||||||
var column = 0;
|
var column = 0;
|
||||||
var columnChanged = false;
|
var columnChanged = false;
|
||||||
|
|
||||||
window.NonBattleGames = {rps: 'Rock Paper Scissors'};
|
window.NonBattleGames = { rps: 'Rock Paper Scissors' };
|
||||||
for (var i = 3; i <= 9; i = i + 2) {
|
for (var i = 3; i <= 9; i += 2) {
|
||||||
window.NonBattleGames['bestof' + i] = 'Best-of-' + i;
|
window.NonBattleGames['bestof' + i] = 'Best-of-' + i;
|
||||||
}
|
}
|
||||||
window.BattleFormats = {};
|
window.BattleFormats = {};
|
||||||
|
|
@ -1473,7 +1474,7 @@ function toId() {
|
||||||
if (silent) return;
|
if (silent) return;
|
||||||
var sData = data.split(':');
|
var sData = data.split(':');
|
||||||
if (sData[0] === 'success') {
|
if (sData[0] === 'success') {
|
||||||
app.addPopup(ReplayUploadedPopup, {id: sData[1] || id});
|
app.addPopup(ReplayUploadedPopup, { id: sData[1] || id });
|
||||||
} else if (data === 'hash mismatch') {
|
} else if (data === 'hash mismatch') {
|
||||||
app.addPopupMessage("Someone else is already uploading a replay of this battle. Try again in five seconds.");
|
app.addPopupMessage("Someone else is already uploading a replay of this battle. Try again in five seconds.");
|
||||||
} else if (data === 'not found') {
|
} else if (data === 'not found') {
|
||||||
|
|
@ -1603,7 +1604,7 @@ function toId() {
|
||||||
*/
|
*/
|
||||||
unjoinRoom: function (id, reason) {
|
unjoinRoom: function (id, reason) {
|
||||||
this.removeRoom(id, true);
|
this.removeRoom(id, true);
|
||||||
if (this.curRoom) this.navigate(this.curRoom.id, {replace: true});
|
if (this.curRoom) this.navigate(this.curRoom.id, { replace: true });
|
||||||
this.updateAutojoin();
|
this.updateAutojoin();
|
||||||
},
|
},
|
||||||
tryJoinRoom: function (id) {
|
tryJoinRoom: function (id) {
|
||||||
|
|
@ -1724,7 +1725,6 @@ function toId() {
|
||||||
}
|
}
|
||||||
|
|
||||||
room.focus(null, focusTextbox);
|
room.focus(null, focusTextbox);
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
focusRoomLeft: function (id) {
|
focusRoomLeft: function (id) {
|
||||||
var room = this.rooms[id];
|
var room = this.rooms[id];
|
||||||
|
|
@ -1750,7 +1750,6 @@ function toId() {
|
||||||
if (this.curRoom.id === id) this.navigate(id);
|
if (this.curRoom.id === id) this.navigate(id);
|
||||||
|
|
||||||
room.focus(null, true);
|
room.focus(null, true);
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
focusRoomRight: function (id) {
|
focusRoomRight: function (id) {
|
||||||
var room = this.rooms[id];
|
var room = this.rooms[id];
|
||||||
|
|
@ -1774,7 +1773,6 @@ function toId() {
|
||||||
// if (this.curRoom.id === id) this.navigate(id);
|
// if (this.curRoom.id === id) this.navigate(id);
|
||||||
|
|
||||||
room.focus(null, true);
|
room.focus(null, true);
|
||||||
return;
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* This is the function for handling the two-panel layout
|
* This is the function for handling the two-panel layout
|
||||||
|
|
@ -2068,7 +2066,7 @@ function toId() {
|
||||||
} else {
|
} else {
|
||||||
if (Config.server.id !== 'showdown') {
|
if (Config.server.id !== 'showdown') {
|
||||||
// Switch to the autojoin object to handle multiple servers
|
// Switch to the autojoin object to handle multiple servers
|
||||||
curAutojoin = {showdown: curAutojoin};
|
curAutojoin = { showdown: curAutojoin };
|
||||||
if (!autojoins.length) return;
|
if (!autojoins.length) return;
|
||||||
curAutojoin[Config.server.id] = autojoins.join(',');
|
curAutojoin[Config.server.id] = autojoins.join(',');
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2142,19 +2140,19 @@ function toId() {
|
||||||
addPopupMessage: function (message) {
|
addPopupMessage: function (message) {
|
||||||
// shorthand for adding a popup message
|
// shorthand for adding a popup message
|
||||||
// this is the equivalent of alert(message)
|
// this is the equivalent of alert(message)
|
||||||
app.addPopup(Popup, {message: message});
|
app.addPopup(Popup, { message: message });
|
||||||
},
|
},
|
||||||
addPopupPrompt: function (message, buttonOrCallback, callback) {
|
addPopupPrompt: function (message, buttonOrCallback, callback) {
|
||||||
var button = (callback ? buttonOrCallback : 'OK');
|
var button = (callback ? buttonOrCallback : 'OK');
|
||||||
callback = (!callback ? buttonOrCallback : callback);
|
callback = (!callback ? buttonOrCallback : callback);
|
||||||
app.addPopup(PromptPopup, {message: message, button: button, callback: callback});
|
app.addPopup(PromptPopup, { message: message, button: button, callback: callback });
|
||||||
},
|
},
|
||||||
closePopup: function (id) {
|
closePopup: function (id) {
|
||||||
if (this.popups.length) {
|
if (this.popups.length) {
|
||||||
var popup = this.popups.pop();
|
var popup = this.popups.pop();
|
||||||
if (popup.lastFocusedEl && popup.lastFocusedEl.focus) popup.lastFocusedEl.focus();
|
if (popup.lastFocusedEl && popup.lastFocusedEl.focus) popup.lastFocusedEl.focus();
|
||||||
popup.remove();
|
popup.remove();
|
||||||
if (this.reconnectPending) this.addPopup(ReconnectPopup, {message: this.reconnectPending});
|
if (this.reconnectPending) this.addPopup(ReconnectPopup, { message: this.reconnectPending });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2229,9 +2227,9 @@ function toId() {
|
||||||
*/
|
*/
|
||||||
selectformat: function (value, target) {
|
selectformat: function (value, target) {
|
||||||
var format = value || 'gen9randombattle';
|
var format = value || 'gen9randombattle';
|
||||||
app.addPopup(FormatPopup, {format: format, sourceEl: target, selectType: 'watch', onselect: function (newFormat) {
|
app.addPopup(FormatPopup, { format: format, sourceEl: target, selectType: 'watch', onselect: function (newFormat) {
|
||||||
target.value = newFormat;
|
target.value = newFormat;
|
||||||
}});
|
} });
|
||||||
},
|
},
|
||||||
|
|
||||||
copyText: function (value, target) {
|
copyText: function (value, target) {
|
||||||
|
|
@ -2256,14 +2254,14 @@ function toId() {
|
||||||
this.leftWidth = 0;
|
this.leftWidth = 0;
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case 'left':
|
case 'left':
|
||||||
this.$el.css({left: 0, width: leftWidth, right: 'auto'});
|
this.$el.css({ left: 0, width: leftWidth, right: 'auto' });
|
||||||
break;
|
break;
|
||||||
case 'right':
|
case 'right':
|
||||||
this.$el.css({left: leftWidth + 1, width: 'auto', right: 0});
|
this.$el.css({ left: leftWidth + 1, width: 'auto', right: 0 });
|
||||||
this.leftWidth = leftWidth;
|
this.leftWidth = leftWidth;
|
||||||
break;
|
break;
|
||||||
case 'full':
|
case 'full':
|
||||||
this.$el.css({left: 0, width: 'auto', right: 0});
|
this.$el.css({ left: 0, width: 'auto', right: 0 });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.$el.show();
|
this.$el.show();
|
||||||
|
|
@ -2298,14 +2296,14 @@ function toId() {
|
||||||
notifications: null,
|
notifications: null,
|
||||||
subtleNotification: false,
|
subtleNotification: false,
|
||||||
notify: function (title, body, tag, once) {
|
notify: function (title, body, tag, once) {
|
||||||
if (once && app.focused && (this === app.curRoom || this == app.curSideRoom)) return;
|
if (once && app.focused && (this === app.curRoom || this === app.curSideRoom)) return;
|
||||||
if (!tag) tag = 'message';
|
if (!tag) tag = 'message';
|
||||||
var needsTabbarUpdate = false;
|
var needsTabbarUpdate = false;
|
||||||
if (!this.notifications) {
|
if (!this.notifications) {
|
||||||
this.notifications = {};
|
this.notifications = {};
|
||||||
needsTabbarUpdate = true;
|
needsTabbarUpdate = true;
|
||||||
}
|
}
|
||||||
if (app.focused && (this === app.curRoom || this == app.curSideRoom)) {
|
if (app.focused && (this === app.curRoom || this === app.curSideRoom)) {
|
||||||
this.notifications[tag] = {};
|
this.notifications[tag] = {};
|
||||||
} else if (window.nodewebkit && !nwWindow.setBadgeLabel) {
|
} else if (window.nodewebkit && !nwWindow.setBadgeLabel) {
|
||||||
// old desktop client
|
// old desktop client
|
||||||
|
|
@ -2330,9 +2328,9 @@ function toId() {
|
||||||
};
|
};
|
||||||
if (Dex.prefs('temporarynotifications')) {
|
if (Dex.prefs('temporarynotifications')) {
|
||||||
if (notification.cancel) {
|
if (notification.cancel) {
|
||||||
setTimeout(function () {notification.cancel();}, 5000);
|
setTimeout(function () { notification.cancel(); }, 5000);
|
||||||
} else if (notification.close) {
|
} else if (notification.close) {
|
||||||
setTimeout(function () {notification.close();}, 5000);
|
setTimeout(function () { notification.close(); }, 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (once) notification.psAutoclose = true;
|
if (once) notification.psAutoclose = true;
|
||||||
|
|
@ -2359,7 +2357,7 @@ function toId() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
subtleNotifyOnce: function () {
|
subtleNotifyOnce: function () {
|
||||||
if (app.focused && (this === app.curRoom || this == app.curSideRoom)) return;
|
if (app.focused && (this === app.curRoom || this === app.curSideRoom)) return;
|
||||||
if (this.notifications || this.subtleNotification) return;
|
if (this.notifications || this.subtleNotification) return;
|
||||||
this.subtleNotification = true;
|
this.subtleNotification = true;
|
||||||
this.notificationClass = ' subtle-notifying';
|
this.notificationClass = ' subtle-notifying';
|
||||||
|
|
@ -2629,7 +2627,7 @@ function toId() {
|
||||||
} else {
|
} else {
|
||||||
app.addPopupMessage("You are already registered!");
|
app.addPopupMessage("You are already registered!");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var PromptPopup = this.PromptPopup = Popup.extend({
|
var PromptPopup = this.PromptPopup = Popup.extend({
|
||||||
|
|
@ -2872,7 +2870,7 @@ function toId() {
|
||||||
app.addPopup(UserOptionsPopup, {
|
app.addPopup(UserOptionsPopup, {
|
||||||
name: this.data.name,
|
name: this.data.name,
|
||||||
userid: this.data.userid,
|
userid: this.data.userid,
|
||||||
friended: this.data.friended,
|
friended: this.data.friended
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,8 @@ if (!Array.prototype.filter) {
|
||||||
}
|
}
|
||||||
// ES2016, predates nomodule
|
// ES2016, predates nomodule
|
||||||
if (!Array.prototype.includes) {
|
if (!Array.prototype.includes) {
|
||||||
Array.prototype.includes = function includes(thing) {
|
Array.prototype.includes = function includes(thing, offset) {
|
||||||
return this.indexOf(thing) !== -1;
|
return this.indexOf(thing, offset) !== -1;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// ES5
|
// ES5
|
||||||
|
|
@ -61,8 +61,8 @@ if (!Array.isArray) {
|
||||||
}
|
}
|
||||||
// ES6
|
// ES6
|
||||||
if (!String.prototype.includes) {
|
if (!String.prototype.includes) {
|
||||||
String.prototype.includes = function includes(thing) {
|
String.prototype.includes = function includes(thing, offset) {
|
||||||
return this.indexOf(thing) !== -1;
|
return this.indexOf(thing, offset) !== -1;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// ES6
|
// ES6
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ var Replays = {
|
||||||
log: log.split('\n'),
|
log: log.split('\n'),
|
||||||
isReplay: true,
|
isReplay: true,
|
||||||
paused: true,
|
paused: true,
|
||||||
autoresize: true,
|
autoresize: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$('.replay-controls-2').html('<div class="chooser leftchooser speedchooser"> <em>Speed:</em> <div><button value="hyperfast">Hyperfast</button><button value="fast">Fast</button><button value="normal" class="sel">Normal</button><button value="slow">Slow</button><button value="reallyslow">Really Slow</button></div> </div> <div class="chooser colorchooser"> <em>Color scheme:</em> <div><button class="sel" value="light">Light</button><button value="dark">Dark</button></div> </div> <div class="chooser soundchooser" style="display:none"> <em>Music:</em> <div><button class="sel" value="on">On</button><button value="off">Off</button></div> </div>');
|
this.$('.replay-controls-2').html('<div class="chooser leftchooser speedchooser"> <em>Speed:</em> <div><button value="hyperfast">Hyperfast</button><button value="fast">Fast</button><button value="normal" class="sel">Normal</button><button value="slow">Slow</button><button value="reallyslow">Really Slow</button></div> </div> <div class="chooser colorchooser"> <em>Color scheme:</em> <div><button class="sel" value="light">Light</button><button value="dark">Dark</button></div> </div> <div class="chooser soundchooser" style="display:none"> <em>Music:</em> <div><button class="sel" value="on">On</button><button value="off">Off</button></div> </div>');
|
||||||
|
|
@ -202,7 +202,7 @@ var Replays = {
|
||||||
},
|
},
|
||||||
switchViewpoint: function () {
|
switchViewpoint: function () {
|
||||||
this.battle.switchViewpoint();
|
this.battle.switchViewpoint();
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@
|
||||||
var ability = this.engine.dex.abilities.get(id);
|
var ability = this.engine.dex.abilities.get(id);
|
||||||
return this.renderAbilityRow(ability, matchStart, matchLength, errorMessage, attrs);
|
return this.renderAbilityRow(ability, matchStart, matchLength, errorMessage, attrs);
|
||||||
case 'type':
|
case 'type':
|
||||||
var type = {name: id[0].toUpperCase() + id.substr(1)};
|
var type = { name: id[0].toUpperCase() + id.substr(1) };
|
||||||
return this.renderTypeRow(type, matchStart, matchLength, errorMessage);
|
return this.renderTypeRow(type, matchStart, matchLength, errorMessage);
|
||||||
case 'egggroup':
|
case 'egggroup':
|
||||||
// very hardcode
|
// very hardcode
|
||||||
|
|
@ -223,7 +223,7 @@
|
||||||
} else {
|
} else {
|
||||||
egName = id[0].toUpperCase() + id.substr(1);
|
egName = id[0].toUpperCase() + id.substr(1);
|
||||||
}
|
}
|
||||||
var egggroup = {name: egName};
|
var egggroup = { name: egName };
|
||||||
return this.renderEggGroupRow(egggroup, matchStart, matchLength, errorMessage);
|
return this.renderEggGroupRow(egggroup, matchStart, matchLength, errorMessage);
|
||||||
case 'tier':
|
case 'tier':
|
||||||
// very hardcode
|
// very hardcode
|
||||||
|
|
@ -246,14 +246,14 @@
|
||||||
publ: "PUBL",
|
publ: "PUBL",
|
||||||
zubl: "ZUBL"
|
zubl: "ZUBL"
|
||||||
};
|
};
|
||||||
var tier = {name: tierTable[id]};
|
var tier = { name: tierTable[id] };
|
||||||
return this.renderTierRow(tier, matchStart, matchLength, errorMessage);
|
return this.renderTierRow(tier, matchStart, matchLength, errorMessage);
|
||||||
case 'category':
|
case 'category':
|
||||||
var category = {name: id[0].toUpperCase() + id.substr(1), id: id};
|
var category = { name: id[0].toUpperCase() + id.substr(1), id: id };
|
||||||
return this.renderCategoryRow(category, matchStart, matchLength, errorMessage);
|
return this.renderCategoryRow(category, matchStart, matchLength, errorMessage);
|
||||||
case 'article':
|
case 'article':
|
||||||
var articleTitle = (window.BattleArticleTitles && BattleArticleTitles[id]) || (id[0].toUpperCase() + id.substr(1));
|
var articleTitle = (window.BattleArticleTitles && BattleArticleTitles[id]) || (id[0].toUpperCase() + id.substr(1));
|
||||||
var article = {name: articleTitle, id: id};
|
var article = { name: articleTitle, id: id };
|
||||||
return this.renderArticleRow(article, matchStart, matchLength, errorMessage);
|
return this.renderArticleRow(article, matchStart, matchLength, errorMessage);
|
||||||
}
|
}
|
||||||
return 'Error: not found';
|
return 'Error: not found';
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ Storage.initialize = function () {
|
||||||
Storage.safeJSON = function (callback) {
|
Storage.safeJSON = function (callback) {
|
||||||
return function (data) {
|
return function (data) {
|
||||||
if (data.length < 1) return;
|
if (data.length < 1) return;
|
||||||
if (data[0] == ']') data = data.substr(1);
|
if (data[0] === ']') data = data.substr(1);
|
||||||
return callback(JSON.parse(data));
|
return callback(JSON.parse(data));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -183,16 +183,15 @@ Storage.bg = {
|
||||||
var l = (max + min) / 2;
|
var l = (max + min) / 2;
|
||||||
if (max === min) {
|
if (max === min) {
|
||||||
return '0, 0%';
|
return '0, 0%';
|
||||||
} else {
|
|
||||||
var d = max - min;
|
|
||||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
||||||
switch (max) {
|
|
||||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
|
||||||
case g: h = (b - r) / d + 2; break;
|
|
||||||
case b: h = (r - g) / d + 4; break;
|
|
||||||
}
|
|
||||||
h /= 6;
|
|
||||||
}
|
}
|
||||||
|
var d = max - min;
|
||||||
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||||
|
switch (max) {
|
||||||
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||||
|
case g: h = (b - r) / d + 2; break;
|
||||||
|
case b: h = (r - g) / d + 4; break;
|
||||||
|
}
|
||||||
|
h /= 6;
|
||||||
return '' + (h * 360) + ',' + (s * 100) + '%';
|
return '' + (h * 360) + ',' + (s * 100) + '%';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -304,7 +303,7 @@ var updatePrefs = function () {
|
||||||
var oldShowjoins = Storage.prefs('showjoins');
|
var oldShowjoins = Storage.prefs('showjoins');
|
||||||
if (oldShowjoins !== undefined && typeof oldShowjoins !== 'object') {
|
if (oldShowjoins !== undefined && typeof oldShowjoins !== 'object') {
|
||||||
var showjoins = {};
|
var showjoins = {};
|
||||||
var serverShowjoins = {global: (oldShowjoins ? 1 : 0)};
|
var serverShowjoins = { global: (oldShowjoins ? 1 : 0) };
|
||||||
var showroomjoins = Storage.prefs('showroomjoins');
|
var showroomjoins = Storage.prefs('showroomjoins');
|
||||||
for (var roomid in showroomjoins) {
|
for (var roomid in showroomjoins) {
|
||||||
serverShowjoins[roomid] = (showroomjoins[roomid] ? 1 : 0);
|
serverShowjoins[roomid] = (showroomjoins[roomid] ? 1 : 0);
|
||||||
|
|
@ -538,7 +537,7 @@ Storage.initTestClient = function () {
|
||||||
data.sid = sid;
|
data.sid = sid;
|
||||||
get(uri, data, callback, type);
|
get(uri, data, callback, type);
|
||||||
} else {
|
} else {
|
||||||
app.addPopup(ProxyPopup, {uri: uri, callback: callback});
|
app.addPopup(ProxyPopup, { uri: uri, callback: callback });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var post = $.post;
|
var post = $.post;
|
||||||
|
|
@ -546,7 +545,7 @@ Storage.initTestClient = function () {
|
||||||
if (type === 'html') {
|
if (type === 'html') {
|
||||||
uri += '&testclient';
|
uri += '&testclient';
|
||||||
}
|
}
|
||||||
if (uri[0] === '/') { //relative URI
|
if (uri[0] === '/') { // relative URI
|
||||||
uri = Dex.resourcePrefix + uri.substr(1);
|
uri = Dex.resourcePrefix + uri.substr(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -560,7 +559,7 @@ Storage.initTestClient = function () {
|
||||||
src += '<input type=hidden name="' + i + '" value="' + BattleLog.escapeHTML(data[i]) + '">';
|
src += '<input type=hidden name="' + i + '" value="' + BattleLog.escapeHTML(data[i]) + '">';
|
||||||
}
|
}
|
||||||
src += '<input type=submit value="Please click this button first."></form></body></html>';
|
src += '<input type=submit value="Please click this button first."></form></body></html>';
|
||||||
app.addPopup(ProxyPopup, {uri: "data:text/html;charset=UTF-8," + encodeURIComponent(src), callback: callback});
|
app.addPopup(ProxyPopup, { uri: "data:text/html;charset=UTF-8," + encodeURIComponent(src), callback: callback });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Storage.whenPrefsLoaded.load();
|
Storage.whenPrefsLoaded.load();
|
||||||
|
|
@ -622,7 +621,7 @@ Storage.compareTeams = function (serverTeam, localTeam) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Storage.loadRemoteTeams = function (after) {
|
Storage.loadRemoteTeams = function (after) {
|
||||||
$.get(app.user.getActionPHP(), {act: 'getteams'}, Storage.safeJSON(function (data) {
|
$.get(app.user.getActionPHP(), { act: 'getteams' }, Storage.safeJSON(function (data) {
|
||||||
if (data.actionerror) {
|
if (data.actionerror) {
|
||||||
return app.addPopupMessage('Error loading uploaded teams: ' + data.actionerror);
|
return app.addPopupMessage('Error loading uploaded teams: ' + data.actionerror);
|
||||||
}
|
}
|
||||||
|
|
@ -652,7 +651,7 @@ Storage.loadRemoteTeams = function (after) {
|
||||||
// team comes down from loginserver as comma-separated list of mons
|
// team comes down from loginserver as comma-separated list of mons
|
||||||
// to save bandwidth
|
// to save bandwidth
|
||||||
var mons = team.team.split(',').map(function (mon) {
|
var mons = team.team.split(',').map(function (mon) {
|
||||||
return {species: mon};
|
return { species: mon };
|
||||||
});
|
});
|
||||||
team.team = Storage.packTeam(mons);
|
team.team = Storage.packTeam(mons);
|
||||||
Storage.teams.unshift(team);
|
Storage.teams.unshift(team);
|
||||||
|
|
@ -854,7 +853,7 @@ Storage.packTeam = function (team) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// level
|
// level
|
||||||
if (set.level && set.level != 100) {
|
if (set.level && set.level !== 100) {
|
||||||
buf += '|' + set.level;
|
buf += '|' + set.level;
|
||||||
} else {
|
} else {
|
||||||
buf += '|';
|
buf += '|';
|
||||||
|
|
@ -938,7 +937,7 @@ Storage.fastUnpackTeam = function (buf) {
|
||||||
spe: Number(evs[5]) || 0
|
spe: Number(evs[5]) || 0
|
||||||
};
|
};
|
||||||
} else if (evstring === '0') {
|
} else if (evstring === '0') {
|
||||||
set.evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
set.evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i = j + 1;
|
i = j + 1;
|
||||||
|
|
@ -1025,7 +1024,7 @@ Storage.unpackTeam = function (buf) {
|
||||||
j = buf.indexOf('|', i);
|
j = buf.indexOf('|', i);
|
||||||
var ability = Dex.abilities.get(buf.substring(i, j)).name;
|
var ability = Dex.abilities.get(buf.substring(i, j)).name;
|
||||||
var species = Dex.species.get(set.species);
|
var species = Dex.species.get(set.species);
|
||||||
set.ability = (species.abilities && ability in {'':1, 0:1, 1:1, H:1} ? species.abilities[ability || '0'] : ability);
|
set.ability = (species.abilities && ability in { '': 1, 0: 1, 1: 1, H: 1 } ? species.abilities[ability || '0'] : ability);
|
||||||
i = j + 1;
|
i = j + 1;
|
||||||
|
|
||||||
// moves
|
// moves
|
||||||
|
|
@ -1056,7 +1055,7 @@ Storage.unpackTeam = function (buf) {
|
||||||
spe: Number(evs[5]) || 0
|
spe: Number(evs[5]) || 0
|
||||||
};
|
};
|
||||||
} else if (evstring === '0') {
|
} else if (evstring === '0') {
|
||||||
set.evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
set.evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i = j + 1;
|
i = j + 1;
|
||||||
|
|
@ -1239,7 +1238,7 @@ Storage.importTeam = function (buffer, teams) {
|
||||||
curSet = null;
|
curSet = null;
|
||||||
teams.push(Storage.unpackLine(line));
|
teams.push(Storage.unpackLine(line));
|
||||||
} else if (!curSet) {
|
} else if (!curSet) {
|
||||||
curSet = {name: '', species: '', gender: ''};
|
curSet = { name: '', species: '', gender: '' };
|
||||||
team.push(curSet);
|
team.push(curSet);
|
||||||
var atIndex = line.lastIndexOf(' @ ');
|
var atIndex = line.lastIndexOf(' @ ');
|
||||||
if (atIndex !== -1) {
|
if (atIndex !== -1) {
|
||||||
|
|
@ -1296,7 +1295,7 @@ Storage.importTeam = function (buffer, teams) {
|
||||||
} else if (line.substr(0, 5) === 'EVs: ') {
|
} else if (line.substr(0, 5) === 'EVs: ') {
|
||||||
line = line.substr(5);
|
line = line.substr(5);
|
||||||
var evLines = line.split('/');
|
var evLines = line.split('/');
|
||||||
curSet.evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
curSet.evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
||||||
for (var j = 0; j < evLines.length; j++) {
|
for (var j = 0; j < evLines.length; j++) {
|
||||||
var evLine = $.trim(evLines[j]);
|
var evLine = $.trim(evLines[j]);
|
||||||
var spaceIndex = evLine.indexOf(' ');
|
var spaceIndex = evLine.indexOf(' ');
|
||||||
|
|
@ -1309,7 +1308,7 @@ Storage.importTeam = function (buffer, teams) {
|
||||||
} else if (line.substr(0, 5) === 'IVs: ') {
|
} else if (line.substr(0, 5) === 'IVs: ') {
|
||||||
line = line.substr(5);
|
line = line.substr(5);
|
||||||
var ivLines = line.split(' / ');
|
var ivLines = line.split(' / ');
|
||||||
curSet.ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31};
|
curSet.ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 };
|
||||||
for (var j = 0; j < ivLines.length; j++) {
|
for (var j = 0; j < ivLines.length; j++) {
|
||||||
var ivLine = ivLines[j];
|
var ivLine = ivLines[j];
|
||||||
var spaceIndex = ivLine.indexOf(' ');
|
var spaceIndex = ivLine.indexOf(' ');
|
||||||
|
|
@ -1397,7 +1396,7 @@ Storage.exportTeam = function (team, gen, hidestats) {
|
||||||
if (curSet.ability) {
|
if (curSet.ability) {
|
||||||
text += 'Ability: ' + curSet.ability + " \n";
|
text += 'Ability: ' + curSet.ability + " \n";
|
||||||
}
|
}
|
||||||
if (curSet.level && curSet.level != 100) {
|
if (curSet.level && curSet.level !== 100) {
|
||||||
text += 'Level: ' + curSet.level + " \n";
|
text += 'Level: ' + curSet.level + " \n";
|
||||||
}
|
}
|
||||||
if (curSet.shiny) {
|
if (curSet.shiny) {
|
||||||
|
|
@ -1472,7 +1471,7 @@ Storage.exportTeam = function (team, gen, hidestats) {
|
||||||
}
|
}
|
||||||
if (!defaultIvs) {
|
if (!defaultIvs) {
|
||||||
for (var stat in BattleStatNames) {
|
for (var stat in BattleStatNames) {
|
||||||
if (typeof curSet.ivs[stat] === 'undefined' || isNaN(curSet.ivs[stat]) || curSet.ivs[stat] == 31) continue;
|
if (typeof curSet.ivs[stat] === 'undefined' || isNaN(curSet.ivs[stat]) || curSet.ivs[stat] === 31) continue;
|
||||||
if (first) {
|
if (first) {
|
||||||
text += 'IVs: ';
|
text += 'IVs: ';
|
||||||
first = false;
|
first = false;
|
||||||
|
|
@ -1509,7 +1508,7 @@ Storage.initDirectory = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var dir = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH;
|
var dir = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH;
|
||||||
if (!(dir.charAt(dir.length - 1) in {'/': 1, '\\': 1})) dir += '/';
|
if (!(dir.charAt(dir.length - 1) in { '/': 1, '\\': 1 })) dir += '/';
|
||||||
fs.stat(dir + 'Documents', function (err, stats) {
|
fs.stat(dir + 'Documents', function (err, stats) {
|
||||||
if (err || !stats.isDirectory()) {
|
if (err || !stats.isDirectory()) {
|
||||||
fs.stat(dir + 'My Documents', function (err, stats) {
|
fs.stat(dir + 'My Documents', function (err, stats) {
|
||||||
|
|
@ -1860,7 +1859,7 @@ Storage.nwLogChat = function (roomid, line) {
|
||||||
var timestamp = '[' + hours + ':' + minutes + '] ';
|
var timestamp = '[' + hours + ':' + minutes + '] ';
|
||||||
|
|
||||||
if (!this.chatLogStreams[roomid]) {
|
if (!this.chatLogStreams[roomid]) {
|
||||||
this.chatLogStreams[roomid] = fs.createWriteStream(this.dir + 'Logs/' + chatLogFdMonth + '/' + roomid + '.txt', {flags: 'a'});
|
this.chatLogStreams[roomid] = fs.createWriteStream(this.dir + 'Logs/' + chatLogFdMonth + '/' + roomid + '.txt', { flags: 'a' });
|
||||||
this.chatLogStreams[roomid].write('\n\n\nLog starting ' + now + '\n\n');
|
this.chatLogStreams[roomid].write('\n\n\nLog starting ' + now + '\n\n');
|
||||||
}
|
}
|
||||||
this.chatLogStreams[roomid].write(timestamp + line + '\n');
|
this.chatLogStreams[roomid].write(timestamp + line + '\n');
|
||||||
|
|
|
||||||
|
|
@ -51,9 +51,9 @@
|
||||||
linkStyle("style/battle-search.css");
|
linkStyle("style/battle-search.css");
|
||||||
linkStyle("/style/font-awesome.css");
|
linkStyle("/style/font-awesome.css");
|
||||||
</script>
|
</script>
|
||||||
|
<script nomodule defer src="/js/lib/ps-polyfill.js"></script>
|
||||||
<script defer src="/config/config.js?"></script>
|
<script defer src="/config/config.js?"></script>
|
||||||
<script defer src="/js/client-core.js?"></script>
|
<script defer src="/js/client-core.js?"></script>
|
||||||
<script nomodule defer src="/js/lib/ps-polyfill.js"></script>
|
|
||||||
|
|
||||||
<script defer src="/js/battle-dex.js?"></script>
|
<script defer src="/js/battle-dex.js?"></script>
|
||||||
<script defer src="/js/battle-text-parser.js?"></script>
|
<script defer src="/js/battle-text-parser.js?"></script>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -11,15 +11,15 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {Battle, Pokemon, Side, WeatherState} from './battle';
|
import type { Battle, Pokemon, Side, WeatherState } from './battle';
|
||||||
import type {BattleSceneStub} from './battle-scene-stub';
|
import type { BattleSceneStub } from './battle-scene-stub';
|
||||||
import {BattleMoveAnims} from './battle-animations-moves';
|
import { BattleMoveAnims } from './battle-animations-moves';
|
||||||
import {BattleLog} from './battle-log';
|
import { BattleLog } from './battle-log';
|
||||||
import {BattleBGM, BattleSound} from './battle-sound';
|
import { type BattleBGM, BattleSound } from './battle-sound';
|
||||||
import {Dex, toID, type ID, type SpriteData} from './battle-dex';
|
import { Dex, toID, type ID, type SpriteData } from './battle-dex';
|
||||||
import {BattleNatures} from './battle-dex-data';
|
import { BattleNatures } from './battle-dex-data';
|
||||||
import {BattleTooltips} from './battle-tooltips';
|
import { BattleTooltips } from './battle-tooltips';
|
||||||
import {BattleTextParser, type Args, type KWArgs} from './battle-text-parser';
|
import { BattleTextParser, type Args, type KWArgs } from './battle-text-parser';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
@ -74,14 +74,14 @@ export class BattleScene implements BattleSceneStub {
|
||||||
$tooltips: JQuery = null!;
|
$tooltips: JQuery = null!;
|
||||||
tooltips: BattleTooltips;
|
tooltips: BattleTooltips;
|
||||||
|
|
||||||
sideConditions: [{[id: string]: Sprite[]}, {[id: string]: Sprite[]}] = [{}, {}];
|
sideConditions: [{ [id: string]: Sprite[] }, { [id: string]: Sprite[] }] = [{}, {}];
|
||||||
|
|
||||||
preloadDone = 0;
|
preloadDone = 0;
|
||||||
preloadNeeded = 0;
|
preloadNeeded = 0;
|
||||||
bgm: BattleBGM | null = null;
|
bgm: BattleBGM | null = null;
|
||||||
backdropImage: string = '';
|
backdropImage = '';
|
||||||
bgmNum = 0;
|
bgmNum = 0;
|
||||||
preloadCache: {[url: string]: HTMLImageElement} = {};
|
preloadCache: { [url: string]: HTMLImageElement } = {};
|
||||||
|
|
||||||
messagebarOpen = false;
|
messagebarOpen = false;
|
||||||
customControls = false;
|
customControls = false;
|
||||||
|
|
@ -271,7 +271,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
effect: string | SpriteData, start: ScenePos, end: ScenePos,
|
effect: string | SpriteData, start: ScenePos, end: ScenePos,
|
||||||
transition: string, after?: string, additionalCss?: JQuery.PlainObject
|
transition: string, after?: string, additionalCss?: JQuery.PlainObject
|
||||||
) {
|
) {
|
||||||
if (typeof effect === 'string') effect = BattleEffects[effect] as SpriteData;
|
if (typeof effect === 'string') effect = BattleEffects[effect];
|
||||||
if (!start.time) start.time = 0;
|
if (!start.time) start.time = 0;
|
||||||
if (!end.time) end.time = start.time + 500;
|
if (!end.time) end.time = start.time + 500;
|
||||||
start.time += this.timeOffset;
|
start.time += this.timeOffset;
|
||||||
|
|
@ -279,18 +279,18 @@ export class BattleScene implements BattleSceneStub {
|
||||||
if (!end.scale && end.scale !== 0 && start.scale) end.scale = start.scale;
|
if (!end.scale && end.scale !== 0 && start.scale) end.scale = start.scale;
|
||||||
if (!end.xscale && end.xscale !== 0 && start.xscale) end.xscale = start.xscale;
|
if (!end.xscale && end.xscale !== 0 && start.xscale) end.xscale = start.xscale;
|
||||||
if (!end.yscale && end.yscale !== 0 && start.yscale) end.yscale = start.yscale;
|
if (!end.yscale && end.yscale !== 0 && start.yscale) end.yscale = start.yscale;
|
||||||
end = {...start, ...end};
|
end = { ...start, ...end };
|
||||||
|
|
||||||
let startpos = this.pos(start, effect);
|
let startpos = this.pos(start, effect);
|
||||||
let endpos = this.posT(end, effect, transition, start);
|
let endpos = this.posT(end, effect, transition, start);
|
||||||
|
|
||||||
let $effect = $('<img src="' + effect.url + '" style="display:block;position:absolute" />');
|
let $effect = $(`<img src="${effect.url!}" style="display:block;position:absolute" />`);
|
||||||
this.$fx.append($effect);
|
this.$fx.append($effect);
|
||||||
if (additionalCss) $effect.css(additionalCss);
|
if (additionalCss) $effect.css(additionalCss);
|
||||||
$effect = this.$fx.children().last();
|
$effect = this.$fx.children().last();
|
||||||
|
|
||||||
if (start.time) {
|
if (start.time) {
|
||||||
$effect.css({...startpos, opacity: 0});
|
$effect.css({ ...startpos, opacity: 0 });
|
||||||
$effect.delay(start.time).animate({
|
$effect.delay(start.time).animate({
|
||||||
opacity: startpos.opacity,
|
opacity: startpos.opacity,
|
||||||
}, 1);
|
}, 1);
|
||||||
|
|
@ -349,10 +349,10 @@ export class BattleScene implements BattleSceneStub {
|
||||||
|
|
||||||
let left = 210;
|
let left = 210;
|
||||||
let top = 245;
|
let top = 245;
|
||||||
let scale = (obj.gen === 5
|
let scale = (obj.gen === 5 ?
|
||||||
? 2.0 - ((loc.z!) / 200)
|
2.0 - ((loc.z!) / 200) :
|
||||||
: 1.5 - 0.5 * ((loc.z!) / 200));
|
1.5 - 0.5 * ((loc.z!) / 200));
|
||||||
if (scale < .1) scale = .1;
|
if (scale < 0.1) scale = 0.1;
|
||||||
|
|
||||||
left += (410 - 190) * ((loc.z!) / 200);
|
left += (410 - 190) * ((loc.z!) / 200);
|
||||||
top += (135 - 245) * ((loc.z!) / 200);
|
top += (135 - 245) * ((loc.z!) / 200);
|
||||||
|
|
@ -476,7 +476,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
}, this.battle.messageFadeTime / this.acceleration);
|
}, this.battle.messageFadeTime / this.acceleration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.battle.hardcoreMode && message.slice(0, 8) === '<small>(') {
|
if (this.battle.hardcoreMode && message.startsWith('<small>(')) {
|
||||||
message = '';
|
message = '';
|
||||||
}
|
}
|
||||||
if (message && this.animating) {
|
if (message && this.animating) {
|
||||||
|
|
@ -580,15 +580,15 @@ export class BattleScene implements BattleSceneStub {
|
||||||
} else {
|
} else {
|
||||||
if (gen <= 1) bg = 'fx/bg-gen1.png?';
|
if (gen <= 1) bg = 'fx/bg-gen1.png?';
|
||||||
else if (gen <= 2) bg = 'fx/bg-gen2.png?';
|
else if (gen <= 2) bg = 'fx/bg-gen2.png?';
|
||||||
else if (gen <= 3) bg = 'fx/' + BattleBackdropsThree[this.numericId % BattleBackdropsThree.length] + '?';
|
else if (gen <= 3) bg = `fx/${BattleBackdropsThree[this.numericId % BattleBackdropsThree.length]}?`;
|
||||||
else if (gen <= 4) bg = 'fx/' + BattleBackdropsFour[this.numericId % BattleBackdropsFour.length];
|
else if (gen <= 4) bg = `fx/${BattleBackdropsFour[this.numericId % BattleBackdropsFour.length]}`;
|
||||||
else if (gen <= 5) bg = 'fx/' + BattleBackdropsFive[this.numericId % BattleBackdropsFive.length];
|
else if (gen <= 5) bg = `fx/${BattleBackdropsFive[this.numericId % BattleBackdropsFive.length]}`;
|
||||||
else bg = 'sprites/gen6bgs/' + BattleBackdrops[this.numericId % BattleBackdrops.length];
|
else bg = `sprites/gen6bgs/${BattleBackdrops[this.numericId % BattleBackdrops.length]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.backdropImage = bg;
|
this.backdropImage = bg;
|
||||||
if (this.$bg) {
|
if (this.$bg) {
|
||||||
this.$bg.css('background-image', 'url(' + Dex.resourcePrefix + '' + this.backdropImage + ')');
|
this.$bg.css('background-image', `url(${Dex.resourcePrefix}${this.backdropImage})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -596,7 +596,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
let name = pokemon.side?.isFar &&
|
let name = pokemon.side?.isFar &&
|
||||||
(this.battle.ignoreOpponent || this.battle.ignoreNicks) ? pokemon.speciesForme : pokemon.name;
|
(this.battle.ignoreOpponent || this.battle.ignoreNicks) ? pokemon.speciesForme : pokemon.name;
|
||||||
if (name !== pokemon.speciesForme) {
|
if (name !== pokemon.speciesForme) {
|
||||||
name += ' (' + pokemon.speciesForme + ')';
|
name += ' (' + pokemon.speciesForme + ')';
|
||||||
}
|
}
|
||||||
if (pokemon === pokemon.side.active[0]) {
|
if (pokemon === pokemon.side.active[0]) {
|
||||||
name += ' (active)';
|
name += ' (active)';
|
||||||
|
|
@ -665,29 +665,29 @@ export class BattleScene implements BattleSceneStub {
|
||||||
for (let i = 0; i < sidebarIcons.length; i++) {
|
for (let i = 0; i < sidebarIcons.length; i++) {
|
||||||
const [iconType, pokeIndex] = sidebarIcons[i];
|
const [iconType, pokeIndex] = sidebarIcons[i];
|
||||||
const poke = pokeIndex !== null ? side.pokemon[pokeIndex] : null;
|
const poke = pokeIndex !== null ? side.pokemon[pokeIndex] : null;
|
||||||
const tooltipCode = ` class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${pokeIndex}${iconType === 'pokemon-illusion' ? '|illusion' : ''}"`;
|
const tooltipCode = ` class="picon has-tooltip" data-tooltip="pokemon|${side.n}|${pokeIndex!}${iconType === 'pokemon-illusion' ? '|illusion' : ''}"`;
|
||||||
if (iconType === 'empty') {
|
if (iconType === 'empty') {
|
||||||
pokemonhtml += `<span class="picon" style="` + Dex.getPokemonIcon('pokeball-none') + `"></span>`;
|
pokemonhtml += `<span class="picon" style="${Dex.getPokemonIcon('pokeball-none')}"></span>`;
|
||||||
} else if (noShow) {
|
} else if (noShow) {
|
||||||
if (poke?.fainted) {
|
if (poke?.fainted) {
|
||||||
pokemonhtml += `<span${tooltipCode} style="` + Dex.getPokemonIcon('pokeball-fainted') + `" aria-label="Fainted"></span>`;
|
pokemonhtml += `<span${tooltipCode} style="${Dex.getPokemonIcon('pokeball-fainted')}" aria-label="Fainted"></span>`;
|
||||||
} else if (poke?.status) {
|
} else if (poke?.status) {
|
||||||
pokemonhtml += `<span${tooltipCode} style="` + Dex.getPokemonIcon('pokeball-statused') + `" aria-label="Statused"></span>`;
|
pokemonhtml += `<span${tooltipCode} style="${Dex.getPokemonIcon('pokeball-statused')}" aria-label="Statused"></span>`;
|
||||||
} else {
|
} else {
|
||||||
pokemonhtml += `<span${tooltipCode} style="` + Dex.getPokemonIcon('pokeball') + `" aria-label="Non-statused"></span>`;
|
pokemonhtml += `<span${tooltipCode} style="${Dex.getPokemonIcon('pokeball')}" aria-label="Non-statused"></span>`;
|
||||||
}
|
}
|
||||||
} else if (iconType === 'pseudo-zoroark') {
|
} else if (iconType === 'pseudo-zoroark') {
|
||||||
pokemonhtml += `<span class="picon" style="` + Dex.getPokemonIcon('zoroark') + `" title="Unrevealed Illusion user" aria-label="Unrevealed Illusion user"></span>`;
|
pokemonhtml += `<span class="picon" style="${Dex.getPokemonIcon('zoroark')}" title="Unrevealed Illusion user" aria-label="Unrevealed Illusion user"></span>`;
|
||||||
} else if (!poke) {
|
} else if (!poke) {
|
||||||
pokemonhtml += `<span class="picon" style="` + Dex.getPokemonIcon('pokeball') + `" title="Not revealed" aria-label="Not revealed"></span>`;
|
pokemonhtml += `<span class="picon" style="${Dex.getPokemonIcon('pokeball')}" title="Not revealed" aria-label="Not revealed"></span>`;
|
||||||
} else if (!poke.ident && this.battle.teamPreviewCount && this.battle.teamPreviewCount < side.pokemon.length) {
|
} else if (!poke.ident && this.battle.teamPreviewCount && this.battle.teamPreviewCount < side.pokemon.length) {
|
||||||
// in VGC (bring 6 pick 4) and other pick-less-than-you-bring formats, this is
|
// in VGC (bring 6 pick 4) and other pick-less-than-you-bring formats, this is
|
||||||
// a pokemon that's been brought but not necessarily picked
|
// a pokemon that's been brought but not necessarily picked
|
||||||
const details = this.getDetailsText(poke);
|
const details = this.getDetailsText(poke);
|
||||||
pokemonhtml += `<span${tooltipCode} style="` + Dex.getPokemonIcon(poke, !side.isFar) + `;opacity:0.6" aria-label="${details}"></span>`;
|
pokemonhtml += `<span${tooltipCode} style="${Dex.getPokemonIcon(poke, !side.isFar)};opacity:0.6" aria-label="${details}"></span>`;
|
||||||
} else {
|
} else {
|
||||||
const details = this.getDetailsText(poke);
|
const details = this.getDetailsText(poke);
|
||||||
pokemonhtml += `<span${tooltipCode} style="` + Dex.getPokemonIcon(poke, !side.isFar) + `" aria-label="${details}"></span>`;
|
pokemonhtml += `<span${tooltipCode} style="${Dex.getPokemonIcon(poke, !side.isFar)}" aria-label="${details}"></span>`;
|
||||||
}
|
}
|
||||||
if (i % 3 === 2) pokemonhtml += `</div><div class="teamicons">`;
|
if (i % 3 === 2) pokemonhtml += `</div><div class="teamicons">`;
|
||||||
}
|
}
|
||||||
|
|
@ -737,7 +737,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
const side = this.battle.nearSide;
|
const side = this.battle.nearSide;
|
||||||
|
|
||||||
if (side.ally) {
|
if (side.ally) {
|
||||||
const side2 = side.ally!;
|
const side2 = side.ally;
|
||||||
this.$leftbar.html(this.getSidebarHTML(side, 'near2') + this.getSidebarHTML(side2, 'near'));
|
this.$leftbar.html(this.getSidebarHTML(side, 'near2') + this.getSidebarHTML(side2, 'near'));
|
||||||
} else if (this.battle.sides.length > 2) { // FFA
|
} else if (this.battle.sides.length > 2) { // FFA
|
||||||
const side2 = this.battle.sides[side.n === 0 ? 3 : 2];
|
const side2 = this.battle.sides[side.n === 0 ? 3 : 2];
|
||||||
|
|
@ -750,7 +750,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
const side = this.battle.farSide;
|
const side = this.battle.farSide;
|
||||||
|
|
||||||
if (side.ally) {
|
if (side.ally) {
|
||||||
const side2 = side.ally!;
|
const side2 = side.ally;
|
||||||
this.$rightbar.html(this.getSidebarHTML(side, 'far2') + this.getSidebarHTML(side2, 'far'));
|
this.$rightbar.html(this.getSidebarHTML(side, 'far2') + this.getSidebarHTML(side2, 'far'));
|
||||||
} else if (this.battle.sides.length > 2) { // FFA
|
} else if (this.battle.sides.length > 2) { // FFA
|
||||||
const side2 = this.battle.sides[side.n === 0 ? 3 : 2];
|
const side2 = this.battle.sides[side.n === 0 ? 3 : 2];
|
||||||
|
|
@ -802,17 +802,17 @@ export class BattleScene implements BattleSceneStub {
|
||||||
const tooltips = this.battle.gameType === 'freeforall' ? {
|
const tooltips = this.battle.gameType === 'freeforall' ? {
|
||||||
// FFA battles are visually rendered as triple battle with the center slots empty
|
// FFA battles are visually rendered as triple battle with the center slots empty
|
||||||
// so we swap the 2nd and 3rd tooltips on each side
|
// so we swap the 2nd and 3rd tooltips on each side
|
||||||
p2b: {top: 70, left: 250, width: 80, height: 100, tooltip: 'activepokemon|1|1'},
|
p2b: { top: 70, left: 250, width: 80, height: 100, tooltip: 'activepokemon|1|1' },
|
||||||
p2a: {top: 90, left: 390, width: 100, height: 100, tooltip: 'activepokemon|1|0'},
|
p2a: { top: 90, left: 390, width: 100, height: 100, tooltip: 'activepokemon|1|0' },
|
||||||
p1a: {top: 200, left: 130, width: 120, height: 160, tooltip: 'activepokemon|0|0'},
|
p1a: { top: 200, left: 130, width: 120, height: 160, tooltip: 'activepokemon|0|0' },
|
||||||
p1b: {top: 200, left: 350, width: 150, height: 160, tooltip: 'activepokemon|0|1'},
|
p1b: { top: 200, left: 350, width: 150, height: 160, tooltip: 'activepokemon|0|1' },
|
||||||
} : {
|
} : {
|
||||||
p2c: {top: 70, left: 250, width: 80, height: 100, tooltip: 'activepokemon|1|2'},
|
p2c: { top: 70, left: 250, width: 80, height: 100, tooltip: 'activepokemon|1|2' },
|
||||||
p2b: {top: 85, left: 320, width: 90, height: 100, tooltip: 'activepokemon|1|1'},
|
p2b: { top: 85, left: 320, width: 90, height: 100, tooltip: 'activepokemon|1|1' },
|
||||||
p2a: {top: 90, left: 390, width: 100, height: 100, tooltip: 'activepokemon|1|0'},
|
p2a: { top: 90, left: 390, width: 100, height: 100, tooltip: 'activepokemon|1|0' },
|
||||||
p1a: {top: 200, left: 130, width: 120, height: 160, tooltip: 'activepokemon|0|0'},
|
p1a: { top: 200, left: 130, width: 120, height: 160, tooltip: 'activepokemon|0|0' },
|
||||||
p1b: {top: 200, left: 250, width: 150, height: 160, tooltip: 'activepokemon|0|1'},
|
p1b: { top: 200, left: 250, width: 150, height: 160, tooltip: 'activepokemon|0|1' },
|
||||||
p1c: {top: 200, left: 350, width: 150, height: 160, tooltip: 'activepokemon|0|2'},
|
p1c: { top: 200, left: 350, width: 150, height: 160, tooltip: 'activepokemon|0|2' },
|
||||||
};
|
};
|
||||||
for (const id in tooltips) {
|
for (const id in tooltips) {
|
||||||
let layout = tooltips[id as 'p1a'];
|
let layout = tooltips[id as 'p1a'];
|
||||||
|
|
@ -861,26 +861,26 @@ export class BattleScene implements BattleSceneStub {
|
||||||
textBuf += pokemon.speciesForme;
|
textBuf += pokemon.speciesForme;
|
||||||
let url = spriteData.url;
|
let url = spriteData.url;
|
||||||
// if (this.paused) url.replace('/xyani', '/xy').replace('.gif', '.png');
|
// if (this.paused) url.replace('/xyani', '/xy').replace('.gif', '.png');
|
||||||
buf += '<img src="' + url + '" width="' + spriteData.w + '" height="' + spriteData.h + '" style="position:absolute;top:' + Math.floor(y - spriteData.h / 2) + 'px;left:' + Math.floor(x - spriteData.w / 2) + 'px" />';
|
buf += `<img src="${url}" width="${spriteData.w}" height="${spriteData.h}" style="position:absolute;top:${Math.floor(y - spriteData.h / 2)}px;left:${Math.floor(x - spriteData.w / 2)}px" />`;
|
||||||
buf2 += '<div style="position:absolute;top:' + (y + 45) + 'px;left:' + (x - 40) + 'px;width:80px;font-size:10px;text-align:center;color:#FFF;">';
|
buf2 += `<div style="position:absolute;top:${y + 45}px;left:${x - 40}px;width:80px;font-size:10px;text-align:center;color:#FFF;">`;
|
||||||
const gender = pokemon.gender;
|
const gender = pokemon.gender;
|
||||||
if (gender === 'M' || gender === 'F') {
|
if (gender === 'M' || gender === 'F') {
|
||||||
buf2 += `<img src="${Dex.fxPrefix}gender-${gender.toLowerCase()}.png" alt="${gender}" width="7" height="10" class="pixelated" style="margin-bottom:-1px" /> `;
|
buf2 += `<img src="${Dex.fxPrefix}gender-${gender.toLowerCase()}.png" alt="${gender}" width="7" height="10" class="pixelated" style="margin-bottom:-1px" /> `;
|
||||||
}
|
}
|
||||||
if (pokemon.level !== 100) {
|
if (pokemon.level !== 100) {
|
||||||
buf2 += '<span style="text-shadow:#000 1px 1px 0,#000 1px -1px 0,#000 -1px 1px 0,#000 -1px -1px 0"><small>L</small>' + pokemon.level + '</span>';
|
buf2 += `<span style="text-shadow:#000 1px 1px 0,#000 1px -1px 0,#000 -1px 1px 0,#000 -1px -1px 0"><small>L</small>${pokemon.level}</span>`;
|
||||||
}
|
}
|
||||||
if (pokemon.item === '(mail)') {
|
if (pokemon.item === '(mail)') {
|
||||||
buf2 += ' <img src="' + Dex.resourcePrefix + 'fx/mail.png" width="8" height="10" alt="F" style="margin-bottom:-1px" />';
|
buf2 += ` <img src="${Dex.resourcePrefix}fx/mail.png" width="8" height="10" alt="F" style="margin-bottom:-1px" />`;
|
||||||
} else if (pokemon.item) {
|
} else if (pokemon.item) {
|
||||||
buf2 += ' <img src="' + Dex.resourcePrefix + 'fx/item.png" width="8" height="10" alt="F" style="margin-bottom:-1px" />';
|
buf2 += ` <img src="${Dex.resourcePrefix}fx/item.png" width="8" height="10" alt="F" style="margin-bottom:-1px" />`;
|
||||||
}
|
}
|
||||||
buf2 += '</div>';
|
buf2 += '</div>';
|
||||||
}
|
}
|
||||||
side.totalPokemon = side.pokemon.length;
|
side.totalPokemon = side.pokemon.length;
|
||||||
if (textBuf) {
|
if (textBuf) {
|
||||||
this.log.addDiv('chat battle-history',
|
this.log.addDiv('chat battle-history',
|
||||||
'<strong>' + BattleLog.escapeHTML(side.name) + '\'s team:</strong> <em style="color:#445566;display:block;">' + BattleLog.escapeHTML(textBuf) + '</em>'
|
`<strong>${BattleLog.escapeHTML(side.name)}'s team:</strong> <em style="color:#445566;display:block;">${BattleLog.escapeHTML(textBuf)}</em>`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.$sprites[spriteIndex].html(buf + buf2);
|
this.$sprites[spriteIndex].html(buf + buf2);
|
||||||
|
|
@ -916,21 +916,21 @@ export class BattleScene implements BattleSceneStub {
|
||||||
}
|
}
|
||||||
|
|
||||||
pseudoWeatherLeft(pWeather: WeatherState) {
|
pseudoWeatherLeft(pWeather: WeatherState) {
|
||||||
let buf = '<br />' + Dex.moves.get(pWeather[0]).name;
|
let buf = `<br />${Dex.moves.get(pWeather[0]).name}`;
|
||||||
if (!pWeather[1] && pWeather[2]) {
|
if (!pWeather[1] && pWeather[2]) {
|
||||||
pWeather[1] = pWeather[2];
|
pWeather[1] = pWeather[2];
|
||||||
pWeather[2] = 0;
|
pWeather[2] = 0;
|
||||||
}
|
}
|
||||||
if (this.battle.gen < 7 && this.battle.hardcoreMode) return buf;
|
if (this.battle.gen < 7 && this.battle.hardcoreMode) return buf;
|
||||||
if (pWeather[2]) {
|
if (pWeather[2]) {
|
||||||
return buf + ' <small>(' + pWeather[1] + ' or ' + pWeather[2] + ' turns)</small>';
|
return `${buf} <small>(${pWeather[1]} or ${pWeather[2]} turns)</small>`;
|
||||||
}
|
}
|
||||||
if (pWeather[1]) {
|
if (pWeather[1]) {
|
||||||
return buf + ' <small>(' + pWeather[1] + ' turn' + (pWeather[1] === 1 ? '' : 's') + ')</small>';
|
return `${buf} <small>(${pWeather[1]} turn${pWeather[1] === 1 ? '' : 's'})</small>`;
|
||||||
}
|
}
|
||||||
return buf; // weather not found
|
return buf; // weather not found
|
||||||
}
|
}
|
||||||
sideConditionLeft(cond: [string, number, number, number], isFoe: boolean, all?: boolean) {
|
sideConditionLeft(cond: Side['sideConditions'][string], isFoe: boolean, all?: boolean) {
|
||||||
if (!cond[2] && !cond[3] && !all) return '';
|
if (!cond[2] && !cond[3] && !all) return '';
|
||||||
let buf = `<br />${isFoe && !all ? "Foe's " : ""}${Dex.moves.get(cond[0]).name}`;
|
let buf = `<br />${isFoe && !all ? "Foe's " : ""}${Dex.moves.get(cond[0]).name}`;
|
||||||
if (this.battle.gen < 7 && this.battle.hardcoreMode) return buf;
|
if (this.battle.gen < 7 && this.battle.hardcoreMode) return buf;
|
||||||
|
|
@ -941,9 +941,9 @@ export class BattleScene implements BattleSceneStub {
|
||||||
cond[3] = 0;
|
cond[3] = 0;
|
||||||
}
|
}
|
||||||
if (!cond[3]) {
|
if (!cond[3]) {
|
||||||
return buf + ' <small>(' + cond[2] + ' turn' + (cond[2] === 1 ? '' : 's') + ')</small>';
|
return `${buf} <small>(${cond[2]} turn${cond[2] === 1 ? '' : 's'})</small>`;
|
||||||
}
|
}
|
||||||
return buf + ' <small>(' + cond[2] + ' or ' + cond[3] + ' turns)</small>';
|
return `${buf} <small>(${cond[2]} or ${cond[3]} turns)</small>`;
|
||||||
}
|
}
|
||||||
weatherLeft() {
|
weatherLeft() {
|
||||||
if (this.battle.gen < 7 && this.battle.hardcoreMode) return '';
|
if (this.battle.gen < 7 && this.battle.hardcoreMode) return '';
|
||||||
|
|
@ -951,7 +951,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
let weatherhtml = ``;
|
let weatherhtml = ``;
|
||||||
|
|
||||||
if (this.battle.weather) {
|
if (this.battle.weather) {
|
||||||
const weatherNameTable: {[id: string]: string} = {
|
const weatherNameTable: { [id: string]: string } = {
|
||||||
sunnyday: 'Sun',
|
sunnyday: 'Sun',
|
||||||
desolateland: 'Intense Sun',
|
desolateland: 'Intense Sun',
|
||||||
raindance: 'Rain',
|
raindance: 'Rain',
|
||||||
|
|
@ -1030,7 +1030,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
}, this.curWeather ? 300 : 100, () => {
|
}, this.curWeather ? 300 : 100, () => {
|
||||||
this.$weather.html('<em>' + weatherhtml + '</em>');
|
this.$weather.html('<em>' + weatherhtml + '</em>');
|
||||||
this.$weather.attr('class', weather ? 'weather ' + weather + 'weather' : 'weather');
|
this.$weather.attr('class', weather ? 'weather ' + weather + 'weather' : 'weather');
|
||||||
this.$weather.animate({opacity: isIntense || !weather ? 0.9 : 0.5}, 300);
|
this.$weather.animate({ opacity: isIntense || !weather ? 0.9 : 0.5 }, 300);
|
||||||
});
|
});
|
||||||
this.curWeather = weather;
|
this.curWeather = weather;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1043,7 +1043,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
}, this.curTerrain ? 400 : 1, () => {
|
}, this.curTerrain ? 400 : 1, () => {
|
||||||
this.$terrain.attr('class', terrain ? 'weather ' + terrain + 'weather' : 'weather');
|
this.$terrain.attr('class', terrain ? 'weather ' + terrain + 'weather' : 'weather');
|
||||||
this.$terrain.animate({top: 0, opacity: 1}, 400);
|
this.$terrain.animate({ top: 0, opacity: 1 }, 400);
|
||||||
});
|
});
|
||||||
this.curTerrain = terrain;
|
this.curTerrain = terrain;
|
||||||
}
|
}
|
||||||
|
|
@ -1053,7 +1053,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
this.$turn.html('');
|
this.$turn.html('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$turn.html('<div class="turn has-tooltip" data-tooltip="field" data-ownheight="1">Turn ' + this.battle.turn + '</div>');
|
this.$turn.html(`<div class="turn has-tooltip" data-tooltip="field" data-ownheight="1">Turn ${this.battle.turn}</div>`);
|
||||||
}
|
}
|
||||||
incrementTurn() {
|
incrementTurn() {
|
||||||
if (!this.animating) return;
|
if (!this.animating) return;
|
||||||
|
|
@ -1061,7 +1061,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
const turn = this.battle.turn;
|
const turn = this.battle.turn;
|
||||||
if (turn <= 0) return;
|
if (turn <= 0) return;
|
||||||
const $prevTurn = this.$turn.children();
|
const $prevTurn = this.$turn.children();
|
||||||
const $newTurn = $('<div class="turn has-tooltip" data-tooltip="field" data-ownheight="1">Turn ' + turn + '</div>');
|
const $newTurn = $(`<div class="turn has-tooltip" data-tooltip="field" data-ownheight="1">Turn ${turn}</div>`);
|
||||||
$newTurn.css({
|
$newTurn.css({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
left: 160,
|
left: 160,
|
||||||
|
|
@ -1071,7 +1071,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
left: 110,
|
left: 110,
|
||||||
}, 500).animate({
|
}, 500).animate({
|
||||||
opacity: .4,
|
opacity: 0.4,
|
||||||
}, 1500);
|
}, 1500);
|
||||||
$prevTurn.animate({
|
$prevTurn.animate({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
|
|
@ -1130,7 +1130,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
yscale: 0,
|
yscale: 0,
|
||||||
opacity: 0.1,
|
opacity: 0.1,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(auroraveil.$el!);
|
this.$spritesFront[spriteIndex].append(auroraveil.$el);
|
||||||
this.sideConditions[siden][id] = [auroraveil];
|
this.sideConditions[siden][id] = [auroraveil];
|
||||||
auroraveil.anim({
|
auroraveil.anim({
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
|
|
@ -1150,7 +1150,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
yscale: 0,
|
yscale: 0,
|
||||||
opacity: 0.1,
|
opacity: 0.1,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(reflect.$el!);
|
this.$spritesFront[spriteIndex].append(reflect.$el);
|
||||||
this.sideConditions[siden][id] = [reflect];
|
this.sideConditions[siden][id] = [reflect];
|
||||||
reflect.anim({
|
reflect.anim({
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
|
|
@ -1170,7 +1170,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
yscale: 0,
|
yscale: 0,
|
||||||
opacity: 0.1,
|
opacity: 0.1,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(safeguard.$el!);
|
this.$spritesFront[spriteIndex].append(safeguard.$el);
|
||||||
this.sideConditions[siden][id] = [safeguard];
|
this.sideConditions[siden][id] = [safeguard];
|
||||||
safeguard.anim({
|
safeguard.anim({
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
|
|
@ -1190,7 +1190,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
yscale: 0,
|
yscale: 0,
|
||||||
opacity: 0.1,
|
opacity: 0.1,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(lightscreen.$el!);
|
this.$spritesFront[spriteIndex].append(lightscreen.$el);
|
||||||
this.sideConditions[siden][id] = [lightscreen];
|
this.sideConditions[siden][id] = [lightscreen];
|
||||||
lightscreen.anim({
|
lightscreen.anim({
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
|
|
@ -1210,7 +1210,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
yscale: 0,
|
yscale: 0,
|
||||||
opacity: 0.1,
|
opacity: 0.1,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(mist.$el!);
|
this.$spritesFront[spriteIndex].append(mist.$el);
|
||||||
this.sideConditions[siden][id] = [mist];
|
this.sideConditions[siden][id] = [mist];
|
||||||
mist.anim({
|
mist.anim({
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
|
|
@ -1257,10 +1257,10 @@ export class BattleScene implements BattleSceneStub {
|
||||||
scale: 0.2,
|
scale: 0.2,
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
this.$spritesFront[spriteIndex].append(rock1.$el!);
|
this.$spritesFront[spriteIndex].append(rock1.$el);
|
||||||
this.$spritesFront[spriteIndex].append(rock2.$el!);
|
this.$spritesFront[spriteIndex].append(rock2.$el);
|
||||||
this.$spritesFront[spriteIndex].append(rock3.$el!);
|
this.$spritesFront[spriteIndex].append(rock3.$el);
|
||||||
this.$spritesFront[spriteIndex].append(rock4.$el!);
|
this.$spritesFront[spriteIndex].append(rock4.$el);
|
||||||
this.sideConditions[siden][id] = [rock1, rock2, rock3, rock4];
|
this.sideConditions[siden][id] = [rock1, rock2, rock3, rock4];
|
||||||
break;
|
break;
|
||||||
case 'gmaxsteelsurge':
|
case 'gmaxsteelsurge':
|
||||||
|
|
@ -1289,9 +1289,9 @@ export class BattleScene implements BattleSceneStub {
|
||||||
scale: 0.8,
|
scale: 0.8,
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
this.$spritesFront[spriteIndex].append(surge1.$el!);
|
this.$spritesFront[spriteIndex].append(surge1.$el);
|
||||||
this.$spritesFront[spriteIndex].append(surge2.$el!);
|
this.$spritesFront[spriteIndex].append(surge2.$el);
|
||||||
this.$spritesFront[spriteIndex].append(surge3.$el!);
|
this.$spritesFront[spriteIndex].append(surge3.$el);
|
||||||
this.sideConditions[siden][id] = [surge1, surge2, surge3];
|
this.sideConditions[siden][id] = [surge1, surge2, surge3];
|
||||||
break;
|
break;
|
||||||
case 'spikes':
|
case 'spikes':
|
||||||
|
|
@ -1309,7 +1309,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
z: side.z,
|
z: side.z,
|
||||||
scale: 0.3,
|
scale: 0.3,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(spike1.$el!);
|
this.$spritesFront[spriteIndex].append(spike1.$el);
|
||||||
spikeArray.push(spike1);
|
spikeArray.push(spike1);
|
||||||
}
|
}
|
||||||
if (spikeArray.length < 2 && levels >= 2) {
|
if (spikeArray.length < 2 && levels >= 2) {
|
||||||
|
|
@ -1318,9 +1318,9 @@ export class BattleScene implements BattleSceneStub {
|
||||||
x: x + 30,
|
x: x + 30,
|
||||||
y: y - 45,
|
y: y - 45,
|
||||||
z: side.z,
|
z: side.z,
|
||||||
scale: .3,
|
scale: 0.3,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(spike2.$el!);
|
this.$spritesFront[spriteIndex].append(spike2.$el);
|
||||||
spikeArray.push(spike2);
|
spikeArray.push(spike2);
|
||||||
}
|
}
|
||||||
if (spikeArray.length < 3 && levels >= 3) {
|
if (spikeArray.length < 3 && levels >= 3) {
|
||||||
|
|
@ -1329,9 +1329,9 @@ export class BattleScene implements BattleSceneStub {
|
||||||
x: x + 50,
|
x: x + 50,
|
||||||
y: y - 40,
|
y: y - 40,
|
||||||
z: side.z,
|
z: side.z,
|
||||||
scale: .3,
|
scale: 0.3,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(spike3.$el!);
|
this.$spritesFront[spriteIndex].append(spike3.$el);
|
||||||
spikeArray.push(spike3);
|
spikeArray.push(spike3);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1350,7 +1350,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
z: side.z,
|
z: side.z,
|
||||||
scale: 0.3,
|
scale: 0.3,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(tspike1.$el!);
|
this.$spritesFront[spriteIndex].append(tspike1.$el);
|
||||||
tspikeArray.push(tspike1);
|
tspikeArray.push(tspike1);
|
||||||
}
|
}
|
||||||
if (tspikeArray.length < 2 && tspikeLevels >= 2) {
|
if (tspikeArray.length < 2 && tspikeLevels >= 2) {
|
||||||
|
|
@ -1359,9 +1359,9 @@ export class BattleScene implements BattleSceneStub {
|
||||||
x: x - 15,
|
x: x - 15,
|
||||||
y: y - 35,
|
y: y - 35,
|
||||||
z: side.z,
|
z: side.z,
|
||||||
scale: .3,
|
scale: 0.3,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(tspike2.$el!);
|
this.$spritesFront[spriteIndex].append(tspike2.$el);
|
||||||
tspikeArray.push(tspike2);
|
tspikeArray.push(tspike2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1374,7 +1374,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
opacity: 0.4,
|
opacity: 0.4,
|
||||||
scale: 0.7,
|
scale: 0.7,
|
||||||
}, this);
|
}, this);
|
||||||
this.$spritesFront[spriteIndex].append(web.$el!);
|
this.$spritesFront[spriteIndex].append(web.$el);
|
||||||
this.sideConditions[siden][id] = [web];
|
this.sideConditions[siden][id] = [web];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1399,7 +1399,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
|
|
||||||
typeAnim(pokemon: Pokemon, types: string) {
|
typeAnim(pokemon: Pokemon, types: string) {
|
||||||
const result = BattleLog.escapeHTML(types).split('/').map(type =>
|
const result = BattleLog.escapeHTML(types).split('/').map(type =>
|
||||||
'<img src="' + Dex.resourcePrefix + 'sprites/types/' + encodeURIComponent(type) + '.png" alt="' + type + '" class="pixelated" />'
|
`<img src="${Dex.resourcePrefix}sprites/types/${encodeURIComponent(type)}.png" alt="${type}" class="pixelated" />`
|
||||||
).join(' ');
|
).join(' ');
|
||||||
this.resultAnim(pokemon, result, 'neutral');
|
this.resultAnim(pokemon, result, 'neutral');
|
||||||
}
|
}
|
||||||
|
|
@ -1425,7 +1425,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
}
|
}
|
||||||
abilityActivateAnim(pokemon: Pokemon, result: string) {
|
abilityActivateAnim(pokemon: Pokemon, result: string) {
|
||||||
if (!this.animating) return;
|
if (!this.animating) return;
|
||||||
this.$fx.append('<div class="result abilityresult"><strong>' + result + '</strong></div>');
|
this.$fx.append(`<div class="result abilityresult"><strong>${result}</strong></div>`);
|
||||||
let $effect = this.$fx.children().last();
|
let $effect = this.$fx.children().last();
|
||||||
$effect.delay(this.timeOffset).css({
|
$effect.delay(this.timeOffset).css({
|
||||||
display: 'block',
|
display: 'block',
|
||||||
|
|
@ -1459,7 +1459,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (damage === '100%' && pokemon.hp > 0) damage = '99%';
|
if (damage === '100%' && pokemon.hp > 0) damage = '99%';
|
||||||
this.resultAnim(pokemon, this.battle.hardcoreMode ? 'Damage' : '−' + damage, 'bad');
|
this.resultAnim(pokemon, this.battle.hardcoreMode ? 'Damage' : `−${damage}`, 'bad');
|
||||||
|
|
||||||
$hp.animate({
|
$hp.animate({
|
||||||
width: w,
|
width: w,
|
||||||
|
|
@ -1482,7 +1482,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
callback = () => { $hp.removeClass('hp-red'); };
|
callback = () => { $hp.removeClass('hp-red'); };
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resultAnim(pokemon, this.battle.hardcoreMode ? 'Heal' : '+' + damage, 'good');
|
this.resultAnim(pokemon, this.battle.hardcoreMode ? 'Heal' : `+${damage}`, 'good');
|
||||||
|
|
||||||
$hp.animate({
|
$hp.animate({
|
||||||
width: w,
|
width: w,
|
||||||
|
|
@ -1685,7 +1685,7 @@ export class BattleScene implements BattleSceneStub {
|
||||||
}
|
}
|
||||||
this.battle = null!;
|
this.battle = null!;
|
||||||
}
|
}
|
||||||
static getHPColor(pokemon: {hp: number, maxhp: number}) {
|
static getHPColor(pokemon: { hp: number, maxhp: number }) {
|
||||||
let ratio = pokemon.hp / pokemon.maxhp;
|
let ratio = pokemon.hp / pokemon.maxhp;
|
||||||
if (ratio > 0.5) return 'g';
|
if (ratio > 0.5) return 'g';
|
||||||
if (ratio > 0.2) return 'y';
|
if (ratio > 0.2) return 'y';
|
||||||
|
|
@ -1732,7 +1732,7 @@ export class Sprite {
|
||||||
if (spriteData) {
|
if (spriteData) {
|
||||||
sp = spriteData;
|
sp = spriteData;
|
||||||
let rawHTML = sp.rawHTML ||
|
let rawHTML = sp.rawHTML ||
|
||||||
'<img src="' + sp.url + '" style="display:none;position:absolute"' + (sp.pixelated ? ' class="pixelated"' : '') + ' />';
|
`<img src="${sp.url!}" style="display:none;position:absolute"${sp.pixelated ? ' class="pixelated"' : ''} />`;
|
||||||
this.$el = $(rawHTML);
|
this.$el = $(rawHTML);
|
||||||
} else {
|
} else {
|
||||||
sp = {
|
sp = {
|
||||||
|
|
@ -1746,7 +1746,7 @@ export class Sprite {
|
||||||
this.x = pos.x;
|
this.x = pos.x;
|
||||||
this.y = pos.y;
|
this.y = pos.y;
|
||||||
this.z = pos.z;
|
this.z = pos.z;
|
||||||
if (pos.opacity !== 0 && spriteData) this.$el!.css(scene.pos(pos, sp));
|
if (pos.opacity !== 0 && spriteData) this.$el.css(scene.pos(pos, sp));
|
||||||
|
|
||||||
if (!spriteData) {
|
if (!spriteData) {
|
||||||
this.delay = function () { return this; };
|
this.delay = function () { return this; };
|
||||||
|
|
@ -1760,7 +1760,7 @@ export class Sprite {
|
||||||
this.scene = null!;
|
this.scene = null!;
|
||||||
}
|
}
|
||||||
delay(time: number) {
|
delay(time: number) {
|
||||||
this.$el!.delay(time);
|
this.$el.delay(time);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
anim(end: ScenePos, transition?: string) {
|
anim(end: ScenePos, transition?: string) {
|
||||||
|
|
@ -1774,17 +1774,17 @@ export class Sprite {
|
||||||
...end,
|
...end,
|
||||||
};
|
};
|
||||||
if (end.time === 0) {
|
if (end.time === 0) {
|
||||||
this.$el!.css(this.scene.pos(end, this.sp));
|
this.$el.css(this.scene.pos(end, this.sp));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
this.$el!.animate(this.scene.posT(end, this.sp, transition, this), end.time!);
|
this.$el.animate(this.scene.posT(end, this.sp, transition, this), end.time!);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PokemonSprite extends Sprite {
|
export class PokemonSprite extends Sprite {
|
||||||
// HTML strings are constructed from this table and stored back in it to cache them
|
// HTML strings are constructed from this table and stored back in it to cache them
|
||||||
protected static statusTable: {[id: string]: [string, 'good' | 'bad' | 'neutral'] | null | string} = {
|
protected static statusTable: { [id: string]: [string, 'good' | 'bad' | 'neutral'] | null | string } = {
|
||||||
formechange: null,
|
formechange: null,
|
||||||
typechange: null,
|
typechange: null,
|
||||||
typeadd: null,
|
typeadd: null,
|
||||||
|
|
@ -1921,7 +1921,7 @@ export class PokemonSprite extends Sprite {
|
||||||
left = 0;
|
left = 0;
|
||||||
top = 0;
|
top = 0;
|
||||||
|
|
||||||
effects: {[id: string]: Sprite[]} = {};
|
effects: { [id: string]: Sprite[] } = {};
|
||||||
|
|
||||||
constructor(spriteData: SpriteData | null, pos: InitScenePos, scene: BattleScene, isFrontSprite: boolean) {
|
constructor(spriteData: SpriteData | null, pos: InitScenePos, scene: BattleScene, isFrontSprite: boolean) {
|
||||||
super(spriteData, pos, scene);
|
super(spriteData, pos, scene);
|
||||||
|
|
@ -1985,7 +1985,7 @@ export class PokemonSprite extends Sprite {
|
||||||
x: this.x,
|
x: this.x,
|
||||||
y: this.y,
|
y: this.y,
|
||||||
z: (this.isSubActive ? this.behind(30) : this.z),
|
z: (this.isSubActive ? this.behind(30) : this.z),
|
||||||
opacity: (this.$sub ? .3 : 1),
|
opacity: (this.$sub ? 0.3 : 1),
|
||||||
}, sp));
|
}, sp));
|
||||||
}
|
}
|
||||||
animSub(instant?: boolean, noAnim?: boolean) {
|
animSub(instant?: boolean, noAnim?: boolean) {
|
||||||
|
|
@ -2043,14 +2043,14 @@ export class PokemonSprite extends Sprite {
|
||||||
}, this.subsp!), 500);
|
}, this.subsp!), 500);
|
||||||
|
|
||||||
this.$sub = null;
|
this.$sub = null;
|
||||||
this.anim({time: 500});
|
this.anim({ time: 500 });
|
||||||
if (this.scene.animating) this.scene.waitFor(this.$el);
|
if (this.scene.animating) this.scene.waitFor(this.$el);
|
||||||
}
|
}
|
||||||
beforeMove() {
|
beforeMove() {
|
||||||
if (!this.scene.animating) return false;
|
if (!this.scene.animating) return false;
|
||||||
if (!this.isSubActive) return false;
|
if (!this.isSubActive) return false;
|
||||||
this.isSubActive = false;
|
this.isSubActive = false;
|
||||||
this.anim({time: 300});
|
this.anim({ time: 300 });
|
||||||
this.$sub!.animate(this.scene.pos({
|
this.$sub!.animate(this.scene.pos({
|
||||||
x: this.leftof(-50),
|
x: this.leftof(-50),
|
||||||
y: this.y,
|
y: this.y,
|
||||||
|
|
@ -2082,7 +2082,7 @@ export class PokemonSprite extends Sprite {
|
||||||
z: this.behind(30),
|
z: this.behind(30),
|
||||||
opacity: 0.3,
|
opacity: 0.3,
|
||||||
}, this.sp), 300);
|
}, this.sp), 300);
|
||||||
this.anim({time: 300});
|
this.anim({ time: 300 });
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -2128,7 +2128,7 @@ export class PokemonSprite extends Sprite {
|
||||||
if (this.$el) {
|
if (this.$el) {
|
||||||
this.$el.stop(true, false);
|
this.$el.stop(true, false);
|
||||||
this.$el.remove();
|
this.$el.remove();
|
||||||
const $newEl = $('<img src="' + this.sp.url + '" style="display:none;position:absolute"' + (this.sp.pixelated ? ' class="pixelated"' : '') + ' />');
|
const $newEl = $(`<img src="${this.sp.url!}" style="display:none;position:absolute"${this.sp.pixelated ? ' class="pixelated"' : ''} />`);
|
||||||
this.$el = $newEl;
|
this.$el = $newEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2164,7 +2164,7 @@ export class PokemonSprite extends Sprite {
|
||||||
x: this.x,
|
x: this.x,
|
||||||
y: this.y,
|
y: this.y,
|
||||||
z: this.behind(30),
|
z: this.behind(30),
|
||||||
opacity: .3,
|
opacity: 0.3,
|
||||||
}, this.sp));
|
}, this.sp));
|
||||||
this.$sub.css(this.scene.pos({
|
this.$sub.css(this.scene.pos({
|
||||||
x: this.x,
|
x: this.x,
|
||||||
|
|
@ -2278,7 +2278,7 @@ export class PokemonSprite extends Sprite {
|
||||||
x: this.x,
|
x: this.x,
|
||||||
y: this.y + 30,
|
y: this.y + 30,
|
||||||
z: this.behind(50),
|
z: this.behind(50),
|
||||||
scale: .7,
|
scale: 0.7,
|
||||||
}, {
|
}, {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
x: this.x,
|
x: this.x,
|
||||||
|
|
@ -2409,7 +2409,7 @@ export class PokemonSprite extends Sprite {
|
||||||
left: this.statbarLeft - (this.isFrontSprite ? -100 : 100),
|
left: this.statbarLeft - (this.isFrontSprite ? -100 : 100),
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
}, 300 / this.scene.acceleration, () => {
|
}, 300 / this.scene.acceleration, () => {
|
||||||
$statbar!.remove();
|
$statbar.remove();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2447,7 +2447,7 @@ export class PokemonSprite extends Sprite {
|
||||||
x: this.x,
|
x: this.x,
|
||||||
y: this.y - 40,
|
y: this.y - 40,
|
||||||
z: this.z,
|
z: this.z,
|
||||||
scale: .7,
|
scale: 0.7,
|
||||||
time: 300 / this.scene.acceleration,
|
time: 300 / this.scene.acceleration,
|
||||||
}, {
|
}, {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
|
|
@ -2466,7 +2466,7 @@ export class PokemonSprite extends Sprite {
|
||||||
left: this.statbarLeft + (this.isFrontSprite ? 50 : -50),
|
left: this.statbarLeft + (this.isFrontSprite ? 50 : -50),
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
}, 300 / this.scene.acceleration, () => {
|
}, 300 / this.scene.acceleration, () => {
|
||||||
$statbar!.remove();
|
$statbar.remove();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2500,7 +2500,7 @@ export class PokemonSprite extends Sprite {
|
||||||
$statbar.animate({
|
$statbar.animate({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
}, 300, () => {
|
}, 300, () => {
|
||||||
$statbar!.remove();
|
$statbar.remove();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2617,7 +2617,7 @@ export class PokemonSprite extends Sprite {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
time: 100,
|
time: 100,
|
||||||
}).anim({
|
}).anim({
|
||||||
opacity: .4,
|
opacity: 0.4,
|
||||||
time: 300,
|
time: 300,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -2636,32 +2636,32 @@ export class PokemonSprite extends Sprite {
|
||||||
x: this.x - 30,
|
x: this.x - 30,
|
||||||
y: this.y - 40,
|
y: this.y - 40,
|
||||||
z: this.z,
|
z: this.z,
|
||||||
scale: .2,
|
scale: 0.2,
|
||||||
opacity: .6,
|
opacity: 0.6,
|
||||||
};
|
};
|
||||||
const pos2 = {
|
const pos2 = {
|
||||||
display: 'block',
|
display: 'block',
|
||||||
x: this.x + 40,
|
x: this.x + 40,
|
||||||
y: this.y - 35,
|
y: this.y - 35,
|
||||||
z: this.z,
|
z: this.z,
|
||||||
scale: .2,
|
scale: 0.2,
|
||||||
opacity: .6,
|
opacity: 0.6,
|
||||||
};
|
};
|
||||||
const pos3 = {
|
const pos3 = {
|
||||||
display: 'block',
|
display: 'block',
|
||||||
x: this.x + 20,
|
x: this.x + 20,
|
||||||
y: this.y - 25,
|
y: this.y - 25,
|
||||||
z: this.z,
|
z: this.z,
|
||||||
scale: .2,
|
scale: 0.2,
|
||||||
opacity: .6,
|
opacity: 0.6,
|
||||||
};
|
};
|
||||||
|
|
||||||
const leechseed1 = new Sprite(BattleEffects.energyball, pos1, this.scene);
|
const leechseed1 = new Sprite(BattleEffects.energyball, pos1, this.scene);
|
||||||
const leechseed2 = new Sprite(BattleEffects.energyball, pos2, this.scene);
|
const leechseed2 = new Sprite(BattleEffects.energyball, pos2, this.scene);
|
||||||
const leechseed3 = new Sprite(BattleEffects.energyball, pos3, this.scene);
|
const leechseed3 = new Sprite(BattleEffects.energyball, pos3, this.scene);
|
||||||
this.scene.$spritesFront[spriten].append(leechseed1.$el!);
|
this.scene.$spritesFront[spriten].append(leechseed1.$el);
|
||||||
this.scene.$spritesFront[spriten].append(leechseed2.$el!);
|
this.scene.$spritesFront[spriten].append(leechseed2.$el);
|
||||||
this.scene.$spritesFront[spriten].append(leechseed3.$el!);
|
this.scene.$spritesFront[spriten].append(leechseed3.$el);
|
||||||
this.effects['leechseed'] = [leechseed1, leechseed2, leechseed3];
|
this.effects['leechseed'] = [leechseed1, leechseed2, leechseed3];
|
||||||
} else if (id === 'protect' || id === 'magiccoat') {
|
} else if (id === 'protect' || id === 'magiccoat') {
|
||||||
const protect = new Sprite(BattleEffects.protect, {
|
const protect = new Sprite(BattleEffects.protect, {
|
||||||
|
|
@ -2671,15 +2671,15 @@ export class PokemonSprite extends Sprite {
|
||||||
z: this.behind(-15),
|
z: this.behind(-15),
|
||||||
xscale: 1,
|
xscale: 1,
|
||||||
yscale: 0,
|
yscale: 0,
|
||||||
opacity: .1,
|
opacity: 0.1,
|
||||||
}, this.scene);
|
}, this.scene);
|
||||||
this.scene.$spritesFront[spriten].append(protect.$el!);
|
this.scene.$spritesFront[spriten].append(protect.$el);
|
||||||
this.effects[id] = [protect];
|
this.effects[id] = [protect];
|
||||||
protect.anim({
|
protect.anim({
|
||||||
opacity: .9,
|
opacity: 0.9,
|
||||||
time: instant ? 0 : 400,
|
time: instant ? 0 : 400,
|
||||||
}).anim({
|
}).anim({
|
||||||
opacity: .4,
|
opacity: 0.4,
|
||||||
time: instant ? 0 : 300,
|
time: instant ? 0 : 300,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -2702,7 +2702,7 @@ export class PokemonSprite extends Sprite {
|
||||||
dogarsCheck(pokemon: Pokemon) {
|
dogarsCheck(pokemon: Pokemon) {
|
||||||
if (pokemon.side.isFar) return;
|
if (pokemon.side.isFar) return;
|
||||||
|
|
||||||
if (pokemon.speciesForme === 'Koffing' && pokemon.name.match(/dogars/i)) {
|
if (pokemon.speciesForme === 'Koffing' && (/dogars/i.exec(pokemon.name))) {
|
||||||
this.scene.setBgm(-1);
|
this.scene.setBgm(-1);
|
||||||
} else if (this.scene.bgmNum === -1) {
|
} else if (this.scene.bgmNum === -1) {
|
||||||
this.scene.rollBgm();
|
this.scene.rollBgm();
|
||||||
|
|
@ -2734,7 +2734,7 @@ export class PokemonSprite extends Sprite {
|
||||||
buf += (pokemon.level === 100 ? `` : ` <small>L${pokemon.level}</small>`);
|
buf += (pokemon.level === 100 ? `` : ` <small>L${pokemon.level}</small>`);
|
||||||
|
|
||||||
let symbol = '';
|
let symbol = '';
|
||||||
if (pokemon.speciesForme.indexOf('-Mega') >= 0) symbol = 'mega';
|
if (pokemon.speciesForme.includes('-Mega')) symbol = 'mega';
|
||||||
else if (pokemon.speciesForme === 'Kyogre-Primal') symbol = 'alpha';
|
else if (pokemon.speciesForme === 'Kyogre-Primal') symbol = 'alpha';
|
||||||
else if (pokemon.speciesForme === 'Groudon-Primal') symbol = 'omega';
|
else if (pokemon.speciesForme === 'Groudon-Primal') symbol = 'omega';
|
||||||
if (symbol) {
|
if (symbol) {
|
||||||
|
|
@ -2852,7 +2852,7 @@ export class PokemonSprite extends Sprite {
|
||||||
private static getEffectTag(id: string) {
|
private static getEffectTag(id: string) {
|
||||||
let effect = PokemonSprite.statusTable[id];
|
let effect = PokemonSprite.statusTable[id];
|
||||||
if (typeof effect === 'string') return effect;
|
if (typeof effect === 'string') return effect;
|
||||||
if (effect === null) return PokemonSprite.statusTable[id] = '';
|
if (effect === null) return (PokemonSprite.statusTable[id] = '');
|
||||||
if (effect === undefined) {
|
if (effect === undefined) {
|
||||||
let label = `[[${id}]]`;
|
let label = `[[${id}]]`;
|
||||||
if (Dex.species.get(id).exists) {
|
if (Dex.species.get(id).exists) {
|
||||||
|
|
@ -2868,7 +2868,7 @@ export class PokemonSprite extends Sprite {
|
||||||
}
|
}
|
||||||
effect = [label, 'neutral'];
|
effect = [label, 'neutral'];
|
||||||
}
|
}
|
||||||
return PokemonSprite.statusTable[id] = `<span class="${effect[1]}">${effect[0].replace(/ /g, ' ')}</span> `;
|
return (PokemonSprite.statusTable[id] = `<span class="${effect[1]}">${effect[0].replace(/ /g, ' ')}</span> `);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHPText(pokemon: Pokemon) {
|
updateHPText(pokemon: Pokemon) {
|
||||||
|
|
@ -2879,11 +2879,11 @@ export class PokemonSprite extends Sprite {
|
||||||
$hptext.hide();
|
$hptext.hide();
|
||||||
$hptextborder.hide();
|
$hptextborder.hide();
|
||||||
} else if (this.scene.battle.hardcoreMode || this.scene.battle.reportExactHP) {
|
} else if (this.scene.battle.hardcoreMode || this.scene.battle.reportExactHP) {
|
||||||
$hptext.html(pokemon.hp + '/');
|
$hptext.html(`${pokemon.hp}/`);
|
||||||
$hptext.show();
|
$hptext.show();
|
||||||
$hptextborder.show();
|
$hptextborder.show();
|
||||||
} else {
|
} else {
|
||||||
$hptext.html(pokemon.hpWidth(100) + '%');
|
$hptext.html(`${pokemon.hpWidth(100)}%`);
|
||||||
$hptext.show();
|
$hptext.show();
|
||||||
$hptextborder.show();
|
$hptextborder.show();
|
||||||
}
|
}
|
||||||
|
|
@ -2897,7 +2897,6 @@ export class PokemonSprite extends Sprite {
|
||||||
// slp: -webkit-filter: grayscale(100%);
|
// slp: -webkit-filter: grayscale(100%);
|
||||||
// frz: -webkit-filter: sepia(100%) hue-rotate(154deg) saturate(759%) brightness(23%);
|
// frz: -webkit-filter: sepia(100%) hue-rotate(154deg) saturate(759%) brightness(23%);
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
Object.assign($.easing, {
|
Object.assign($.easing, {
|
||||||
ballisticUp(x: number, t: number, b: number, c: number, d: number) {
|
ballisticUp(x: number, t: number, b: number, c: number, d: number) {
|
||||||
return -3 * x * x + 4 * x;
|
return -3 * x * x + 4 * x;
|
||||||
|
|
@ -2920,9 +2919,9 @@ interface AnimData {
|
||||||
prepareAnim?(scene: BattleScene, args: PokemonSprite[]): void;
|
prepareAnim?(scene: BattleScene, args: PokemonSprite[]): void;
|
||||||
residualAnim?(scene: BattleScene, args: PokemonSprite[]): void;
|
residualAnim?(scene: BattleScene, args: PokemonSprite[]): void;
|
||||||
}
|
}
|
||||||
export type AnimTable = {[k: string]: AnimData};
|
export type AnimTable = { [k: string]: AnimData };
|
||||||
|
|
||||||
const BattleEffects: {[k: string]: SpriteData} = {
|
const BattleEffects: { [k: string]: SpriteData } = {
|
||||||
wisp: {
|
wisp: {
|
||||||
url: 'wisp.png',
|
url: 'wisp.png',
|
||||||
w: 100, h: 100,
|
w: 100, h: 100,
|
||||||
|
|
@ -4266,16 +4265,16 @@ export const BattleOtherAnims: AnimTable = {
|
||||||
},
|
},
|
||||||
shake: {
|
shake: {
|
||||||
anim(scene, [attacker]) {
|
anim(scene, [attacker]) {
|
||||||
attacker.anim({x: attacker.x - 10, time: 200});
|
attacker.anim({ x: attacker.x - 10, time: 200 });
|
||||||
attacker.anim({x: attacker.x + 10, time: 300});
|
attacker.anim({ x: attacker.x + 10, time: 300 });
|
||||||
attacker.anim({x: attacker.x, time: 200});
|
attacker.anim({ x: attacker.x, time: 200 });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dance: {
|
dance: {
|
||||||
anim(scene, [attacker]) {
|
anim(scene, [attacker]) {
|
||||||
attacker.anim({x: attacker.x - 10});
|
attacker.anim({ x: attacker.x - 10 });
|
||||||
attacker.anim({x: attacker.x + 10});
|
attacker.anim({ x: attacker.x + 10 });
|
||||||
attacker.anim({x: attacker.x});
|
attacker.anim({ x: attacker.x });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
consume: {
|
consume: {
|
||||||
|
|
@ -6072,11 +6071,11 @@ export const BattleStatusAnims: AnimTable = {
|
||||||
anim(scene, [attacker]) {
|
anim(scene, [attacker]) {
|
||||||
scene.backgroundEffect('#000000', 700, 0.2);
|
scene.backgroundEffect('#000000', 700, 0.2);
|
||||||
attacker.delay(300);
|
attacker.delay(300);
|
||||||
attacker.anim({x: attacker.x - 5, time: 50});
|
attacker.anim({ x: attacker.x - 5, time: 50 });
|
||||||
attacker.anim({x: attacker.x + 5, time: 50});
|
attacker.anim({ x: attacker.x + 5, time: 50 });
|
||||||
attacker.anim({x: attacker.x - 5, time: 50});
|
attacker.anim({ x: attacker.x - 5, time: 50 });
|
||||||
attacker.anim({x: attacker.x + 5, time: 50});
|
attacker.anim({ x: attacker.x + 5, time: 50 });
|
||||||
attacker.anim({x: attacker.x, time: 50});
|
attacker.anim({ x: attacker.x, time: 50 });
|
||||||
|
|
||||||
scene.showEffect(attacker.sp, {
|
scene.showEffect(attacker.sp, {
|
||||||
x: attacker.x,
|
x: attacker.x,
|
||||||
|
|
@ -6172,4 +6171,4 @@ export const BattleStatusAnims: AnimTable = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
BattleStatusAnims['focuspunch'] = {anim: BattleStatusAnims['flinch'].anim};
|
BattleStatusAnims['focuspunch'] = { anim: BattleStatusAnims['flinch'].anim };
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {Battle, ServerPokemon} from "./battle";
|
import type { Battle, ServerPokemon } from "./battle";
|
||||||
import {Dex, toID, type ID} from "./battle-dex";
|
import { Dex, toID, type ID } from "./battle-dex";
|
||||||
|
|
||||||
export interface BattleRequestSideInfo {
|
export interface BattleRequestSideInfo {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -281,7 +281,7 @@ export class BattleChoiceBuilder {
|
||||||
|
|
||||||
const index = this.choices.length;
|
const index = this.choices.length;
|
||||||
|
|
||||||
if (choice === 'shift') return {choiceType: 'shift'};
|
if (choice === 'shift') return { choiceType: 'shift' };
|
||||||
|
|
||||||
if (choice.startsWith('move ')) {
|
if (choice.startsWith('move ')) {
|
||||||
if (request.requestType !== 'move') {
|
if (request.requestType !== 'move') {
|
||||||
|
|
@ -397,7 +397,7 @@ export class BattleChoiceBuilder {
|
||||||
const choiceid = toID(choice);
|
const choiceid = toID(choice);
|
||||||
let matchLevel = 0;
|
let matchLevel = 0;
|
||||||
let match = 0;
|
let match = 0;
|
||||||
for (let i = 0 ; i < request.side.pokemon.length; i++) {
|
for (let i = 0; i < request.side.pokemon.length; i++) {
|
||||||
const serverPokemon = request.side.pokemon[i];
|
const serverPokemon = request.side.pokemon[i];
|
||||||
let curMatchLevel = 0;
|
let curMatchLevel = 0;
|
||||||
if (choice === serverPokemon.name) {
|
if (choice === serverPokemon.name) {
|
||||||
|
|
@ -429,7 +429,7 @@ export class BattleChoiceBuilder {
|
||||||
throw new Error(`Couldn't find Pokémon "${choice}" to switch to!`);
|
throw new Error(`Couldn't find Pokémon "${choice}" to switch to!`);
|
||||||
}
|
}
|
||||||
if (target.fainted) {
|
if (target.fainted) {
|
||||||
throw new Error(`${target} is fainted and cannot battle!`);
|
throw new Error(`${target.name} is fainted and cannot battle!`);
|
||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,19 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Dex, toID} from "./battle-dex";
|
import { Dex, toID } from "./battle-dex";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String that contains only lowercase alphanumeric characters.
|
* String that contains only lowercase alphanumeric characters.
|
||||||
*/
|
*/
|
||||||
export type ID = string & {__isID: true};
|
export type ID = string & { __isID: true };
|
||||||
|
|
||||||
export interface Nature {
|
export interface Nature {
|
||||||
plus?: StatNameExceptHP;
|
plus?: StatNameExceptHP;
|
||||||
minus?: StatNameExceptHP;
|
minus?: StatNameExceptHP;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BattleNatures: {[k in NatureName]: Nature} = {
|
export const BattleNatures: { [k in NatureName]: Nature } = {
|
||||||
Adamant: {
|
Adamant: {
|
||||||
plus: 'atk',
|
plus: 'atk',
|
||||||
minus: 'spa',
|
minus: 'spa',
|
||||||
|
|
@ -113,7 +113,7 @@ export const BattleNatures: {[k in NatureName]: Nature} = {
|
||||||
minus: 'atk',
|
minus: 'atk',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export const BattleStatIDs: {[k: string]: StatName | undefined} = {
|
export const BattleStatIDs: { [k: string]: StatName | undefined } = {
|
||||||
HP: 'hp',
|
HP: 'hp',
|
||||||
hp: 'hp',
|
hp: 'hp',
|
||||||
Atk: 'atk',
|
Atk: 'atk',
|
||||||
|
|
@ -148,7 +148,7 @@ export const BattleBaseSpeciesChart = [
|
||||||
"unown", "burmy", "shellos", "gastrodon", "deerling", "sawsbuck", "vivillon", "flabebe", "floette", "florges", "furfrou", "minior", "alcremie", "tatsugiri", "pokestarufo", "pokestarbrycenman", "pokestarmt", "pokestarmt2", "pokestartransport", "pokestargiant", "pokestarhumanoid", "pokestarmonster", "pokestarf00", "pokestarf002", "pokestarspirit", "pokestarblackdoor", "pokestarwhitedoor", "pokestarblackbelt",
|
"unown", "burmy", "shellos", "gastrodon", "deerling", "sawsbuck", "vivillon", "flabebe", "floette", "florges", "furfrou", "minior", "alcremie", "tatsugiri", "pokestarufo", "pokestarbrycenman", "pokestarmt", "pokestarmt2", "pokestartransport", "pokestargiant", "pokestarhumanoid", "pokestarmonster", "pokestarf00", "pokestarf002", "pokestarspirit", "pokestarblackdoor", "pokestarwhitedoor", "pokestarblackbelt",
|
||||||
] as ID[];
|
] as ID[];
|
||||||
|
|
||||||
export const BattlePokemonIconIndexes: {[id: string]: number} = {
|
export const BattlePokemonIconIndexes: { [id: string]: number } = {
|
||||||
// alt forms
|
// alt forms
|
||||||
egg: 1032 + 1,
|
egg: 1032 + 1,
|
||||||
pikachubelle: 1032 + 2,
|
pikachubelle: 1032 + 2,
|
||||||
|
|
@ -628,7 +628,7 @@ export const BattlePokemonIconIndexes: {[id: string]: number} = {
|
||||||
draggalong: 1512 + 77,
|
draggalong: 1512 + 77,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BattlePokemonIconIndexesLeft: {[id: string]: number} = {
|
export const BattlePokemonIconIndexesLeft: { [id: string]: number } = {
|
||||||
pikachubelle: 1404 + 0,
|
pikachubelle: 1404 + 0,
|
||||||
pikachupopstar: 1404 + 1,
|
pikachupopstar: 1404 + 1,
|
||||||
clefairy: 1404 + 2,
|
clefairy: 1404 + 2,
|
||||||
|
|
@ -738,7 +738,7 @@ export const BattlePokemonIconIndexesLeft: {[id: string]: number} = {
|
||||||
blacephalon: 1404 + 105,
|
blacephalon: 1404 + 105,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BattleAvatarNumbers: {[k: string]: string} = {
|
export const BattleAvatarNumbers: { [k: string]: string } = {
|
||||||
1: 'lucas',
|
1: 'lucas',
|
||||||
2: 'dawn',
|
2: 'dawn',
|
||||||
3: 'youngster-gen4dp',
|
3: 'youngster-gen4dp',
|
||||||
|
|
@ -1240,10 +1240,10 @@ export class Move implements Effect {
|
||||||
readonly zMove?: {
|
readonly zMove?: {
|
||||||
basePower?: number,
|
basePower?: number,
|
||||||
effect?: string,
|
effect?: string,
|
||||||
boost?: {[stat in StatName]?: number},
|
boost?: { [stat in StatName]?: number },
|
||||||
};
|
};
|
||||||
readonly isMax: boolean | string;
|
readonly isMax: boolean | string;
|
||||||
readonly maxMove: {basePower: number};
|
readonly maxMove: { basePower: number };
|
||||||
readonly ohko: true | 'Ice' | null;
|
readonly ohko: true | 'Ice' | null;
|
||||||
readonly recoil: number[] | null;
|
readonly recoil: number[] | null;
|
||||||
readonly heal: number[] | null;
|
readonly heal: number[] | null;
|
||||||
|
|
@ -1252,7 +1252,7 @@ export class Move implements Effect {
|
||||||
readonly basePowerCallback: boolean;
|
readonly basePowerCallback: boolean;
|
||||||
readonly noPPBoosts: boolean;
|
readonly noPPBoosts: boolean;
|
||||||
readonly status: string;
|
readonly status: string;
|
||||||
readonly secondaries: ReadonlyArray<any> | null;
|
readonly secondaries: readonly any[] | null;
|
||||||
readonly num: number;
|
readonly num: number;
|
||||||
|
|
||||||
constructor(id: ID, name: string, data: any) {
|
constructor(id: ID, name: string, data: any) {
|
||||||
|
|
@ -1291,43 +1291,43 @@ export class Move implements Effect {
|
||||||
this.secondaries = data.secondaries || (data.secondary ? [data.secondary] : null);
|
this.secondaries = data.secondaries || (data.secondary ? [data.secondary] : null);
|
||||||
|
|
||||||
this.isMax = data.isMax || false;
|
this.isMax = data.isMax || false;
|
||||||
this.maxMove = data.maxMove || {basePower: 0};
|
this.maxMove = data.maxMove || { basePower: 0 };
|
||||||
if (this.category !== 'Status' && !this.maxMove?.basePower) {
|
if (this.category !== 'Status' && !this.maxMove?.basePower) {
|
||||||
if (this.isZ || this.isMax) {
|
if (this.isZ || this.isMax) {
|
||||||
this.maxMove = {basePower: 1};
|
this.maxMove = { basePower: 1 };
|
||||||
} else if (!this.basePower) {
|
} else if (!this.basePower) {
|
||||||
this.maxMove = {basePower: 100};
|
this.maxMove = { basePower: 100 };
|
||||||
} else if (['Fighting', 'Poison'].includes(this.type)) {
|
} else if (['Fighting', 'Poison'].includes(this.type)) {
|
||||||
if (this.basePower >= 150) {
|
if (this.basePower >= 150) {
|
||||||
this.maxMove = {basePower: 100};
|
this.maxMove = { basePower: 100 };
|
||||||
} else if (this.basePower >= 110) {
|
} else if (this.basePower >= 110) {
|
||||||
this.maxMove = {basePower: 95};
|
this.maxMove = { basePower: 95 };
|
||||||
} else if (this.basePower >= 75) {
|
} else if (this.basePower >= 75) {
|
||||||
this.maxMove = {basePower: 90};
|
this.maxMove = { basePower: 90 };
|
||||||
} else if (this.basePower >= 65) {
|
} else if (this.basePower >= 65) {
|
||||||
this.maxMove = {basePower: 85};
|
this.maxMove = { basePower: 85 };
|
||||||
} else if (this.basePower >= 55) {
|
} else if (this.basePower >= 55) {
|
||||||
this.maxMove = {basePower: 80};
|
this.maxMove = { basePower: 80 };
|
||||||
} else if (this.basePower >= 45) {
|
} else if (this.basePower >= 45) {
|
||||||
this.maxMove = {basePower: 75};
|
this.maxMove = { basePower: 75 };
|
||||||
} else {
|
} else {
|
||||||
this.maxMove = {basePower: 70};
|
this.maxMove = { basePower: 70 };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.basePower >= 150) {
|
if (this.basePower >= 150) {
|
||||||
this.maxMove = {basePower: 150};
|
this.maxMove = { basePower: 150 };
|
||||||
} else if (this.basePower >= 110) {
|
} else if (this.basePower >= 110) {
|
||||||
this.maxMove = {basePower: 140};
|
this.maxMove = { basePower: 140 };
|
||||||
} else if (this.basePower >= 75) {
|
} else if (this.basePower >= 75) {
|
||||||
this.maxMove = {basePower: 130};
|
this.maxMove = { basePower: 130 };
|
||||||
} else if (this.basePower >= 65) {
|
} else if (this.basePower >= 65) {
|
||||||
this.maxMove = {basePower: 120};
|
this.maxMove = { basePower: 120 };
|
||||||
} else if (this.basePower >= 55) {
|
} else if (this.basePower >= 55) {
|
||||||
this.maxMove = {basePower: 110};
|
this.maxMove = { basePower: 110 };
|
||||||
} else if (this.basePower >= 45) {
|
} else if (this.basePower >= 45) {
|
||||||
this.maxMove = {basePower: 100};
|
this.maxMove = { basePower: 100 };
|
||||||
} else {
|
} else {
|
||||||
this.maxMove = {basePower: 90};
|
this.maxMove = { basePower: 90 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1468,7 +1468,7 @@ export class Species implements Effect {
|
||||||
|
|
||||||
// basic data
|
// basic data
|
||||||
readonly num: number;
|
readonly num: number;
|
||||||
readonly types: ReadonlyArray<TypeName>;
|
readonly types: readonly TypeName[];
|
||||||
readonly abilities: Readonly<{
|
readonly abilities: Readonly<{
|
||||||
0: string, 1?: string, H?: string, S?: string,
|
0: string, 1?: string, H?: string, S?: string,
|
||||||
}>;
|
}>;
|
||||||
|
|
@ -1482,21 +1482,21 @@ export class Species implements Effect {
|
||||||
readonly heightm: number;
|
readonly heightm: number;
|
||||||
readonly gender: GenderName;
|
readonly gender: GenderName;
|
||||||
readonly color: string;
|
readonly color: string;
|
||||||
readonly genderRatio: Readonly<{M: number, F: number}> | null;
|
readonly genderRatio: Readonly<{ M: number, F: number }> | null;
|
||||||
readonly eggGroups: ReadonlyArray<string>;
|
readonly eggGroups: readonly string[];
|
||||||
readonly tags: ReadonlyArray<string>;
|
readonly tags: readonly string[];
|
||||||
|
|
||||||
// format data
|
// format data
|
||||||
readonly otherFormes: ReadonlyArray<string> | null;
|
readonly otherFormes: readonly string[] | null;
|
||||||
readonly cosmeticFormes: ReadonlyArray<string> | null;
|
readonly cosmeticFormes: readonly string[] | null;
|
||||||
readonly evos: ReadonlyArray<string> | null;
|
readonly evos: readonly string[] | null;
|
||||||
readonly prevo: string;
|
readonly prevo: string;
|
||||||
readonly evoType: 'trade' | 'useItem' | 'levelMove' | 'levelExtra' | 'levelFriendship' | 'levelHold' | 'other' | '';
|
readonly evoType: 'trade' | 'useItem' | 'levelMove' | 'levelExtra' | 'levelFriendship' | 'levelHold' | 'other' | '';
|
||||||
readonly evoLevel: number;
|
readonly evoLevel: number;
|
||||||
readonly evoMove: string;
|
readonly evoMove: string;
|
||||||
readonly evoItem: string;
|
readonly evoItem: string;
|
||||||
readonly evoCondition: string;
|
readonly evoCondition: string;
|
||||||
readonly requiredItems: ReadonlyArray<string>;
|
readonly requiredItems: readonly string[];
|
||||||
readonly tier: string;
|
readonly tier: string;
|
||||||
readonly isTotem: boolean;
|
readonly isTotem: boolean;
|
||||||
readonly isMega: boolean;
|
readonly isMega: boolean;
|
||||||
|
|
@ -1521,15 +1521,15 @@ export class Species implements Effect {
|
||||||
const baseId = toID(this.baseSpecies);
|
const baseId = toID(this.baseSpecies);
|
||||||
this.formeid = (baseId === this.id ? '' : '-' + toID(this.forme));
|
this.formeid = (baseId === this.id ? '' : '-' + toID(this.forme));
|
||||||
this.spriteid = baseId + this.formeid;
|
this.spriteid = baseId + this.formeid;
|
||||||
if (this.spriteid.slice(-5) === 'totem') this.spriteid = this.spriteid.slice(0, -5);
|
if (this.spriteid.endsWith('totem')) this.spriteid = this.spriteid.slice(0, -5);
|
||||||
if (this.spriteid === 'greninja-bond') this.spriteid = 'greninja';
|
if (this.spriteid === 'greninja-bond') this.spriteid = 'greninja';
|
||||||
if (this.spriteid.slice(-1) === '-') this.spriteid = this.spriteid.slice(0, -1);
|
if (this.spriteid.endsWith('-')) this.spriteid = this.spriteid.slice(0, -1);
|
||||||
this.baseForme = data.baseForme || '';
|
this.baseForme = data.baseForme || '';
|
||||||
|
|
||||||
this.num = data.num || 0;
|
this.num = data.num || 0;
|
||||||
this.types = data.types || ['???'];
|
this.types = data.types || ['???'];
|
||||||
this.abilities = data.abilities || {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.baseStats = data.baseStats || { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
||||||
this.bst = this.baseStats.hp + this.baseStats.atk + this.baseStats.def +
|
this.bst = this.baseStats.hp + this.baseStats.atk + this.baseStats.def +
|
||||||
this.baseStats.spa + this.baseStats.spd + this.baseStats.spe;
|
this.baseStats.spa + this.baseStats.spd + this.baseStats.spe;
|
||||||
this.weightkg = data.weightkg || 0;
|
this.weightkg = data.weightkg || 0;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Dex, ModdedDex, toID, type ID} from "./battle-dex";
|
import { Dex, type ModdedDex, toID, type ID } from "./battle-dex";
|
||||||
|
|
||||||
type SearchType = (
|
type SearchType = (
|
||||||
'pokemon' | 'type' | 'tier' | 'move' | 'item' | 'ability' | 'egggroup' | 'category' | 'article'
|
'pokemon' | 'type' | 'tier' | 'move' | 'item' | 'ability' | 'egggroup' | 'category' | 'article'
|
||||||
|
|
@ -217,7 +217,7 @@ export class DexSearch {
|
||||||
|
|
||||||
/** searching for "Psychic type" will make the type come up over the move */
|
/** searching for "Psychic type" will make the type come up over the move */
|
||||||
let qFilterType: 'type' | '' = '';
|
let qFilterType: 'type' | '' = '';
|
||||||
if (query.slice(-4) === 'type') {
|
if (query.endsWith('type')) {
|
||||||
if (query.slice(0, -4) in window.BattleTypeChart) {
|
if (query.slice(0, -4) in window.BattleTypeChart) {
|
||||||
query = query.slice(0, -4);
|
query = query.slice(0, -4);
|
||||||
qFilterType = 'type';
|
qFilterType = 'type';
|
||||||
|
|
@ -265,7 +265,7 @@ export class DexSearch {
|
||||||
// the alias text before any other passes.
|
// the alias text before any other passes.
|
||||||
let queryAlias;
|
let queryAlias;
|
||||||
if (query in BattleAliases) {
|
if (query in BattleAliases) {
|
||||||
if (['sub', 'tr'].includes(query) || toID(BattleAliases[query]).slice(0, query.length) !== query) {
|
if (['sub', 'tr'].includes(query) || !toID(BattleAliases[query]).startsWith(query)) {
|
||||||
queryAlias = toID(BattleAliases[query]);
|
queryAlias = toID(BattleAliases[query]);
|
||||||
let aliasPassType: SearchPassType = (queryAlias === 'hiddenpower' ? 'exact' : 'normal');
|
let aliasPassType: SearchPassType = (queryAlias === 'hiddenpower' ? 'exact' : 'normal');
|
||||||
searchPasses.unshift([aliasPassType, DexSearch.getClosest(queryAlias), queryAlias]);
|
searchPasses.unshift([aliasPassType, DexSearch.getClosest(queryAlias), queryAlias]);
|
||||||
|
|
@ -552,8 +552,8 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
set: Dex.PokemonSet | null = null;
|
set: Dex.PokemonSet | null = null;
|
||||||
|
|
||||||
protected formatType: 'doubles' | 'bdsp' | 'bdspdoubles' | 'bw1' | 'letsgo' | 'metronome' | 'natdex' | 'nfe' |
|
protected formatType: 'doubles' | 'bdsp' | 'bdspdoubles' | 'bw1' | 'letsgo' | 'metronome' | 'natdex' | 'nfe' |
|
||||||
'ssdlc1' | 'ssdlc1doubles' | 'predlc' | 'predlcdoubles' | 'predlcnatdex' | 'svdlc1' | 'svdlc1doubles' |
|
'ssdlc1' | 'ssdlc1doubles' | 'predlc' | 'predlcdoubles' | 'predlcnatdex' | 'svdlc1' | 'svdlc1doubles' |
|
||||||
'svdlc1natdex' | 'stadium' | 'lc' | null = null;
|
'svdlc1natdex' | 'stadium' | 'lc' | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cached copy of what the results list would be with only base filters
|
* Cached copy of what the results list would be with only base filters
|
||||||
|
|
@ -565,7 +565,7 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
* is wondering why a specific result isn't showing up.
|
* is wondering why a specific result isn't showing up.
|
||||||
*/
|
*/
|
||||||
baseIllegalResults: SearchRow[] | null = null;
|
baseIllegalResults: SearchRow[] | null = null;
|
||||||
illegalReasons: {[id: string]: string} | null = null;
|
illegalReasons: { [id: string]: string } | null = null;
|
||||||
results: SearchRow[] | null = null;
|
results: SearchRow[] | null = null;
|
||||||
|
|
||||||
protected readonly sortRow: SearchRow | null = null;
|
protected readonly sortRow: SearchRow | null = null;
|
||||||
|
|
@ -576,7 +576,7 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
this.baseResults = null;
|
this.baseResults = null;
|
||||||
this.baseIllegalResults = null;
|
this.baseIllegalResults = null;
|
||||||
|
|
||||||
if (format.slice(0, 3) === 'gen') {
|
if (format.startsWith('gen')) {
|
||||||
const gen = (Number(format.charAt(3)) || 6);
|
const gen = (Number(format.charAt(3)) || 6);
|
||||||
format = (format.slice(4) || 'customgame') as ID;
|
format = (format.slice(4) || 'customgame') as ID;
|
||||||
this.dex = Dex.forGen(gen);
|
this.dex = Dex.forGen(gen);
|
||||||
|
|
@ -668,10 +668,10 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
if (typeof speciesOrSet === 'string') {
|
if (typeof speciesOrSet === 'string') {
|
||||||
if (speciesOrSet) this.species = speciesOrSet;
|
if (speciesOrSet) this.species = speciesOrSet;
|
||||||
} else {
|
} else {
|
||||||
this.set = speciesOrSet as Dex.PokemonSet;
|
this.set = speciesOrSet;
|
||||||
this.species = toID(this.set.species);
|
this.species = toID(this.set.species);
|
||||||
}
|
}
|
||||||
if (!searchType || !this.set) return;
|
// if (!searchType || !this.set) return;
|
||||||
}
|
}
|
||||||
getResults(filters?: SearchFilter[] | null, sortCol?: string | null, reverseSort?: boolean): SearchRow[] {
|
getResults(filters?: SearchFilter[] | null, sortCol?: string | null, reverseSort?: boolean): SearchRow[] {
|
||||||
if (sortCol === 'type') {
|
if (sortCol === 'type') {
|
||||||
|
|
@ -687,7 +687,7 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.baseIllegalResults) {
|
if (!this.baseIllegalResults) {
|
||||||
const legalityFilter: {[id: string]: 1} = {};
|
const legalityFilter: { [id: string]: 1 } = {};
|
||||||
for (const [resultType, value] of this.baseResults) {
|
for (const [resultType, value] of this.baseResults) {
|
||||||
if (resultType === this.searchType) legalityFilter[value] = 1;
|
if (resultType === this.searchType) legalityFilter[value] = 1;
|
||||||
}
|
}
|
||||||
|
|
@ -742,7 +742,7 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
if (this.sortRow) {
|
if (this.sortRow) {
|
||||||
results = [this.sortRow, ...results];
|
results = [this.sortRow, ...results];
|
||||||
}
|
}
|
||||||
if (illegalResults && illegalResults.length) {
|
if (illegalResults?.length) {
|
||||||
results = [...results, ['header', "Illegal results"], ...illegalResults];
|
results = [...results, ['header', "Illegal results"], ...illegalResults];
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
|
|
@ -825,7 +825,7 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
if (learnset && (moveid in learnset) && (!this.format.startsWith('tradebacks') ? learnset[moveid].includes(genChar) :
|
if (learnset && (moveid in learnset) && (!this.format.startsWith('tradebacks') ? learnset[moveid].includes(genChar) :
|
||||||
learnset[moveid].includes(genChar) || (learnset[moveid].includes(`${gen + 1}`) && move.gen === gen)) &&
|
learnset[moveid].includes(genChar) || (learnset[moveid].includes(`${gen + 1}`) && move.gen === gen)) &&
|
||||||
(!eggMovesOnly || (learnset[moveid].includes('e') && this.dex.gen === 9))
|
(!eggMovesOnly || (learnset[moveid].includes('e') && this.dex.gen === 9))
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
learnsetid = this.nextLearnsetid(learnsetid, speciesid, true);
|
learnsetid = this.nextLearnsetid(learnsetid, speciesid, true);
|
||||||
|
|
@ -849,14 +849,14 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
this.formatType === 'ssdlc1doubles' ? 'gen8dlc1doubles' :
|
this.formatType === 'ssdlc1doubles' ? 'gen8dlc1doubles' :
|
||||||
this.formatType === 'predlc' ? 'gen9predlc' :
|
this.formatType === 'predlc' ? 'gen9predlc' :
|
||||||
this.formatType === 'predlcdoubles' ? 'gen9predlcdoubles' :
|
this.formatType === 'predlcdoubles' ? 'gen9predlcdoubles' :
|
||||||
this.formatType === 'predlcnatdex' ? 'gen9predlcnatdex' :
|
this.formatType === 'predlcnatdex' ? 'gen9predlcnatdex' :
|
||||||
this.formatType === 'svdlc1' ? 'gen9dlc1' :
|
this.formatType === 'svdlc1' ? 'gen9dlc1' :
|
||||||
this.formatType === 'svdlc1doubles' ? 'gen9dlc1doubles' :
|
this.formatType === 'svdlc1doubles' ? 'gen9dlc1doubles' :
|
||||||
this.formatType === 'svdlc1natdex' ? 'gen9dlc1natdex' :
|
this.formatType === 'svdlc1natdex' ? 'gen9dlc1natdex' :
|
||||||
this.formatType === 'natdex' ? `gen${gen}natdex` :
|
this.formatType === 'natdex' ? `gen${gen}natdex` :
|
||||||
this.formatType === 'stadium' ? `gen${gen}stadium${gen > 1 ? gen : ''}` :
|
this.formatType === 'stadium' ? `gen${gen}stadium${gen > 1 ? gen : ''}` :
|
||||||
`gen${gen}`;
|
`gen${gen}`;
|
||||||
if (table && table[tableKey]) {
|
if (table?.[tableKey]) {
|
||||||
table = table[tableKey];
|
table = table[tableKey];
|
||||||
}
|
}
|
||||||
if (!table) return pokemon.tier;
|
if (!table) return pokemon.tier;
|
||||||
|
|
@ -865,7 +865,7 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
if (id in table.overrideTier) {
|
if (id in table.overrideTier) {
|
||||||
return table.overrideTier[id];
|
return table.overrideTier[id];
|
||||||
}
|
}
|
||||||
if (id.slice(-5) === 'totem' && id.slice(0, -5) in table.overrideTier) {
|
if (id.endsWith('totem') && id.slice(0, -5) in table.overrideTier) {
|
||||||
return table.overrideTier[id.slice(0, -5)];
|
return table.overrideTier[id.slice(0, -5)];
|
||||||
}
|
}
|
||||||
id = toID(pokemon.baseSpecies);
|
id = toID(pokemon.baseSpecies);
|
||||||
|
|
@ -884,7 +884,7 @@ abstract class BattleTypedSearch<T extends SearchType> {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
abstract getTable(): {[id: string]: any};
|
abstract getTable(): { [id: string]: any };
|
||||||
abstract getDefaultResults(): SearchRow[];
|
abstract getDefaultResults(): SearchRow[];
|
||||||
abstract getBaseResults(): SearchRow[];
|
abstract getBaseResults(): SearchRow[];
|
||||||
abstract filter(input: SearchRow, filters: string[][]): boolean;
|
abstract filter(input: SearchRow, filters: string[][]): boolean;
|
||||||
|
|
@ -951,13 +951,13 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
|
||||||
|
|
||||||
let table = BattleTeambuilderTable;
|
let table = BattleTeambuilderTable;
|
||||||
if ((format.endsWith('cap') || format.endsWith('caplc')) && dex.gen < 9) {
|
if ((format.endsWith('cap') || format.endsWith('caplc')) && dex.gen < 9) {
|
||||||
table = table['gen' + dex.gen];
|
table = table[`gen${dex.gen}`];
|
||||||
} else if (isVGCOrBS) {
|
} else if (isVGCOrBS) {
|
||||||
table = table['gen' + dex.gen + 'vgc'];
|
table = table[`gen${dex.gen}vgc`];
|
||||||
} else if (dex.gen === 9 && isHackmons && !this.formatType) {
|
} else if (dex.gen === 9 && isHackmons && !this.formatType) {
|
||||||
table = table['bh'];
|
table = table['bh'];
|
||||||
} else if (
|
} else if (
|
||||||
table['gen' + dex.gen + 'doubles'] && dex.gen > 4 &&
|
table[`gen${dex.gen}doubles`] && dex.gen > 4 &&
|
||||||
this.formatType !== 'letsgo' && this.formatType !== 'bdspdoubles' &&
|
this.formatType !== 'letsgo' && this.formatType !== 'bdspdoubles' &&
|
||||||
this.formatType !== 'ssdlc1doubles' && this.formatType !== 'predlcdoubles' &&
|
this.formatType !== 'ssdlc1doubles' && this.formatType !== 'predlcdoubles' &&
|
||||||
this.formatType !== 'svdlc1doubles' && !this.formatType?.includes('natdex') &&
|
this.formatType !== 'svdlc1doubles' && !this.formatType?.includes('natdex') &&
|
||||||
|
|
@ -967,10 +967,10 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
|
||||||
format === 'partnersincrime'
|
format === 'partnersincrime'
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
table = table['gen' + dex.gen + 'doubles'];
|
table = table[`gen${dex.gen}doubles`];
|
||||||
isDoublesOrBS = true;
|
isDoublesOrBS = true;
|
||||||
} else if (dex.gen < 9 && !this.formatType) {
|
} else if (dex.gen < 9 && !this.formatType) {
|
||||||
table = table['gen' + dex.gen];
|
table = table[`gen${dex.gen}`];
|
||||||
} else if (this.formatType?.startsWith('bdsp')) {
|
} else if (this.formatType?.startsWith('bdsp')) {
|
||||||
table = table['gen8' + this.formatType];
|
table = table['gen8' + this.formatType];
|
||||||
} else if (this.formatType === 'letsgo') {
|
} else if (this.formatType === 'letsgo') {
|
||||||
|
|
@ -978,13 +978,13 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
|
||||||
} else if (this.formatType === 'bw1') {
|
} else if (this.formatType === 'bw1') {
|
||||||
table = table['gen5bw1'];
|
table = table['gen5bw1'];
|
||||||
} else if (this.formatType === 'natdex') {
|
} else if (this.formatType === 'natdex') {
|
||||||
table = table['gen' + dex.gen + 'natdex'];
|
table = table[`gen${dex.gen}natdex`];
|
||||||
} else if (this.formatType === 'metronome') {
|
} else if (this.formatType === 'metronome') {
|
||||||
table = table['gen' + dex.gen + 'metronome'];
|
table = table[`gen${dex.gen}metronome`];
|
||||||
} else if (this.formatType === 'nfe') {
|
} else if (this.formatType === 'nfe') {
|
||||||
table = table['gen' + dex.gen + 'nfe'];
|
table = table[`gen${dex.gen}nfe`];
|
||||||
} else if (this.formatType === 'lc') {
|
} else if (this.formatType === 'lc') {
|
||||||
table = table['gen' + dex.gen + 'lc'];
|
table = table[`gen${dex.gen}lc`];
|
||||||
} else if (this.formatType?.startsWith('ssdlc1')) {
|
} else if (this.formatType?.startsWith('ssdlc1')) {
|
||||||
if (this.formatType.includes('doubles')) {
|
if (this.formatType.includes('doubles')) {
|
||||||
table = table['gen8dlc1doubles'];
|
table = table['gen8dlc1doubles'];
|
||||||
|
|
@ -1008,7 +1008,7 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
|
||||||
table = table['gen9dlc1'];
|
table = table['gen9dlc1'];
|
||||||
}
|
}
|
||||||
} else if (this.formatType === 'stadium') {
|
} else if (this.formatType === 'stadium') {
|
||||||
table = table['gen' + dex.gen + 'stadium' + (dex.gen > 1 ? dex.gen : '')];
|
table = table[`gen${dex.gen}stadium${dex.gen > 1 ? dex.gen : ''}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!table.tierSet) {
|
if (!table.tierSet) {
|
||||||
|
|
@ -1019,7 +1019,7 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
|
||||||
table.tiers = null;
|
table.tiers = null;
|
||||||
}
|
}
|
||||||
let tierSet: SearchRow[] = table.tierSet;
|
let tierSet: SearchRow[] = table.tierSet;
|
||||||
let slices: {[k: string]: number} = table.formatSlices;
|
let slices: { [k: string]: number } = table.formatSlices;
|
||||||
if (format === 'ubers' || format === 'uber' || format === 'ubersuu' || format === 'nationaldexdoubles') {
|
if (format === 'ubers' || format === 'uber' || format === 'ubersuu' || format === 'nationaldexdoubles') {
|
||||||
tierSet = tierSet.slice(slices.Uber);
|
tierSet = tierSet.slice(slices.Uber);
|
||||||
} else if (isVGCOrBS || (isHackmons && dex.gen === 9 && !this.formatType)) {
|
} else if (isVGCOrBS || (isHackmons && dex.gen === 9 && !this.formatType)) {
|
||||||
|
|
@ -1049,7 +1049,9 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
|
||||||
else if (format === 'pu') tierSet = tierSet.slice(slices.PU || slices.NU);
|
else if (format === 'pu') tierSet = tierSet.slice(slices.PU || slices.NU);
|
||||||
else if (format === 'zu' && dex.gen === 5) tierSet = tierSet.slice(slices.PU || slices.NU);
|
else if (format === 'zu' && dex.gen === 5) tierSet = tierSet.slice(slices.PU || slices.NU);
|
||||||
else if (format === 'zu') tierSet = tierSet.slice(slices.ZU || slices.PU || slices.NU);
|
else if (format === 'zu') tierSet = tierSet.slice(slices.ZU || slices.PU || slices.NU);
|
||||||
else if (format === 'lc' || format === 'lcuu' || format.startsWith('lc') || (format !== 'caplc' && format.endsWith('lc'))) tierSet = tierSet.slice(slices.LC);
|
else if (
|
||||||
|
format === 'lc' || format === 'lcuu' || format.startsWith('lc') || (format !== 'caplc' && format.endsWith('lc'))
|
||||||
|
) tierSet = tierSet.slice(slices.LC);
|
||||||
else if (format === 'cap' || format.endsWith('cap')) {
|
else if (format === 'cap' || format.endsWith('cap')) {
|
||||||
tierSet = tierSet.slice(0, slices.AG || slices.Uber).concat(tierSet.slice(slices.OU));
|
tierSet = tierSet.slice(0, slices.AG || slices.Uber).concat(tierSet.slice(slices.OU));
|
||||||
} else if (format === 'caplc') {
|
} else if (format === 'caplc') {
|
||||||
|
|
@ -1276,11 +1278,11 @@ class BattleItemSearch extends BattleTypedSearch<'item'> {
|
||||||
} else if (this.formatType === 'bw1') {
|
} else if (this.formatType === 'bw1') {
|
||||||
table = table['gen5bw1'];
|
table = table['gen5bw1'];
|
||||||
} else if (this.formatType === 'natdex') {
|
} else if (this.formatType === 'natdex') {
|
||||||
table = table['gen' + this.dex.gen + 'natdex'];
|
table = table[`gen${this.dex.gen}natdex`];
|
||||||
} else if (this.formatType === 'metronome') {
|
} else if (this.formatType === 'metronome') {
|
||||||
table = table['gen' + this.dex.gen + 'metronome'];
|
table = table[`gen${this.dex.gen}metronome`];
|
||||||
} else if (this.dex.gen < 9) {
|
} else if (this.dex.gen < 9) {
|
||||||
table = table['gen' + this.dex.gen];
|
table = table[`gen${this.dex.gen}`];
|
||||||
}
|
}
|
||||||
if (!table.itemSet) {
|
if (!table.itemSet) {
|
||||||
table.itemSet = table.items.map((r: any) => {
|
table.itemSet = table.items.map((r: any) => {
|
||||||
|
|
@ -1635,7 +1637,7 @@ class BattleMoveSearch extends BattleTypedSearch<'move'> {
|
||||||
let moves: string[] = [];
|
let moves: string[] = [];
|
||||||
let sketchMoves: string[] = [];
|
let sketchMoves: string[] = [];
|
||||||
let sketch = false;
|
let sketch = false;
|
||||||
let gen = '' + dex.gen;
|
let gen = `${dex.gen}`;
|
||||||
let lsetTable = BattleTeambuilderTable;
|
let lsetTable = BattleTeambuilderTable;
|
||||||
if (this.formatType?.startsWith('bdsp')) lsetTable = lsetTable['gen8bdsp'];
|
if (this.formatType?.startsWith('bdsp')) lsetTable = lsetTable['gen8bdsp'];
|
||||||
if (this.formatType === 'letsgo') lsetTable = lsetTable['gen7letsgo'];
|
if (this.formatType === 'letsgo') lsetTable = lsetTable['gen7letsgo'];
|
||||||
|
|
@ -1649,7 +1651,7 @@ class BattleMoveSearch extends BattleTypedSearch<'move'> {
|
||||||
for (let moveid in learnset) {
|
for (let moveid in learnset) {
|
||||||
let learnsetEntry = learnset[moveid];
|
let learnsetEntry = learnset[moveid];
|
||||||
const move = dex.moves.get(moveid);
|
const move = dex.moves.get(moveid);
|
||||||
const minGenCode: {[gen: number]: string} = {6: 'p', 7: 'q', 8: 'g', 9: 'a'};
|
const minGenCode: { [gen: number]: string } = { 6: 'p', 7: 'q', 8: 'g', 9: 'a' };
|
||||||
if (regionBornLegality && !learnsetEntry.includes(minGenCode[dex.gen])) {
|
if (regionBornLegality && !learnsetEntry.includes(minGenCode[dex.gen])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1661,7 +1663,7 @@ class BattleMoveSearch extends BattleTypedSearch<'move'> {
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!learnsetEntry.includes(gen) &&
|
!learnsetEntry.includes(gen) &&
|
||||||
(!isTradebacks ? true : !(move.gen <= dex.gen && learnsetEntry.includes('' + (dex.gen + 1))))
|
(!isTradebacks ? true : !(move.gen <= dex.gen && learnsetEntry.includes(`${dex.gen + 1}`)))
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1819,7 +1821,7 @@ class BattleMoveSearch extends BattleTypedSearch<'move'> {
|
||||||
const sortOrder = reverseSort ? -1 : 1;
|
const sortOrder = reverseSort ? -1 : 1;
|
||||||
switch (sortCol) {
|
switch (sortCol) {
|
||||||
case 'power':
|
case 'power':
|
||||||
let powerTable: {[id: string]: number | undefined} = {
|
let powerTable: { [id: string]: number | undefined } = {
|
||||||
return: 102, frustration: 102, spitup: 300, trumpcard: 200, naturalgift: 80, grassknot: 120,
|
return: 102, frustration: 102, spitup: 300, trumpcard: 200, naturalgift: 80, grassknot: 120,
|
||||||
lowkick: 120, gyroball: 150, electroball: 150, flail: 200, reversal: 200, present: 120,
|
lowkick: 120, gyroball: 150, electroball: 150, flail: 200, reversal: 200, present: 120,
|
||||||
wringout: 120, crushgrip: 120, heatcrash: 120, heavyslam: 120, fling: 130, magnitude: 150,
|
wringout: 120, crushgrip: 120, heatcrash: 120, heavyslam: 120, fling: 130, magnitude: 150,
|
||||||
|
|
@ -1861,7 +1863,7 @@ class BattleMoveSearch extends BattleTypedSearch<'move'> {
|
||||||
|
|
||||||
class BattleCategorySearch extends BattleTypedSearch<'category'> {
|
class BattleCategorySearch extends BattleTypedSearch<'category'> {
|
||||||
getTable() {
|
getTable() {
|
||||||
return {physical: 1, special: 1, status: 1};
|
return { physical: 1, special: 1, status: 1 };
|
||||||
}
|
}
|
||||||
getDefaultResults(): SearchRow[] {
|
getDefaultResults(): SearchRow[] {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -18,16 +18,15 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pokemon, type ServerPokemon} from "./battle";
|
import { Pokemon, type ServerPokemon } from "./battle";
|
||||||
import {
|
import {
|
||||||
BattleAvatarNumbers, BattleBaseSpeciesChart, BattlePokemonIconIndexes, BattlePokemonIconIndexesLeft, BattleStatNames,
|
BattleAvatarNumbers, BattleBaseSpeciesChart, BattlePokemonIconIndexes, BattlePokemonIconIndexesLeft, BattleStatNames,
|
||||||
Ability, Item, Move, Species, PureEffect, type ID, type Type,
|
Ability, Item, Move, Species, PureEffect, type ID, type Type,
|
||||||
} from "./battle-dex-data";
|
} from "./battle-dex-data";
|
||||||
// tslint:disable-next-line
|
import type * as DexData from "./battle-dex-data";
|
||||||
import * as DexData from "./battle-dex-data";
|
|
||||||
|
|
||||||
export declare namespace Dex {
|
export declare namespace Dex {
|
||||||
/* tslint:disable:no-shadowed-variable */
|
/* eslint-disable @typescript-eslint/no-shadow */
|
||||||
export type Ability = DexData.Ability;
|
export type Ability = DexData.Ability;
|
||||||
export type Item = DexData.Item;
|
export type Item = DexData.Item;
|
||||||
export type Move = DexData.Move;
|
export type Move = DexData.Move;
|
||||||
|
|
@ -37,7 +36,7 @@ export declare namespace Dex {
|
||||||
export type PureEffect = DexData.PureEffect;
|
export type PureEffect = DexData.PureEffect;
|
||||||
export type Effect = DexData.Effect;
|
export type Effect = DexData.Effect;
|
||||||
export type ID = DexData.ID;
|
export type ID = DexData.ID;
|
||||||
/* tslint:enable:no-shadowed-variable */
|
/* eslint-enable @typescript-eslint/no-shadow */
|
||||||
export type StatName = DexData.StatName;
|
export type StatName = DexData.StatName;
|
||||||
export type StatNameExceptHP = DexData.StatNameExceptHP;
|
export type StatNameExceptHP = DexData.StatNameExceptHP;
|
||||||
export type BoostStatName = DexData.BoostStatName;
|
export type BoostStatName = DexData.BoostStatName;
|
||||||
|
|
@ -46,7 +45,7 @@ export declare namespace Dex {
|
||||||
export type GenderName = DexData.GenderName;
|
export type GenderName = DexData.GenderName;
|
||||||
export type NatureName = DexData.NatureName;
|
export type NatureName = DexData.NatureName;
|
||||||
export type MoveTarget = DexData.MoveTarget;
|
export type MoveTarget = DexData.MoveTarget;
|
||||||
export type StatsTable = {hp: number, atk: number, def: number, spa: number, spd: number, spe: number};
|
export type StatsTable = { hp: number, atk: number, def: number, spa: number, spd: number, spe: number };
|
||||||
/**
|
/**
|
||||||
* Dex.PokemonSet can be sparse, in which case that entry should be
|
* Dex.PokemonSet can be sparse, in which case that entry should be
|
||||||
* inferred from the rest of the set, according to sensible
|
* inferred from the rest of the set, according to sensible
|
||||||
|
|
@ -87,10 +86,11 @@ export declare namespace Dex {
|
||||||
teraType?: string;
|
teraType?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export type {ID};
|
export type { ID };
|
||||||
|
|
||||||
declare const require: any;
|
declare const require: any;
|
||||||
declare const global: any;
|
declare const global: any;
|
||||||
|
declare const process: any;
|
||||||
|
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
// Node
|
// Node
|
||||||
|
|
@ -100,8 +100,7 @@ if (typeof window === 'undefined') {
|
||||||
window.exports = window;
|
window.exports = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
window.nodewebkit = !!(typeof process !== 'undefined' && process.versions?.['node-webkit']);
|
||||||
window.nodewebkit = !!(typeof process !== 'undefined' && process.versions && process.versions['node-webkit']);
|
|
||||||
|
|
||||||
export function toID(text: any) {
|
export function toID(text: any) {
|
||||||
if (text?.id) {
|
if (text?.id) {
|
||||||
|
|
@ -110,14 +109,14 @@ export function toID(text: any) {
|
||||||
text = text.userid;
|
text = text.userid;
|
||||||
}
|
}
|
||||||
if (typeof text !== 'string' && typeof text !== 'number') return '' as ID;
|
if (typeof text !== 'string' && typeof text !== 'number') return '' as ID;
|
||||||
return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '') as ID;
|
return `${text}`.toLowerCase().replace(/[^a-z0-9]+/g, '') as ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toUserid(text: any) {
|
export function toUserid(text: any) {
|
||||||
return toID(text);
|
return toID(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
type Comparable = number | string | boolean | Comparable[] | {reverse: Comparable};
|
type Comparable = number | string | boolean | Comparable[] | { reverse: Comparable };
|
||||||
export const PSUtils = new class {
|
export const PSUtils = new class {
|
||||||
/**
|
/**
|
||||||
* Like string.split(delimiter), but only recognizes the first `limit`
|
* Like string.split(delimiter), but only recognizes the first `limit`
|
||||||
|
|
@ -129,7 +128,7 @@ export const PSUtils = new class {
|
||||||
*
|
*
|
||||||
* Returns an array of length exactly limit + 1.
|
* Returns an array of length exactly limit + 1.
|
||||||
*/
|
*/
|
||||||
splitFirst(str: string, delimiter: string, limit: number = 1) {
|
splitFirst(str: string, delimiter: string, limit = 1) {
|
||||||
let splitStr: string[] = [];
|
let splitStr: string[] = [];
|
||||||
while (splitStr.length < limit) {
|
while (splitStr.length < limit) {
|
||||||
let delimiterIndex = str.indexOf(delimiter);
|
let delimiterIndex = str.indexOf(delimiter);
|
||||||
|
|
@ -174,9 +173,9 @@ export const PSUtils = new class {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (a.reverse) {
|
if (a.reverse) {
|
||||||
return PSUtils.compare((b as {reverse: string}).reverse, a.reverse);
|
return PSUtils.compare((b as { reverse: string }).reverse, a.reverse);
|
||||||
}
|
}
|
||||||
throw new Error(`Passed value ${a} is not comparable`);
|
throw new Error(`Passed value ${a as any} is not comparable`);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sorts an array according to the callback's output on its elements.
|
* Sorts an array according to the callback's output on its elements.
|
||||||
|
|
@ -206,7 +205,7 @@ export function toRoomid(roomid: string) {
|
||||||
|
|
||||||
export function toName(name: any) {
|
export function toName(name: any) {
|
||||||
if (typeof name !== 'string' && typeof name !== 'number') return '';
|
if (typeof name !== 'string' && typeof name !== 'number') return '';
|
||||||
name = ('' + name).replace(/[\|\s\[\]\,\u202e]+/g, ' ').trim();
|
name = `${name}`.replace(/[|\s[\],\u202e]+/g, ' ').trim();
|
||||||
if (name.length > 18) name = name.substr(0, 18).trim();
|
if (name.length > 18) name = name.substr(0, 18).trim();
|
||||||
|
|
||||||
// remove zalgo
|
// remove zalgo
|
||||||
|
|
@ -250,8 +249,8 @@ export const Dex = new class implements ModdedDex {
|
||||||
readonly modid = 'gen9' as ID;
|
readonly modid = 'gen9' as ID;
|
||||||
readonly cache = null!;
|
readonly cache = null!;
|
||||||
|
|
||||||
readonly statNames: ReadonlyArray<Dex.StatName> = ['hp', 'atk', 'def', 'spa', 'spd', 'spe'];
|
readonly statNames: readonly Dex.StatName[] = ['hp', 'atk', 'def', 'spa', 'spd', 'spe'];
|
||||||
readonly statNamesExceptHP: ReadonlyArray<Dex.StatNameExceptHP> = ['atk', 'def', 'spa', 'spd', 'spe'];
|
readonly statNamesExceptHP: readonly Dex.StatNameExceptHP[] = ['atk', 'def', 'spa', 'spd', 'spe'];
|
||||||
|
|
||||||
pokeballs: string[] | null = null;
|
pokeballs: string[] | null = null;
|
||||||
|
|
||||||
|
|
@ -266,8 +265,8 @@ export const Dex = new class implements ModdedDex {
|
||||||
return `${protocol}//${window.Config ? Config.routes.client : 'play.pokemonshowdown.com'}/fx/`;
|
return `${protocol}//${window.Config ? Config.routes.client : 'play.pokemonshowdown.com'}/fx/`;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
loadedSpriteData = {xy: 1, bw: 0};
|
loadedSpriteData = { xy: 1, bw: 0 };
|
||||||
moddedDexes: {[mod: string]: ModdedDex} = {};
|
moddedDexes: { [mod: string]: ModdedDex } = {};
|
||||||
|
|
||||||
mod(modid: ID): ModdedDex {
|
mod(modid: ID): ModdedDex {
|
||||||
if (modid === 'gen9') return this;
|
if (modid === 'gen9') return this;
|
||||||
|
|
@ -287,14 +286,14 @@ export const Dex = new class implements ModdedDex {
|
||||||
if (window.BattleAvatarNumbers && avatar in BattleAvatarNumbers) {
|
if (window.BattleAvatarNumbers && avatar in BattleAvatarNumbers) {
|
||||||
avatar = BattleAvatarNumbers[avatar];
|
avatar = BattleAvatarNumbers[avatar];
|
||||||
}
|
}
|
||||||
if (avatar.charAt(0) === '#') {
|
if (avatar.startsWith('#')) {
|
||||||
return Dex.resourcePrefix + 'sprites/trainers-custom/' + toID(avatar.substr(1)) + '.png';
|
return Dex.resourcePrefix + 'sprites/trainers-custom/' + toID(avatar.substr(1)) + '.png';
|
||||||
}
|
}
|
||||||
if (avatar.includes('.') && window.Config?.server?.registered) {
|
if (avatar.includes('.') && window.Config?.server?.registered) {
|
||||||
// custom avatar served by the server
|
// custom avatar served by the server
|
||||||
let protocol = (Config.server.port === 443) ? 'https' : 'http';
|
let protocol = (Config.server.port === 443) ? 'https' : 'http';
|
||||||
return protocol + '://' + Config.server.host + ':' + Config.server.port +
|
return protocol + '://' + Config.server.host + ':' + Config.server.port +
|
||||||
'/avatars/' + encodeURIComponent(avatar).replace(/\%3F/g, '?');
|
'/avatars/' + encodeURIComponent(avatar).replace(/%3F/g, '?');
|
||||||
}
|
}
|
||||||
return Dex.resourcePrefix + 'sprites/trainers/' + Dex.sanitizeName(avatar || 'unknown') + '.png';
|
return Dex.resourcePrefix + 'sprites/trainers/' + Dex.sanitizeName(avatar || 'unknown') + '.png';
|
||||||
}
|
}
|
||||||
|
|
@ -318,14 +317,14 @@ export const Dex = new class implements ModdedDex {
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs(prop: string) {
|
prefs(prop: string) {
|
||||||
// @ts-ignore
|
// @ts-expect-error this is what I get for calling it Storage...
|
||||||
return window.Storage?.prefs?.(prop);
|
return window.Storage?.prefs?.(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
getShortName(name: string) {
|
getShortName(name: string) {
|
||||||
let shortName = name.replace(/[^A-Za-z0-9]+$/, '');
|
let shortName = name.replace(/[^A-Za-z0-9]+$/, '');
|
||||||
if (shortName.indexOf('(') >= 0) {
|
if (shortName.includes('(')) {
|
||||||
shortName += name.slice(shortName.length).replace(/[^\(\)]+/g, '').replace(/\(\)/g, '');
|
shortName += name.slice(shortName.length).replace(/[^()]+/g, '').replace(/\(\)/g, '');
|
||||||
}
|
}
|
||||||
return shortName;
|
return shortName;
|
||||||
}
|
}
|
||||||
|
|
@ -379,7 +378,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data) data = {exists: false};
|
if (!data) data = { exists: false };
|
||||||
let move = new Move(id, name, data);
|
let move = new Move(id, name, data);
|
||||||
window.BattleMovedex[id] = move;
|
window.BattleMovedex[id] = move;
|
||||||
return move;
|
return move;
|
||||||
|
|
@ -407,7 +406,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
if (!window.BattleItems) window.BattleItems = {};
|
if (!window.BattleItems) window.BattleItems = {};
|
||||||
let data = window.BattleItems[id];
|
let data = window.BattleItems[id];
|
||||||
if (data && typeof data.exists === 'boolean') return data;
|
if (data && typeof data.exists === 'boolean') return data;
|
||||||
if (!data) data = {exists: false};
|
if (!data) data = { exists: false };
|
||||||
let item = new Item(id, name, data);
|
let item = new Item(id, name, data);
|
||||||
window.BattleItems[id] = item;
|
window.BattleItems[id] = item;
|
||||||
return item;
|
return item;
|
||||||
|
|
@ -429,7 +428,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
if (!window.BattleAbilities) window.BattleAbilities = {};
|
if (!window.BattleAbilities) window.BattleAbilities = {};
|
||||||
let data = window.BattleAbilities[id];
|
let data = window.BattleAbilities[id];
|
||||||
if (data && typeof data.exists === 'boolean') return data;
|
if (data && typeof data.exists === 'boolean') return data;
|
||||||
if (!data) data = {exists: false};
|
if (!data) data = { exists: false };
|
||||||
let ability = new Ability(id, name, data);
|
let ability = new Ability(id, name, data);
|
||||||
window.BattleAbilities[id] = ability;
|
window.BattleAbilities[id] = ability;
|
||||||
return ability;
|
return ability;
|
||||||
|
|
@ -465,8 +464,8 @@ export const Dex = new class implements ModdedDex {
|
||||||
if (data && typeof data.exists === 'boolean') {
|
if (data && typeof data.exists === 'boolean') {
|
||||||
species = data;
|
species = data;
|
||||||
} else {
|
} else {
|
||||||
if (!data) data = {exists: false};
|
if (!data) data = { exists: false };
|
||||||
if (!data.tier && id.slice(-5) === 'totem') {
|
if (!data.tier && id.endsWith('totem')) {
|
||||||
data.tier = this.species.get(id.slice(0, -5)).tier;
|
data.tier = this.species.get(id.slice(0, -5)).tier;
|
||||||
}
|
}
|
||||||
if (!data.tier && data.baseSpecies && toID(data.baseSpecies) !== id) {
|
if (!data.tier && data.baseSpecies && toID(data.baseSpecies) !== id) {
|
||||||
|
|
@ -503,7 +502,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
if (!type || typeof type === 'string') {
|
if (!type || typeof type === 'string') {
|
||||||
const id = toID(type) as string;
|
const id = toID(type) as string;
|
||||||
const name = id.substr(0, 1).toUpperCase() + id.substr(1);
|
const name = id.substr(0, 1).toUpperCase() + id.substr(1);
|
||||||
type = (window.BattleTypeChart && window.BattleTypeChart[id]) || {};
|
type = window.BattleTypeChart?.[id] || {};
|
||||||
if (type.damageTaken) type.exists = true;
|
if (type.damageTaken) type.exists = true;
|
||||||
if (!type.id) type.id = id;
|
if (!type.id) type.id = id;
|
||||||
if (!type.name) type.name = name;
|
if (!type.name) type.name = name;
|
||||||
|
|
@ -525,14 +524,13 @@ export const Dex = new class implements ModdedDex {
|
||||||
isName: (name: string | null): boolean => {
|
isName: (name: string | null): boolean => {
|
||||||
const id = toID(name);
|
const id = toID(name);
|
||||||
if (name !== id.substr(0, 1).toUpperCase() + id.substr(1)) return false;
|
if (name !== id.substr(0, 1).toUpperCase() + id.substr(1)) return false;
|
||||||
return (window.BattleTypeChart || {}).hasOwnProperty(id);
|
return window.BattleTypeChart?.hasOwnProperty(id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
hasAbility(species: Species, ability: string) {
|
hasAbility(species: Species, ability: string) {
|
||||||
for (const i in species.abilities) {
|
for (const i in species.abilities) {
|
||||||
// @ts-ignore
|
if (ability === species.abilities[i as '0']) return true;
|
||||||
if (ability === species.abilities[i]) return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -543,7 +541,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
|
|
||||||
let path = $('script[src*="pokedex-mini.js"]').attr('src') || '';
|
let path = $('script[src*="pokedex-mini.js"]').attr('src') || '';
|
||||||
let qs = '?' + (path.split('?')[1] || '');
|
let qs = '?' + (path.split('?')[1] || '');
|
||||||
path = (path.match(/.+?(?=data\/pokedex-mini\.js)/) || [])[0] || '';
|
path = ((/.+?(?=data\/pokedex-mini\.js)/.exec(path)) || [])[0] || '';
|
||||||
|
|
||||||
let el = document.createElement('script');
|
let el = document.createElement('script');
|
||||||
el.src = path + 'data/pokedex-mini-bw.js' + qs;
|
el.src = path + 'data/pokedex-mini-bw.js' + qs;
|
||||||
|
|
@ -557,7 +555,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
noScale?: boolean,
|
noScale?: boolean,
|
||||||
mod?: string,
|
mod?: string,
|
||||||
dynamax?: boolean,
|
dynamax?: boolean,
|
||||||
} = {gen: 6}) {
|
} = { gen: 6 }) {
|
||||||
const mechanicsGen = options.gen || 6;
|
const mechanicsGen = options.gen || 6;
|
||||||
let isDynamax = !!options.dynamax;
|
let isDynamax = !!options.dynamax;
|
||||||
if (pokemon instanceof Pokemon) {
|
if (pokemon instanceof Pokemon) {
|
||||||
|
|
@ -706,7 +704,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
let allowAnim = !Dex.prefs('noanim') && !Dex.prefs('nogif');
|
let allowAnim = !Dex.prefs('noanim') && !Dex.prefs('nogif');
|
||||||
if (allowAnim && spriteData.gen >= 6) spriteData.pixelated = false;
|
if (allowAnim && spriteData.gen >= 6) spriteData.pixelated = false;
|
||||||
if (allowAnim && animationData[facing] && spriteData.gen >= 5) {
|
if (allowAnim && animationData[facing] && spriteData.gen >= 5) {
|
||||||
if (facing.slice(-1) === 'f') name += '-f';
|
if (facing.endsWith('f')) name += '-f';
|
||||||
dir = baseDir + 'ani' + dir;
|
dir = baseDir + 'ani' + dir;
|
||||||
|
|
||||||
spriteData.w = animationData[facing].w;
|
spriteData.w = animationData[facing].w;
|
||||||
|
|
@ -794,31 +792,32 @@ export const Dex = new class implements ModdedDex {
|
||||||
|
|
||||||
let id = toID(pokemon);
|
let id = toID(pokemon);
|
||||||
if (!pokemon || typeof pokemon === 'string') pokemon = null;
|
if (!pokemon || typeof pokemon === 'string') pokemon = null;
|
||||||
// @ts-ignore
|
// @ts-expect-error safe, but too lazy to cast
|
||||||
if (pokemon?.speciesForme) id = toID(pokemon.speciesForme);
|
if (pokemon?.speciesForme) id = toID(pokemon.speciesForme);
|
||||||
// @ts-ignore
|
// @ts-expect-error safe, but too lazy to cast
|
||||||
if (pokemon?.species) id = toID(pokemon.species);
|
if (pokemon?.species) id = toID(pokemon.species);
|
||||||
// @ts-ignore
|
// @ts-expect-error safe, but too lazy to cast
|
||||||
if (pokemon?.volatiles?.formechange && !pokemon.volatiles.transform) {
|
if (pokemon?.volatiles?.formechange && !pokemon.volatiles.transform) {
|
||||||
// @ts-ignore
|
// @ts-expect-error safe, but too lazy to cast
|
||||||
id = toID(pokemon.volatiles.formechange[1]);
|
id = toID(pokemon.volatiles.formechange[1]);
|
||||||
}
|
}
|
||||||
let num = this.getPokemonIconNum(id, pokemon?.gender === 'F', facingLeft);
|
let num = this.getPokemonIconNum(id, pokemon?.gender === 'F', facingLeft);
|
||||||
|
|
||||||
let top = Math.floor(num / 12) * 30;
|
let top = Math.floor(num / 12) * 30;
|
||||||
let left = (num % 12) * 40;
|
let left = (num % 12) * 40;
|
||||||
let fainted = ((pokemon as Pokemon | ServerPokemon)?.fainted ? `;opacity:.3;filter:grayscale(100%) brightness(.5)` : ``);
|
let fainted = ((pokemon as Pokemon | ServerPokemon)?.fainted ?
|
||||||
|
`;opacity:.3;filter:grayscale(100%) brightness(.5)` : ``);
|
||||||
return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-sheet.png?v18) no-repeat scroll -${left}px -${top}px${fainted}`;
|
return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-sheet.png?v18) no-repeat scroll -${left}px -${top}px${fainted}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTeambuilderSpriteData(pokemon: any, gen: number = 0): TeambuilderSpriteData {
|
getTeambuilderSpriteData(pokemon: any, gen = 0): TeambuilderSpriteData {
|
||||||
let id = toID(pokemon.species);
|
let id = toID(pokemon.species);
|
||||||
let spriteid = pokemon.spriteid;
|
let spriteid = pokemon.spriteid;
|
||||||
let species = Dex.species.get(pokemon.species);
|
let species = Dex.species.get(pokemon.species);
|
||||||
if (pokemon.species && !spriteid) {
|
if (pokemon.species && !spriteid) {
|
||||||
spriteid = species.spriteid || toID(pokemon.species);
|
spriteid = species.spriteid || toID(pokemon.species);
|
||||||
}
|
}
|
||||||
if (species.exists === false) return { spriteDir: 'sprites/gen5', spriteid: '0', x: 10, y: 5 };
|
if (!species.exists) return { spriteDir: 'sprites/gen5', spriteid: '0', x: 10, y: 5 };
|
||||||
if (window.Config?.server?.afd || Dex.prefs('afd')) {
|
if (window.Config?.server?.afd || Dex.prefs('afd')) {
|
||||||
return {
|
return {
|
||||||
spriteid,
|
spriteid,
|
||||||
|
|
@ -867,11 +866,11 @@ export const Dex = new class implements ModdedDex {
|
||||||
return spriteData;
|
return spriteData;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTeambuilderSprite(pokemon: any, gen: number = 0) {
|
getTeambuilderSprite(pokemon: any, gen = 0) {
|
||||||
if (!pokemon) return '';
|
if (!pokemon) return '';
|
||||||
const data = this.getTeambuilderSpriteData(pokemon, gen);
|
const data = this.getTeambuilderSpriteData(pokemon, gen);
|
||||||
const shiny = (data.shiny ? '-shiny' : '');
|
const shiny = (data.shiny ? '-shiny' : '');
|
||||||
return 'background-image:url(' + Dex.resourcePrefix + data.spriteDir + shiny + '/' + data.spriteid + '.png);background-position:' + data.x + 'px ' + data.y + 'px;background-repeat:no-repeat';
|
return `background-image:url(${Dex.resourcePrefix}${data.spriteDir}${shiny}/${data.spriteid}.png);background-position:${data.x}px ${data.y}px;background-repeat:no-repeat`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemIcon(item: any) {
|
getItemIcon(item: any) {
|
||||||
|
|
@ -881,7 +880,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
|
|
||||||
let top = Math.floor(num / 16) * 24;
|
let top = Math.floor(num / 16) * 24;
|
||||||
let left = (num % 16) * 24;
|
let left = (num % 16) * 24;
|
||||||
return 'background:transparent url(' + Dex.resourcePrefix + 'sprites/itemicons-sheet.png?v1) no-repeat scroll -' + left + 'px -' + top + 'px';
|
return `background:transparent url(${Dex.resourcePrefix}sprites/itemicons-sheet.png?v1) no-repeat scroll -${left}px -${top}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTypeIcon(type: string | null, b?: boolean) { // b is just for utilichart.js
|
getTypeIcon(type: string | null, b?: boolean) { // b is just for utilichart.js
|
||||||
|
|
@ -911,7 +910,7 @@ export const Dex = new class implements ModdedDex {
|
||||||
if (this.pokeballs) return this.pokeballs;
|
if (this.pokeballs) return this.pokeballs;
|
||||||
this.pokeballs = [];
|
this.pokeballs = [];
|
||||||
if (!window.BattleItems) window.BattleItems = {};
|
if (!window.BattleItems) window.BattleItems = {};
|
||||||
for (const data of Object.values(window.BattleItems) as AnyObject[]) {
|
for (const data of Object.values<AnyObject>(window.BattleItems)) {
|
||||||
if (!data.isPokeball) continue;
|
if (!data.isPokeball) continue;
|
||||||
this.pokeballs.push(data.name);
|
this.pokeballs.push(data.name);
|
||||||
}
|
}
|
||||||
|
|
@ -923,11 +922,11 @@ export class ModdedDex {
|
||||||
readonly gen: number;
|
readonly gen: number;
|
||||||
readonly modid: ID;
|
readonly modid: ID;
|
||||||
readonly cache = {
|
readonly cache = {
|
||||||
Moves: {} as any as {[k: string]: Move},
|
Moves: {} as { [k: string]: Move },
|
||||||
Items: {} as any as {[k: string]: Item},
|
Items: {} as { [k: string]: Item },
|
||||||
Abilities: {} as any as {[k: string]: Ability},
|
Abilities: {} as { [k: string]: Ability },
|
||||||
Species: {} as any as {[k: string]: Species},
|
Species: {} as { [k: string]: Species },
|
||||||
Types: {} as any as {[k: string]: Dex.Effect},
|
Types: {} as { [k: string]: Dex.Effect },
|
||||||
};
|
};
|
||||||
pokeballs: string[] | null = null;
|
pokeballs: string[] | null = null;
|
||||||
constructor(modid: ID) {
|
constructor(modid: ID) {
|
||||||
|
|
@ -945,7 +944,7 @@ export class ModdedDex {
|
||||||
}
|
}
|
||||||
if (this.cache.Moves.hasOwnProperty(id)) return this.cache.Moves[id];
|
if (this.cache.Moves.hasOwnProperty(id)) return this.cache.Moves[id];
|
||||||
|
|
||||||
let data = {...Dex.moves.get(name)};
|
let data = { ...Dex.moves.get(name) };
|
||||||
|
|
||||||
for (let i = Dex.gen - 1; i >= this.gen; i--) {
|
for (let i = Dex.gen - 1; i >= this.gen; i--) {
|
||||||
const table = window.BattleTeambuilderTable[`gen${i}`];
|
const table = window.BattleTeambuilderTable[`gen${i}`];
|
||||||
|
|
@ -978,7 +977,7 @@ export class ModdedDex {
|
||||||
}
|
}
|
||||||
if (this.cache.Items.hasOwnProperty(id)) return this.cache.Items[id];
|
if (this.cache.Items.hasOwnProperty(id)) return this.cache.Items[id];
|
||||||
|
|
||||||
let data = {...Dex.items.get(name)};
|
let data = { ...Dex.items.get(name) };
|
||||||
|
|
||||||
for (let i = Dex.gen - 1; i >= this.gen; i--) {
|
for (let i = Dex.gen - 1; i >= this.gen; i--) {
|
||||||
const table = window.BattleTeambuilderTable[`gen${i}`];
|
const table = window.BattleTeambuilderTable[`gen${i}`];
|
||||||
|
|
@ -1008,7 +1007,7 @@ export class ModdedDex {
|
||||||
}
|
}
|
||||||
if (this.cache.Abilities.hasOwnProperty(id)) return this.cache.Abilities[id];
|
if (this.cache.Abilities.hasOwnProperty(id)) return this.cache.Abilities[id];
|
||||||
|
|
||||||
let data = {...Dex.abilities.get(name)};
|
let data = { ...Dex.abilities.get(name) };
|
||||||
|
|
||||||
for (let i = Dex.gen - 1; i >= this.gen; i--) {
|
for (let i = Dex.gen - 1; i >= this.gen; i--) {
|
||||||
const table = window.BattleTeambuilderTable[`gen${i}`];
|
const table = window.BattleTeambuilderTable[`gen${i}`];
|
||||||
|
|
@ -1038,7 +1037,7 @@ export class ModdedDex {
|
||||||
}
|
}
|
||||||
if (this.cache.Species.hasOwnProperty(id)) return this.cache.Species[id];
|
if (this.cache.Species.hasOwnProperty(id)) return this.cache.Species[id];
|
||||||
|
|
||||||
let data = {...Dex.species.get(name)};
|
let data = { ...Dex.species.get(name) };
|
||||||
|
|
||||||
for (let i = Dex.gen - 1; i >= this.gen; i--) {
|
for (let i = Dex.gen - 1; i >= this.gen; i--) {
|
||||||
const table = window.BattleTeambuilderTable[`gen${i}`];
|
const table = window.BattleTeambuilderTable[`gen${i}`];
|
||||||
|
|
@ -1053,12 +1052,12 @@ export class ModdedDex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.gen < 3 || this.modid === 'gen7letsgo') {
|
if (this.gen < 3 || this.modid === 'gen7letsgo') {
|
||||||
data.abilities = {0: "No Ability"};
|
data.abilities = { 0: "No Ability" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const table = window.BattleTeambuilderTable[this.modid];
|
const table = window.BattleTeambuilderTable[this.modid];
|
||||||
if (id in table.overrideTier) data.tier = table.overrideTier[id];
|
if (id in table.overrideTier) data.tier = table.overrideTier[id];
|
||||||
if (!data.tier && id.slice(-5) === 'totem') {
|
if (!data.tier && id.endsWith('totem')) {
|
||||||
data.tier = this.species.get(id.slice(0, -5)).tier;
|
data.tier = this.species.get(id.slice(0, -5)).tier;
|
||||||
}
|
}
|
||||||
if (!data.tier && data.baseSpecies && toID(data.baseSpecies) !== id) {
|
if (!data.tier && data.baseSpecies && toID(data.baseSpecies) !== id) {
|
||||||
|
|
@ -1074,22 +1073,22 @@ export class ModdedDex {
|
||||||
|
|
||||||
types = {
|
types = {
|
||||||
get: (name: string): Dex.Effect => {
|
get: (name: string): Dex.Effect => {
|
||||||
const id = toID(name) as ID;
|
const id = toID(name);
|
||||||
name = id.substr(0, 1).toUpperCase() + id.substr(1);
|
name = id.substr(0, 1).toUpperCase() + id.substr(1);
|
||||||
|
|
||||||
if (this.cache.Types.hasOwnProperty(id)) return this.cache.Types[id];
|
if (this.cache.Types.hasOwnProperty(id)) return this.cache.Types[id];
|
||||||
|
|
||||||
let data = {...Dex.types.get(name)};
|
let data = { ...Dex.types.get(name) };
|
||||||
|
|
||||||
for (let i = 7; i >= this.gen; i--) {
|
for (let i = 7; i >= this.gen; i--) {
|
||||||
const table = window.BattleTeambuilderTable['gen' + i];
|
const table = window.BattleTeambuilderTable[`gen${i}`];
|
||||||
if (id in table.removeType) {
|
if (id in table.removeType) {
|
||||||
data.exists = false;
|
data.exists = false;
|
||||||
// don't bother correcting its attributes given it doesn't exist
|
// don't bother correcting its attributes given it doesn't exist
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (id in table.overrideTypeChart) {
|
if (id in table.overrideTypeChart) {
|
||||||
data = {...data, ...table.overrideTypeChart[id]};
|
data = { ...data, ...table.overrideTypeChart[id] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1102,7 +1101,7 @@ export class ModdedDex {
|
||||||
if (this.pokeballs) return this.pokeballs;
|
if (this.pokeballs) return this.pokeballs;
|
||||||
this.pokeballs = [];
|
this.pokeballs = [];
|
||||||
if (!window.BattleItems) window.BattleItems = {};
|
if (!window.BattleItems) window.BattleItems = {};
|
||||||
for (const data of Object.values(window.BattleItems) as AnyObject[]) {
|
for (const data of Object.values<AnyObject>(window.BattleItems)) {
|
||||||
if (data.gen && data.gen > this.gen) continue;
|
if (data.gen && data.gen > this.gen) continue;
|
||||||
if (!data.isPokeball) continue;
|
if (!data.isPokeball) continue;
|
||||||
this.pokeballs.push(data.name);
|
this.pokeballs.push(data.name);
|
||||||
|
|
@ -1148,9 +1147,9 @@ export const Teams = new class {
|
||||||
|
|
||||||
// moves
|
// moves
|
||||||
j = buf.indexOf('|', i);
|
j = buf.indexOf('|', i);
|
||||||
set.moves = buf.substring(i, j).split(',').map(function (moveid) {
|
set.moves = buf.substring(i, j).split(',').map(
|
||||||
return Dex.moves.get(moveid).name;
|
moveid => Dex.moves.get(moveid).name
|
||||||
});
|
);
|
||||||
i = j + 1;
|
i = j + 1;
|
||||||
|
|
||||||
// nature
|
// nature
|
||||||
|
|
@ -1174,7 +1173,7 @@ export const Teams = new class {
|
||||||
spe: Number(evs[5]) || 0,
|
spe: Number(evs[5]) || 0,
|
||||||
};
|
};
|
||||||
} else if (evstring === '0') {
|
} else if (evstring === '0') {
|
||||||
set.evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
set.evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i = j + 1;
|
i = j + 1;
|
||||||
|
|
@ -1234,49 +1233,49 @@ export const Teams = new class {
|
||||||
export(team: Dex.PokemonSet[] | string, gen: number, hidestats = false) {
|
export(team: Dex.PokemonSet[] | string, gen: number, hidestats = false) {
|
||||||
if (!team) return '';
|
if (!team) return '';
|
||||||
if (typeof team === 'string') {
|
if (typeof team === 'string') {
|
||||||
if (team.indexOf('\n') >= 0) return team;
|
if (team.includes('\n')) return team;
|
||||||
team = this.unpack(team);
|
team = this.unpack(team);
|
||||||
}
|
}
|
||||||
let text = '';
|
let text = '';
|
||||||
for (const curSet of team) {
|
for (const curSet of team) {
|
||||||
if (curSet.name && curSet.name !== curSet.species) {
|
if (curSet.name && curSet.name !== curSet.species) {
|
||||||
text += '' + curSet.name + ' (' + curSet.species + ')';
|
text += `${curSet.name} (${curSet.species})`;
|
||||||
} else {
|
} else {
|
||||||
text += '' + curSet.species;
|
text += `${curSet.species}`;
|
||||||
}
|
}
|
||||||
if (curSet.gender === 'M') text += ' (M)';
|
if (curSet.gender === 'M') text += ' (M)';
|
||||||
if (curSet.gender === 'F') text += ' (F)';
|
if (curSet.gender === 'F') text += ' (F)';
|
||||||
if (curSet.item) {
|
if (curSet.item) {
|
||||||
text += ' @ ' + curSet.item;
|
text += ` @ ${curSet.item}`;
|
||||||
}
|
}
|
||||||
text += " \n";
|
text += " \n";
|
||||||
if (curSet.ability) {
|
if (curSet.ability) {
|
||||||
text += 'Ability: ' + curSet.ability + " \n";
|
text += `Ability: ${curSet.ability} \n`;
|
||||||
}
|
}
|
||||||
if (curSet.level && curSet.level !== 100) {
|
if (curSet.level && curSet.level !== 100) {
|
||||||
text += 'Level: ' + curSet.level + " \n";
|
text += `Level: ${curSet.level} \n`;
|
||||||
}
|
}
|
||||||
if (curSet.shiny) {
|
if (curSet.shiny) {
|
||||||
text += 'Shiny: Yes \n';
|
text += 'Shiny: Yes \n';
|
||||||
}
|
}
|
||||||
if (typeof curSet.happiness === 'number' && curSet.happiness !== 255 && !isNaN(curSet.happiness)) {
|
if (typeof curSet.happiness === 'number' && curSet.happiness !== 255 && !isNaN(curSet.happiness)) {
|
||||||
text += 'Happiness: ' + curSet.happiness + " \n";
|
text += `Happiness: ${curSet.happiness} \n`;
|
||||||
}
|
}
|
||||||
if (curSet.pokeball) {
|
if (curSet.pokeball) {
|
||||||
text += 'Pokeball: ' + curSet.pokeball + " \n";
|
text += `Pokeball: ${curSet.pokeball} \n`;
|
||||||
}
|
}
|
||||||
if (curSet.hpType) {
|
if (curSet.hpType) {
|
||||||
text += 'Hidden Power: ' + curSet.hpType + " \n";
|
text += `Hidden Power: ${curSet.hpType} \n`;
|
||||||
}
|
}
|
||||||
if (typeof curSet.dynamaxLevel === 'number' && curSet.dynamaxLevel !== 10 && !isNaN(curSet.dynamaxLevel)) {
|
if (typeof curSet.dynamaxLevel === 'number' && curSet.dynamaxLevel !== 10 && !isNaN(curSet.dynamaxLevel)) {
|
||||||
text += 'Dynamax Level: ' + curSet.dynamaxLevel + " \n";
|
text += `Dynamax Level: ${curSet.dynamaxLevel} \n`;
|
||||||
}
|
}
|
||||||
if (curSet.gigantamax) {
|
if (curSet.gigantamax) {
|
||||||
text += 'Gigantamax: Yes \n';
|
text += 'Gigantamax: Yes \n';
|
||||||
}
|
}
|
||||||
if (gen === 9) {
|
if (gen === 9) {
|
||||||
const species = Dex.species.get(curSet.species);
|
const species = Dex.species.get(curSet.species);
|
||||||
text += 'Tera Type: ' + (species.forceTeraType || curSet.teraType || species.types[0]) + " \n";
|
text += `Tera Type: ${species.forceTeraType || curSet.teraType || species.types[0]} \n`;
|
||||||
}
|
}
|
||||||
if (!hidestats) {
|
if (!hidestats) {
|
||||||
let first = true;
|
let first = true;
|
||||||
|
|
@ -1290,14 +1289,14 @@ export const Teams = new class {
|
||||||
} else {
|
} else {
|
||||||
text += ' / ';
|
text += ' / ';
|
||||||
}
|
}
|
||||||
text += '' + curSet.evs[j] + ' ' + BattleStatNames[j];
|
text += `${curSet.evs[j]!} ${BattleStatNames[j]}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!first) {
|
if (!first) {
|
||||||
text += " \n";
|
text += " \n";
|
||||||
}
|
}
|
||||||
if (curSet.nature) {
|
if (curSet.nature) {
|
||||||
text += '' + curSet.nature + ' Nature' + " \n";
|
text += `${curSet.nature} Nature \n`;
|
||||||
}
|
}
|
||||||
first = true;
|
first = true;
|
||||||
if (curSet.ivs) {
|
if (curSet.ivs) {
|
||||||
|
|
@ -1338,7 +1337,7 @@ export const Teams = new class {
|
||||||
} else {
|
} else {
|
||||||
text += ' / ';
|
text += ' / ';
|
||||||
}
|
}
|
||||||
text += '' + curSet.ivs[stat] + ' ' + BattleStatNames[stat];
|
text += `${curSet.ivs[stat]} ${BattleStatNames[stat]}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1348,11 +1347,11 @@ export const Teams = new class {
|
||||||
}
|
}
|
||||||
if (curSet.moves) {
|
if (curSet.moves) {
|
||||||
for (let move of curSet.moves) {
|
for (let move of curSet.moves) {
|
||||||
if (move.substr(0, 13) === 'Hidden Power ') {
|
if (move.startsWith('Hidden Power ')) {
|
||||||
move = move.substr(0, 13) + '[' + move.substr(13) + ']';
|
move = `${move.slice(0, 13)}[${move.slice(13)}]`;
|
||||||
}
|
}
|
||||||
if (move) {
|
if (move) {
|
||||||
text += '- ' + move + " \n";
|
text += `- ${move} \n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1364,6 +1363,6 @@ export const Teams = new class {
|
||||||
|
|
||||||
if (typeof require === 'function') {
|
if (typeof require === 'function') {
|
||||||
// in Node
|
// in Node
|
||||||
(global as any).Dex = Dex;
|
global.Dex = Dex;
|
||||||
(global as any).toID = toID;
|
global.toID = toID;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {Battle} from './battle';
|
import type { Battle } from './battle';
|
||||||
import type {BattleScene} from './battle-animations';
|
import type { BattleScene } from './battle-animations';
|
||||||
import {Dex, Teams, toID, toRoomid, toUserid, type ID} from './battle-dex';
|
import { Dex, Teams, toID, toRoomid, toUserid, type ID } from './battle-dex';
|
||||||
import {BattleTextParser, type Args, type KWArgs} from './battle-text-parser';
|
import { BattleTextParser, type Args, type KWArgs } from './battle-text-parser';
|
||||||
|
|
||||||
// Caja
|
// Caja
|
||||||
declare const html4: any;
|
declare const html4: any;
|
||||||
|
|
@ -25,7 +25,7 @@ declare const html: any;
|
||||||
// defined in battle-log-misc
|
// defined in battle-log-misc
|
||||||
declare function MD5(input: string): string;
|
declare function MD5(input: string): string;
|
||||||
declare function formatText(input: string, isTrusted?: boolean): string;
|
declare function formatText(input: string, isTrusted?: boolean): string;
|
||||||
export {MD5, formatText};
|
export { MD5, formatText };
|
||||||
|
|
||||||
export class BattleLog {
|
export class BattleLog {
|
||||||
elem: HTMLDivElement;
|
elem: HTMLDivElement;
|
||||||
|
|
@ -113,7 +113,7 @@ export class BattleLog {
|
||||||
if (
|
if (
|
||||||
battle.seeking === Infinity ?
|
battle.seeking === Infinity ?
|
||||||
battle.currentStep < battle.stepQueue.length - 2000 :
|
battle.currentStep < battle.stepQueue.length - 2000 :
|
||||||
battle.turn < battle.seeking! - 100
|
battle.turn < battle.seeking - 100
|
||||||
) {
|
) {
|
||||||
this.addSeekEarlierButton();
|
this.addSeekEarlierButton();
|
||||||
return;
|
return;
|
||||||
|
|
@ -154,7 +154,7 @@ export class BattleLog {
|
||||||
const user = BattleTextParser.parseNameParts(args[1]);
|
const user = BattleTextParser.parseNameParts(args[1]);
|
||||||
if (battle?.ignoreSpects && ' +'.includes(user.group)) return;
|
if (battle?.ignoreSpects && ' +'.includes(user.group)) return;
|
||||||
const formattedUser = user.group + user.name;
|
const formattedUser = user.group + user.name;
|
||||||
const isJoin = (args[0].charAt(0) === 'j');
|
const isJoin = (args[0].startsWith('j'));
|
||||||
if (!this.joinLeave) {
|
if (!this.joinLeave) {
|
||||||
this.joinLeave = {
|
this.joinLeave = {
|
||||||
joins: [],
|
joins: [],
|
||||||
|
|
@ -271,9 +271,11 @@ export class BattleLog {
|
||||||
const exportedTeam = team.map(set => {
|
const exportedTeam = team.map(set => {
|
||||||
let buf = Teams.export([set], battle.gen).replace(/\n/g, '<br />');
|
let buf = Teams.export([set], battle.gen).replace(/\n/g, '<br />');
|
||||||
if (set.name && set.name !== set.species) {
|
if (set.name && set.name !== set.species) {
|
||||||
buf = buf.replace(set.name, BattleLog.sanitizeHTML(`<span class="picon" style="${Dex.getPokemonIcon(set.species)}"></span><br />${set.name}`));
|
buf = buf.replace(set.name, BattleLog.sanitizeHTML(
|
||||||
|
`<span class="picon" style="${Dex.getPokemonIcon(set.species)}"></span><br />${set.name}`));
|
||||||
} else {
|
} else {
|
||||||
buf = buf.replace(set.species, `<span class="picon" style="${Dex.getPokemonIcon(set.species)}"></span><br />${set.species}`);
|
buf = buf.replace(set.species,
|
||||||
|
`<span class="picon" style="${Dex.getPokemonIcon(set.species)}"></span><br />${set.species}`);
|
||||||
}
|
}
|
||||||
if (set.item) {
|
if (set.item) {
|
||||||
buf = buf.replace(set.item, `${set.item} <span class="itemicon" style="${Dex.getItemIcon(set.item)}"></span>`);
|
buf = buf.replace(set.item, `${set.item} <span class="itemicon" style="${Dex.getItemIcon(set.item)}"></span>`);
|
||||||
|
|
@ -380,7 +382,7 @@ export class BattleLog {
|
||||||
const messages = message.split('\n').map(line => {
|
const messages = message.split('\n').map(line => {
|
||||||
line = BattleLog.escapeHTML(line);
|
line = BattleLog.escapeHTML(line);
|
||||||
line = line.replace(/\*\*(.*)\*\*/, '<strong>$1</strong>');
|
line = line.replace(/\*\*(.*)\*\*/, '<strong>$1</strong>');
|
||||||
line = line.replace(/\|\|([^\|]*)\|\|([^\|]*)\|\|/, '<abbr title="$1">$2</abbr>');
|
line = line.replace(/\|\|([^|]*)\|\|([^|]*)\|\|/, '<abbr title="$1">$2</abbr>');
|
||||||
if (line.startsWith(' ')) line = '<small>' + line.trim() + '</small>';
|
if (line.startsWith(' ')) line = '<small>' + line.trim() + '</small>';
|
||||||
return line;
|
return line;
|
||||||
});
|
});
|
||||||
|
|
@ -550,7 +552,7 @@ export class BattleLog {
|
||||||
return str.replace(/"/g, '"').replace(/>/g, '>').replace(/</g, '<').replace(/&/g, '&');
|
return str.replace(/"/g, '"').replace(/>/g, '>').replace(/</g, '<').replace(/&/g, '&');
|
||||||
}
|
}
|
||||||
|
|
||||||
static colorCache: {[userid: string]: string} = {};
|
static colorCache: { [userid: string]: string } = {};
|
||||||
|
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
static hashColor(name: ID) {
|
static hashColor(name: ID) {
|
||||||
|
|
@ -569,12 +571,12 @@ export class BattleLog {
|
||||||
let S = parseInt(hash.substr(0, 4), 16) % 50 + 40; // 40 to 89
|
let S = parseInt(hash.substr(0, 4), 16) % 50 + 40; // 40 to 89
|
||||||
let L = Math.floor(parseInt(hash.substr(8, 4), 16) % 20 + 30); // 30 to 49
|
let L = Math.floor(parseInt(hash.substr(8, 4), 16) % 20 + 30); // 30 to 49
|
||||||
|
|
||||||
let {R, G, B} = this.HSLToRGB(H, S, L);
|
let { R, G, B } = this.HSLToRGB(H, S, L);
|
||||||
let lum = R * R * R * 0.2126 + G * G * G * 0.7152 + B * B * B * 0.0722; // 0.013 (dark blue) to 0.737 (yellow)
|
let lum = R * R * R * 0.2126 + G * G * G * 0.7152 + B * B * B * 0.0722; // 0.013 (dark blue) to 0.737 (yellow)
|
||||||
|
|
||||||
let HLmod = (lum - 0.2) * -150; // -80 (yellow) to 28 (dark blue)
|
let HLmod = (lum - 0.2) * -150; // -80 (yellow) to 28 (dark blue)
|
||||||
if (HLmod > 18) HLmod = (HLmod - 18) * 2.5;
|
if (HLmod > 18) HLmod = (HLmod - 18) * 2.5;
|
||||||
else if (HLmod < 0) HLmod = (HLmod - 0) / 3;
|
else if (HLmod < 0) HLmod /= 3;
|
||||||
else HLmod = 0;
|
else HLmod = 0;
|
||||||
// let mod = ';border-right: ' + Math.abs(HLmod) + 'px solid ' + (HLmod > 0 ? 'red' : '#0088FF');
|
// let mod = ';border-right: ' + Math.abs(HLmod) + 'px solid ' + (HLmod > 0 ? 'red' : '#0088FF');
|
||||||
let Hdist = Math.min(Math.abs(180 - H), Math.abs(240 - H));
|
let Hdist = Math.min(Math.abs(180 - H), Math.abs(240 - H));
|
||||||
|
|
@ -584,7 +586,7 @@ export class BattleLog {
|
||||||
|
|
||||||
L += HLmod;
|
L += HLmod;
|
||||||
|
|
||||||
let {R: r, G: g, B: b} = this.HSLToRGB(H, S, L);
|
let { R: r, G: g, B: b } = this.HSLToRGB(H, S, L);
|
||||||
const toHex = (x: number) => {
|
const toHex = (x: number) => {
|
||||||
const hex = Math.round(x * 255).toString(16);
|
const hex = Math.round(x * 255).toString(16);
|
||||||
return hex.length === 1 ? '0' + hex : hex;
|
return hex.length === 1 ? '0' + hex : hex;
|
||||||
|
|
@ -612,14 +614,15 @@ export class BattleLog {
|
||||||
let R = R1 + m;
|
let R = R1 + m;
|
||||||
let G = G1 + m;
|
let G = G1 + m;
|
||||||
let B = B1 + m;
|
let B = B1 + m;
|
||||||
return {R, G, B};
|
return { R, G, B };
|
||||||
}
|
}
|
||||||
|
|
||||||
static prefs(name: string) {
|
static prefs(name: string) {
|
||||||
// @ts-ignore
|
// @ts-expect-error optional, for old client
|
||||||
if (window.Storage?.prefs) return Storage.prefs(name);
|
if (window.Storage?.prefs) return Storage.prefs(name);
|
||||||
// @ts-ignore
|
// @ts-expect-error optional, for Preact client
|
||||||
if (window.PS) return PS.prefs[name];
|
if (window.PS) return PS.prefs[name];
|
||||||
|
// may be neither, for e.g. Replays
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -641,7 +644,7 @@ export class BattleLog {
|
||||||
|
|
||||||
let cmd = '';
|
let cmd = '';
|
||||||
let target = '';
|
let target = '';
|
||||||
if (message.charAt(0) === '/') {
|
if (message.startsWith('/')) {
|
||||||
if (message.charAt(1) === '/') {
|
if (message.charAt(1) === '/') {
|
||||||
message = message.slice(1);
|
message = message.slice(1);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -753,11 +756,11 @@ export class BattleLog {
|
||||||
static interstice = (() => {
|
static interstice = (() => {
|
||||||
const whitelist: string[] = Config.whitelist;
|
const whitelist: string[] = Config.whitelist;
|
||||||
const patterns = whitelist.map(entry => new RegExp(
|
const patterns = whitelist.map(entry => new RegExp(
|
||||||
`^(https?:)?//([A-Za-z0-9-]*\\.)?${entry.replace(/\./g, '\\.')}(/.*)?`,
|
`^(https?:)?//([A-Za-z0-9-]*\\.)?${entry.replace(/\./g, '\\.')}(/.*)?`, 'i'
|
||||||
'i'));
|
));
|
||||||
return {
|
return {
|
||||||
isWhitelisted(uri: string) {
|
isWhitelisted(uri: string) {
|
||||||
if (uri[0] === '/' && uri[1] !== '/') {
|
if (uri.startsWith('/') && uri[1] !== '/') {
|
||||||
// domain-relative URIs are safe
|
// domain-relative URIs are safe
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -904,7 +907,7 @@ export class BattleLog {
|
||||||
return {
|
return {
|
||||||
tagName: 'iframe',
|
tagName: 'iframe',
|
||||||
attribs: [
|
attribs: [
|
||||||
'src', `https://player.twitch.tv/?channel=${channelId}&parent=${location.hostname}&autoplay=false`,
|
'src', `https://player.twitch.tv/?channel=${channelId!}&parent=${location.hostname}&autoplay=false`,
|
||||||
'allowfullscreen', 'true', 'height', `${height}`, 'width', `${width}`,
|
'allowfullscreen', 'true', 'height', `${height}`, 'width', `${width}`,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
@ -912,7 +915,7 @@ export class BattleLog {
|
||||||
// <username> is a custom element that handles namecolors
|
// <username> is a custom element that handles namecolors
|
||||||
tagName = 'strong';
|
tagName = 'strong';
|
||||||
const color = this.usernameColor(toID(getAttrib('name')));
|
const color = this.usernameColor(toID(getAttrib('name')));
|
||||||
const style = getAttrib('style');
|
const style = getAttrib('style') || '';
|
||||||
setAttrib('style', `${style};color:${color}`);
|
setAttrib('style', `${style};color:${color}`);
|
||||||
} else if (tagName === 'spotify') {
|
} else if (tagName === 'spotify') {
|
||||||
// <iframe src="https://open.spotify.com/embed/track/6aSYnCIwcLpnDXngGKAEzZ" width="300" height="380" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
|
// <iframe src="https://open.spotify.com/embed/track/6aSYnCIwcLpnDXngGKAEzZ" width="300" height="380" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
|
||||||
|
|
@ -921,7 +924,7 @@ export class BattleLog {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tagName: 'iframe',
|
tagName: 'iframe',
|
||||||
attribs: ['src', `https://open.spotify.com/embed/track/${songId}`, 'width', '300', 'height', '380', 'frameborder', '0', 'allowtransparency', 'true', 'allow', 'encrypted-media'],
|
attribs: ['src', `https://open.spotify.com/embed/track/${songId!}`, 'width', '300', 'height', '380', 'frameborder', '0', 'allowtransparency', 'true', 'allow', 'encrypted-media'],
|
||||||
};
|
};
|
||||||
} else if (tagName === 'youtube') {
|
} else if (tagName === 'youtube') {
|
||||||
// <iframe width="320" height="180" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
// <iframe width="320" height="180" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||||
|
|
@ -936,8 +939,8 @@ export class BattleLog {
|
||||||
if (Number(height) < 200) {
|
if (Number(height) < 200) {
|
||||||
height = window.innerWidth >= 400 ? '225' : '200';
|
height = window.innerWidth >= 400 ? '225' : '200';
|
||||||
}
|
}
|
||||||
const videoId = /(?:\?v=|\/embed\/)([A-Za-z0-9_\-]+)/.exec(src)?.[1];
|
const videoId = /(?:\?v=|\/embed\/)([A-Za-z0-9_-]+)/.exec(src)?.[1];
|
||||||
if (!videoId) return {tagName: 'img', attribs: ['alt', `invalid src for <youtube>`]};
|
if (!videoId) return { tagName: 'img', attribs: ['alt', `invalid src for <youtube>`] };
|
||||||
|
|
||||||
const time = /(?:\?|&)(?:t|start)=([0-9]+)/.exec(src)?.[1];
|
const time = /(?:\?|&)(?:t|start)=([0-9]+)/.exec(src)?.[1];
|
||||||
this.players.push(null);
|
this.players.push(null);
|
||||||
|
|
@ -950,7 +953,7 @@ export class BattleLog {
|
||||||
'width', width, 'height', height,
|
'width', width, 'height', height,
|
||||||
'src', `https://www.youtube.com/embed/${videoId}?enablejsapi=1&playsinline=1${time ? `&start=${time}` : ''}`,
|
'src', `https://www.youtube.com/embed/${videoId}?enablejsapi=1&playsinline=1${time ? `&start=${time}` : ''}`,
|
||||||
'frameborder', '0', 'allow', 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture', 'allowfullscreen', 'allowfullscreen',
|
'frameborder', '0', 'allow', 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture', 'allowfullscreen', 'allowfullscreen',
|
||||||
'time', (time || 0) + "",
|
'time', `${time || 0}`,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
} else if (tagName === 'formatselect') {
|
} else if (tagName === 'formatselect') {
|
||||||
|
|
@ -1026,7 +1029,7 @@ export class BattleLog {
|
||||||
setAttrib('rel', 'noopener');
|
setAttrib('rel', 'noopener');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {tagName, attribs};
|
return { tagName, attribs };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
static localizeTime(full: string, date: string, time: string, timezone?: string) {
|
static localizeTime(full: string, date: string, time: string, timezone?: string) {
|
||||||
|
|
@ -1081,7 +1084,8 @@ export class BattleLog {
|
||||||
// allows T, however it's more practical to also allow spaces.
|
// allows T, however it's more practical to also allow spaces.
|
||||||
return sanitized.replace(
|
return sanitized.replace(
|
||||||
/<time>\s*([+-]?\d{4,}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{3})?)?)(Z|[+-]\d{2}:\d{2})?\s*<\/time>/ig,
|
/<time>\s*([+-]?\d{4,}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{3})?)?)(Z|[+-]\d{2}:\d{2})?\s*<\/time>/ig,
|
||||||
this.localizeTime);
|
this.localizeTime
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static initYoutubePlayer(idx: number) {
|
static initYoutubePlayer(idx: number) {
|
||||||
|
|
@ -1106,7 +1110,6 @@ export class BattleLog {
|
||||||
player.seekTo(time);
|
player.seekTo(time);
|
||||||
}
|
}
|
||||||
this.players[idx - 1] = player;
|
this.players[idx - 1] = player;
|
||||||
|
|
||||||
};
|
};
|
||||||
// wait for html element to be in DOM
|
// wait for html element to be in DOM
|
||||||
this.ensureYoutube().then(() => {
|
this.ensureYoutube().then(() => {
|
||||||
|
|
@ -1164,7 +1167,7 @@ export class BattleLog {
|
||||||
// This allows pretty much anything about the replay viewer to be
|
// This allows pretty much anything about the replay viewer to be
|
||||||
// updated as desired.
|
// updated as desired.
|
||||||
|
|
||||||
static createReplayFile(room: {battle: Battle, id?: string, fragment?: string}) {
|
static createReplayFile(room: { battle: Battle, id?: string, fragment?: string }) {
|
||||||
let battle = room.battle;
|
let battle = room.battle;
|
||||||
let replayid = room.id;
|
let replayid = room.id;
|
||||||
if (replayid) {
|
if (replayid) {
|
||||||
|
|
@ -1208,10 +1211,12 @@ export class BattleLog {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createReplayFileHref(room: {battle: Battle, id?: string, fragment?: string}) {
|
static createReplayFileHref(room: { battle: Battle, id?: string, fragment?: string }) {
|
||||||
// unescape(encodeURIComponent()) is necessary because btoa doesn't support Unicode
|
// unescape(encodeURIComponent()) is necessary because btoa doesn't support Unicode
|
||||||
const replayFile = BattleLog.createReplayFile(room);
|
const replayFile = BattleLog.createReplayFile(room);
|
||||||
if (!replayFile) return 'javascript:alert("You will need to click Download again once the replay file is at the end.");void 0';
|
if (!replayFile) {
|
||||||
|
return 'javascript:alert("You will need to click Download again once the replay file is at the end.");void 0';
|
||||||
|
}
|
||||||
return 'data:text/plain;base64,' + encodeURIComponent(btoa(unescape(encodeURIComponent(replayFile))));
|
return 'data:text/plain;base64,' + encodeURIComponent(btoa(unescape(encodeURIComponent(replayFile))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,36 @@
|
||||||
import type {Pokemon, Side} from './battle';
|
import type { Pokemon, Side } from './battle';
|
||||||
import type {ScenePos, PokemonSprite} from './battle-animations';
|
import type { ScenePos, PokemonSprite } from './battle-animations';
|
||||||
import type {BattleLog} from './battle-log';
|
import type { BattleLog } from './battle-log';
|
||||||
import type {ID} from './battle-dex';
|
import type { ID } from './battle-dex';
|
||||||
import type {Args, KWArgs} from './battle-text-parser';
|
import type { Args, KWArgs } from './battle-text-parser';
|
||||||
|
|
||||||
export class BattleSceneStub {
|
export class BattleSceneStub {
|
||||||
animating: boolean = false;
|
animating = false;
|
||||||
acceleration: number = NaN;
|
acceleration = NaN;
|
||||||
gen: number = NaN;
|
gen = NaN;
|
||||||
activeCount: number = NaN;
|
activeCount = NaN;
|
||||||
numericId: number = NaN;
|
numericId = NaN;
|
||||||
timeOffset: number = NaN;
|
timeOffset = NaN;
|
||||||
interruptionCount: number = NaN;
|
interruptionCount = NaN;
|
||||||
messagebarOpen: boolean = false;
|
messagebarOpen = false;
|
||||||
log: BattleLog = {add: (args: Args, kwargs?: KWArgs) => {}} as any;
|
log: BattleLog = { add: (args: Args, kwargs?: KWArgs) => {} } as any;
|
||||||
$frame?: JQuery;
|
$frame?: JQuery;
|
||||||
|
|
||||||
abilityActivateAnim(pokemon: Pokemon, result: string): void { }
|
abilityActivateAnim(pokemon: Pokemon, result: string): void { }
|
||||||
addPokemonSprite(pokemon: Pokemon): PokemonSprite { return null!; }
|
addPokemonSprite(pokemon: Pokemon): PokemonSprite { return null!; }
|
||||||
addSideCondition(siden: number, id: ID, instant?: boolean | undefined): void { }
|
addSideCondition(siden: number, id: ID, instant?: boolean): void { }
|
||||||
animationOff(): void { }
|
animationOff(): void { }
|
||||||
animationOn(): void { }
|
animationOn(): void { }
|
||||||
maybeCloseMessagebar(args: Args, kwArgs: KWArgs): boolean { return false; }
|
maybeCloseMessagebar(args: Args, kwArgs: KWArgs): boolean { return false; }
|
||||||
closeMessagebar(): boolean { return false; }
|
closeMessagebar(): boolean { return false; }
|
||||||
damageAnim(pokemon: Pokemon, damage: string | number): void { }
|
damageAnim(pokemon: Pokemon, damage: string | number): void { }
|
||||||
destroy(): void { }
|
destroy(): void { }
|
||||||
finishAnimations(): JQuery.Promise<JQuery<HTMLElement>, any, any> | undefined { return void(0); }
|
finishAnimations(): JQuery.Promise<JQuery> | undefined { return undefined; }
|
||||||
healAnim(pokemon: Pokemon, damage: string | number): void { }
|
healAnim(pokemon: Pokemon, damage: string | number): void { }
|
||||||
hideJoinButtons(): void { }
|
hideJoinButtons(): void { }
|
||||||
incrementTurn(): void { }
|
incrementTurn(): void { }
|
||||||
updateAcceleration(): void { }
|
updateAcceleration(): void { }
|
||||||
message(message: string, hiddenMessage?: string | undefined): void { }
|
message(message: string, hiddenMessage?: string): void { }
|
||||||
pause(): void { }
|
pause(): void { }
|
||||||
setMute(muted: boolean): void { }
|
setMute(muted: boolean): void { }
|
||||||
preemptCatchup(): void { }
|
preemptCatchup(): void { }
|
||||||
|
|
@ -55,7 +55,7 @@ export class BattleSceneStub {
|
||||||
updateSidebar(side: Side): void { }
|
updateSidebar(side: Side): void { }
|
||||||
updateSidebars(): void { }
|
updateSidebars(): void { }
|
||||||
updateStatbars(): void { }
|
updateStatbars(): void { }
|
||||||
updateWeather(instant?: boolean | undefined): void { }
|
updateWeather(instant?: boolean): void { }
|
||||||
upkeepWeather(): void { }
|
upkeepWeather(): void { }
|
||||||
wait(time: number): void { }
|
wait(time: number): void { }
|
||||||
setFrameHTML(html: any): void { }
|
setFrameHTML(html: any): void { }
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import preact from "../js/lib/preact";
|
import preact from "../js/lib/preact";
|
||||||
import {Dex, type ID} from "./battle-dex";
|
import { Dex, type ID } from "./battle-dex";
|
||||||
import type {DexSearch, SearchRow} from "./battle-dex-search";
|
import type { DexSearch, SearchRow } from "./battle-dex-search";
|
||||||
|
|
||||||
export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
export class PSSearchResults extends preact.Component<{ search: DexSearch }> {
|
||||||
readonly URL_ROOT = `//${Config.routes.dex}/`;
|
readonly URL_ROOT = `//${Config.routes.dex}/`;
|
||||||
|
|
||||||
renderPokemonSortRow() {
|
renderPokemonSortRow() {
|
||||||
|
|
@ -57,7 +57,23 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
if (search.dex.gen < 2) bst -= stats['spd'];
|
if (search.dex.gen < 2) bst -= stats['spd'];
|
||||||
|
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
return <li class="result"><a href={`${this.URL_ROOT}pokemon/${id}`} data-target="push" data-entry={`pokemon|${pokemon.name}`}>
|
return <li class="result">
|
||||||
|
<a href={`${this.URL_ROOT}pokemon/${id}`} data-target="push" data-entry={`pokemon|${pokemon.name}`}>
|
||||||
|
<span class="col numcol">{search.getTier(pokemon)}</span>
|
||||||
|
|
||||||
|
<span class="col iconcol">
|
||||||
|
<span style={Dex.getPokemonIcon(pokemon.id)}></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="col pokemonnamecol">{this.renderName(pokemon.name, matchStart, matchEnd, tagStart)}</span>
|
||||||
|
|
||||||
|
{errorMessage}
|
||||||
|
</a>
|
||||||
|
</li>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <li class="result">
|
||||||
|
<a href={`${this.URL_ROOT}pokemon/${id}`} data-target="push" data-entry={`pokemon|${pokemon.name}`}>
|
||||||
<span class="col numcol">{search.getTier(pokemon)}</span>
|
<span class="col numcol">{search.getTier(pokemon)}</span>
|
||||||
|
|
||||||
<span class="col iconcol">
|
<span class="col iconcol">
|
||||||
|
|
@ -66,47 +82,43 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
|
|
||||||
<span class="col pokemonnamecol">{this.renderName(pokemon.name, matchStart, matchEnd, tagStart)}</span>
|
<span class="col pokemonnamecol">{this.renderName(pokemon.name, matchStart, matchEnd, tagStart)}</span>
|
||||||
|
|
||||||
{errorMessage}
|
<span class="col typecol">
|
||||||
</a></li>;
|
{pokemon.types.map(type =>
|
||||||
}
|
<img src={`${Dex.resourcePrefix}sprites/types/${type}.png`} alt={type} height="14" width="32" class="pixelated" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
|
||||||
return <li class="result"><a href={`${this.URL_ROOT}pokemon/${id}`} data-target="push" data-entry={`pokemon|${pokemon.name}`}>
|
{search.dex.gen >= 3 && (
|
||||||
<span class="col numcol">{search.getTier(pokemon)}</span>
|
pokemon.abilities['1'] ? (
|
||||||
|
<span class="col twoabilitycol">{pokemon.abilities['0']}<br />{pokemon.abilities['1']}</span>
|
||||||
<span class="col iconcol">
|
) : (
|
||||||
<span style={Dex.getPokemonIcon(pokemon.id)}></span>
|
<span class="col abilitycol">{pokemon.abilities['0']}</span>
|
||||||
</span>
|
)
|
||||||
|
)}
|
||||||
<span class="col pokemonnamecol">{this.renderName(pokemon.name, matchStart, matchEnd, tagStart)}</span>
|
{search.dex.gen >= 5 && (
|
||||||
|
pokemon.abilities['S'] ? (
|
||||||
<span class="col typecol">
|
<span class={`col twoabilitycol${pokemon.unreleasedHidden ? ' unreleasedhacol' : ''}`}>
|
||||||
{pokemon.types.map(type =>
|
{pokemon.abilities['H'] || ''}<br />{pokemon.abilities['S']}
|
||||||
<img src={`${Dex.resourcePrefix}sprites/types/${type}.png`} alt={type} height="14" width="32" class="pixelated" />
|
</span>
|
||||||
|
) : pokemon.abilities['H'] ? (
|
||||||
|
<span class={`col abilitycol${pokemon.unreleasedHidden ? ' unreleasedhacol' : ''}`}>
|
||||||
|
{pokemon.abilities['H']}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span class="col abilitycol"></span>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</span>
|
|
||||||
|
|
||||||
{search.dex.gen >= 3 && (pokemon.abilities['1'] ?
|
<span class="col statcol"><em>HP</em><br />{stats.hp}</span>
|
||||||
<span class="col twoabilitycol">{pokemon.abilities['0']}<br />{pokemon.abilities['1']}</span>
|
<span class="col statcol"><em>Atk</em><br />{stats.atk}</span>
|
||||||
:
|
<span class="col statcol"><em>Def</em><br />{stats.def}</span>
|
||||||
<span class="col abilitycol">{pokemon.abilities['0']}</span>
|
{search.dex.gen > 2 && <span class="col statcol"><em>SpA</em><br />{stats.spa}</span>}
|
||||||
)}
|
{search.dex.gen > 2 && <span class="col statcol"><em>SpD</em><br />{stats.spd}</span>}
|
||||||
{search.dex.gen >= 5 && (pokemon.abilities['S'] ?
|
{search.dex.gen < 2 && <span class="col statcol"><em>Spc</em><br />{stats.spa}</span>}
|
||||||
<span class={`col twoabilitycol${pokemon.unreleasedHidden ? ' unreleasedhacol' : ''}`}>{pokemon.abilities['H'] || ''}<br />{pokemon.abilities['S']}</span>
|
<span class="col statcol"><em>Spe</em><br />{stats.spe}</span>
|
||||||
: pokemon.abilities['H'] ?
|
<span class="col bstcol"><em>BST<br />{bst}</em></span>
|
||||||
<span class={`col abilitycol${pokemon.unreleasedHidden ? ' unreleasedhacol' : ''}`}>{pokemon.abilities['H']}</span>
|
</a>
|
||||||
:
|
</li>;
|
||||||
<span class="col abilitycol"></span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<span class="col statcol"><em>HP</em><br />{stats.hp}</span>
|
|
||||||
<span class="col statcol"><em>Atk</em><br />{stats.atk}</span>
|
|
||||||
<span class="col statcol"><em>Def</em><br />{stats.def}</span>
|
|
||||||
{search.dex.gen > 2 && <span class="col statcol"><em>SpA</em><br />{stats.spa}</span>}
|
|
||||||
{search.dex.gen > 2 && <span class="col statcol"><em>SpD</em><br />{stats.spd}</span>}
|
|
||||||
{search.dex.gen < 2 && <span class="col statcol"><em>Spc</em><br />{stats.spa}</span>}
|
|
||||||
<span class="col statcol"><em>Spe</em><br />{stats.spe}</span>
|
|
||||||
<span class="col bstcol"><em>BST<br />{bst}</em></span>
|
|
||||||
</a></li>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderName(name: string, matchStart: number, matchEnd: number, tagStart?: number) {
|
renderName(name: string, matchStart: number, matchEnd: number, tagStart?: number) {
|
||||||
|
|
@ -166,13 +178,15 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
const ability = search.dex.abilities.get(id);
|
const ability = search.dex.abilities.get(id);
|
||||||
if (!ability) return <li class="result">Unrecognized ability</li>;
|
if (!ability) return <li class="result">Unrecognized ability</li>;
|
||||||
|
|
||||||
return <li class="result"><a href={`${this.URL_ROOT}abilitys/${id}`} data-target="push" data-entry={`ability|${ability.name}`}>
|
return <li class="result">
|
||||||
<span class="col namecol">{this.renderName(ability.name, matchStart, matchEnd)}</span>
|
<a href={`${this.URL_ROOT}abilitys/${id}`} data-target="push" data-entry={`ability|${ability.name}`}>
|
||||||
|
<span class="col namecol">{this.renderName(ability.name, matchStart, matchEnd)}</span>
|
||||||
|
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
|
|
||||||
{!errorMessage && <span class="col abilitydesccol">{ability.shortDesc}</span>}
|
{!errorMessage && <span class="col abilitydesccol">{ability.shortDesc}</span>}
|
||||||
</a></li>;
|
</a>
|
||||||
|
</li>;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMoveRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
renderMoveRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
||||||
|
|
@ -196,8 +210,14 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
<span class="col movenamecol">{this.renderName(move.name, matchStart, matchEnd, tagStart)}</span>
|
<span class="col movenamecol">{this.renderName(move.name, matchStart, matchEnd, tagStart)}</span>
|
||||||
|
|
||||||
<span class="col typecol">
|
<span class="col typecol">
|
||||||
<img src={`${Dex.resourcePrefix}sprites/types/${move.type}.png`} alt={move.type} height="14" width="32" class="pixelated" />
|
<img
|
||||||
<img src={`${Dex.resourcePrefix}sprites/categories/${move.category}.png`} alt={move.category} height="14" width="32" class="pixelated" />
|
src={`${Dex.resourcePrefix}sprites/types/${move.type}.png`}
|
||||||
|
alt={move.type} height="14" width="32" class="pixelated"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src={`${Dex.resourcePrefix}sprites/categories/${move.category}.png`}
|
||||||
|
alt={move.category} height="14" width="32" class="pixelated"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="col labelcol">
|
<span class="col labelcol">
|
||||||
|
|
@ -216,7 +236,6 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTypeRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
renderTypeRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
||||||
const search = this.props.search;
|
|
||||||
const name = id.charAt(0).toUpperCase() + id.slice(1);
|
const name = id.charAt(0).toUpperCase() + id.slice(1);
|
||||||
|
|
||||||
return <li class="result"><a href={`${this.URL_ROOT}types/${id}`} data-target="push" data-entry={`type|${name}`}>
|
return <li class="result"><a href={`${this.URL_ROOT}types/${id}`} data-target="push" data-entry={`type|${name}`}>
|
||||||
|
|
@ -231,25 +250,24 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCategoryRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
renderCategoryRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
||||||
const search = this.props.search;
|
|
||||||
const name = id.charAt(0).toUpperCase() + id.slice(1);
|
const name = id.charAt(0).toUpperCase() + id.slice(1);
|
||||||
|
|
||||||
return <li class="result"><a href={`${this.URL_ROOT}categories/${id}`} data-target="push" data-entry={`category|${name}`}>
|
return <li class="result">
|
||||||
<span class="col namecol">{this.renderName(name, matchStart, matchEnd)}</span>
|
<a href={`${this.URL_ROOT}categories/${id}`} data-target="push" data-entry={`category|${name}`}>
|
||||||
|
<span class="col namecol">{this.renderName(name, matchStart, matchEnd)}</span>
|
||||||
|
|
||||||
<span class="col typecol">
|
<span class="col typecol">
|
||||||
<img src={`${Dex.resourcePrefix}sprites/categories/${name}.png`} alt={name} height="14" width="32" class="pixelated" />
|
<img src={`${Dex.resourcePrefix}sprites/categories/${name}.png`} alt={name} height="14" width="32" class="pixelated" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
</a></li>;
|
</a>
|
||||||
|
</li>;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderArticleRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
renderArticleRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
||||||
const search = this.props.search;
|
|
||||||
const isSearchType = (id === 'pokemon' || id === 'moves');
|
const isSearchType = (id === 'pokemon' || id === 'moves');
|
||||||
const name = (window.BattleArticleTitles && window.BattleArticleTitles[id]) ||
|
const name = window.BattleArticleTitles?.[id] || (id.charAt(0).toUpperCase() + id.substr(1));
|
||||||
(id.charAt(0).toUpperCase() + id.substr(1));
|
|
||||||
|
|
||||||
return <li class="result"><a href={`${this.URL_ROOT}articles/${id}`} data-target="push" data-entry={`article|${name}`}>
|
return <li class="result"><a href={`${this.URL_ROOT}articles/${id}`} data-target="push" data-entry={`article|${name}`}>
|
||||||
<span class="col namecol">{this.renderName(name, matchStart, matchEnd)}</span>
|
<span class="col namecol">{this.renderName(name, matchStart, matchEnd)}</span>
|
||||||
|
|
@ -261,7 +279,6 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEggGroupRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
renderEggGroupRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
||||||
const search = this.props.search;
|
|
||||||
// very hardcode
|
// very hardcode
|
||||||
let name: string | undefined;
|
let name: string | undefined;
|
||||||
if (id === 'humanlike') name = 'Human-Like';
|
if (id === 'humanlike') name = 'Human-Like';
|
||||||
|
|
@ -274,19 +291,20 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
name = id.charAt(0).toUpperCase() + id.slice(1);
|
name = id.charAt(0).toUpperCase() + id.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <li class="result"><a href={`${this.URL_ROOT}egggroups/${id}`} data-target="push" data-entry={`egggroup|${name}`}>
|
return <li class="result">
|
||||||
<span class="col namecol">{this.renderName(name, matchStart, matchEnd)}</span>
|
<a href={`${this.URL_ROOT}egggroups/${id}`} data-target="push" data-entry={`egggroup|${name}`}>
|
||||||
|
<span class="col namecol">{this.renderName(name, matchStart, matchEnd)}</span>
|
||||||
|
|
||||||
<span class="col movedesccol">(egg group)</span>
|
<span class="col movedesccol">(egg group)</span>
|
||||||
|
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
</a></li>;
|
</a>
|
||||||
|
</li>;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTierRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
renderTierRow(id: ID, matchStart: number, matchEnd: number, errorMessage?: preact.ComponentChildren) {
|
||||||
const search = this.props.search;
|
|
||||||
// very hardcode
|
// very hardcode
|
||||||
const tierTable: {[id: string]: string} = {
|
const tierTable: { [id: string]: string } = {
|
||||||
uber: "Uber",
|
uber: "Uber",
|
||||||
caplc: "CAP LC",
|
caplc: "CAP LC",
|
||||||
capnfe: "CAP NFE",
|
capnfe: "CAP NFE",
|
||||||
|
|
@ -314,9 +332,9 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
|
|
||||||
let errorMessage: preact.ComponentChild = null;
|
let errorMessage: preact.ComponentChild = null;
|
||||||
let label;
|
let label;
|
||||||
if ((label = search.filterLabel(type))) { // tslint:disable-line
|
if ((label = search.filterLabel(type))) {
|
||||||
errorMessage = <span class="col filtercol"><em>{label}</em></span>;
|
errorMessage = <span class="col filtercol"><em>{label}</em></span>;
|
||||||
} else if ((label = search.illegalLabel(id as ID))) { // tslint:disable-line
|
} else if ((label = search.illegalLabel(id as ID))) {
|
||||||
errorMessage = <span class="col illegalcol"><em>{label}</em></span>;
|
errorMessage = <span class="col illegalcol"><em>{label}</em></span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,7 +344,7 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
.replace(/<em>/g, '<em>').replace(/<\/em>/g, '</em>')
|
.replace(/<em>/g, '<em>').replace(/<\/em>/g, '</em>')
|
||||||
.replace(/<strong>/g, '<strong>').replace(/<\/strong>/g, '</strong>');
|
.replace(/<strong>/g, '<strong>').replace(/<\/strong>/g, '</strong>');
|
||||||
return <li class="result">
|
return <li class="result">
|
||||||
<p dangerouslySetInnerHTML={{__html: sanitizedHTML}}></p>
|
<p dangerouslySetInnerHTML={{ __html: sanitizedHTML }}></p>
|
||||||
</li>;
|
</li>;
|
||||||
case 'header':
|
case 'header':
|
||||||
return <li class="result"><h3>{id}</h3></li>;
|
return <li class="result"><h3>{id}</h3></li>;
|
||||||
|
|
@ -335,23 +353,23 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
case 'sortmove':
|
case 'sortmove':
|
||||||
return this.renderMoveSortRow();
|
return this.renderMoveSortRow();
|
||||||
case 'pokemon':
|
case 'pokemon':
|
||||||
return this.renderPokemonRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderPokemonRow(id, matchStart, matchEnd, errorMessage);
|
||||||
case 'move':
|
case 'move':
|
||||||
return this.renderMoveRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderMoveRow(id, matchStart, matchEnd, errorMessage);
|
||||||
case 'item':
|
case 'item':
|
||||||
return this.renderItemRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderItemRow(id, matchStart, matchEnd, errorMessage);
|
||||||
case 'ability':
|
case 'ability':
|
||||||
return this.renderAbilityRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderAbilityRow(id, matchStart, matchEnd, errorMessage);
|
||||||
case 'type':
|
case 'type':
|
||||||
return this.renderTypeRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderTypeRow(id, matchStart, matchEnd, errorMessage);
|
||||||
case 'egggroup':
|
case 'egggroup':
|
||||||
return this.renderEggGroupRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderEggGroupRow(id, matchStart, matchEnd, errorMessage);
|
||||||
case 'tier':
|
case 'tier':
|
||||||
return this.renderTierRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderTierRow(id, matchStart, matchEnd, errorMessage);
|
||||||
case 'category':
|
case 'category':
|
||||||
return this.renderCategoryRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderCategoryRow(id, matchStart, matchEnd, errorMessage);
|
||||||
case 'article':
|
case 'article':
|
||||||
return this.renderArticleRow(id as ID, matchStart, matchEnd, errorMessage);
|
return this.renderArticleRow(id, matchStart, matchEnd, errorMessage);
|
||||||
}
|
}
|
||||||
return <li>Error: not found</li>;
|
return <li>Error: not found</li>;
|
||||||
}
|
}
|
||||||
|
|
@ -368,12 +386,11 @@ export class PSSearchResults extends preact.Component<{search: DexSearch}> {
|
||||||
)}
|
)}
|
||||||
{!search.query && <small style="color: #888">(backspace = delete filter)</small>}
|
{!search.query && <small style="color: #888">(backspace = delete filter)</small>}
|
||||||
</p>}
|
</p>}
|
||||||
{search.results &&
|
{
|
||||||
// TODO: implement windowing
|
// TODO: implement windowing
|
||||||
// for now, just show first twenty results
|
// for now, just show first twenty results
|
||||||
search.results.slice(0, 20).map(result =>
|
search.results?.slice(0, 20).map(result => this.renderRow(result))
|
||||||
this.renderRow(result)
|
}
|
||||||
)}
|
|
||||||
</ul>;
|
</ul>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {PS} from "./client-main";
|
import { PS } from "./client-main";
|
||||||
|
|
||||||
export class BattleBGM {
|
export class BattleBGM {
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,7 +102,7 @@ export class BattleBGM {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BattleSound = new class {
|
export const BattleSound = new class {
|
||||||
soundCache: {[url: string]: HTMLAudioElement | undefined} = {};
|
soundCache: { [url: string]: HTMLAudioElement | undefined } = {};
|
||||||
|
|
||||||
bgm: BattleBGM[] = [];
|
bgm: BattleBGM[] = [];
|
||||||
|
|
||||||
|
|
@ -171,7 +171,7 @@ export const BattleSound = new class {
|
||||||
loudnessPercentToAmplitudePercent(loudnessPercent: number) {
|
loudnessPercentToAmplitudePercent(loudnessPercent: number) {
|
||||||
// 10 dB is perceived as approximately twice as loud
|
// 10 dB is perceived as approximately twice as loud
|
||||||
let 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;
|
return 10 ** (decibels / 20) * 100;
|
||||||
}
|
}
|
||||||
setBgmVolume(bgmVolume: number) {
|
setBgmVolume(bgmVolume: number) {
|
||||||
this.bgmVolume = this.loudnessPercentToAmplitudePercent(bgmVolume);
|
this.bgmVolume = this.loudnessPercentToAmplitudePercent(bgmVolume);
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {toID, type ID} from "./battle-dex";
|
import { toID, type ID } from "./battle-dex";
|
||||||
|
|
||||||
export type Args = [string, ...string[]];
|
export type Args = [string, ...string[]];
|
||||||
export type KWArgs = {[kw: string]: string};
|
export type KWArgs = { [kw: string]: string };
|
||||||
export type SideID = 'p1' | 'p2' | 'p3' | 'p4';
|
export type SideID = 'p1' | 'p2' | 'p3' | 'p4';
|
||||||
|
|
||||||
export class BattleTextParser {
|
export class BattleTextParser {
|
||||||
|
|
@ -64,22 +64,22 @@ export class BattleTextParser {
|
||||||
return line.slice(1).split('|') as [string, ...string[]];
|
return line.slice(1).split('|') as [string, ...string[]];
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseBattleLine(line: string): {args: Args, kwArgs: KWArgs} {
|
static parseBattleLine(line: string): { args: Args, kwArgs: KWArgs } {
|
||||||
let args = this.parseLine(line, true);
|
let args = this.parseLine(line, true);
|
||||||
if (args) return {args, kwArgs: {}};
|
if (args) return { args, kwArgs: {} };
|
||||||
|
|
||||||
args = line.slice(1).split('|') as [string, ...string[]];
|
args = line.slice(1).split('|') as [string, ...string[]];
|
||||||
const kwArgs: KWArgs = {};
|
const kwArgs: KWArgs = {};
|
||||||
while (args.length > 1) {
|
while (args.length > 1) {
|
||||||
const lastArg = args[args.length - 1];
|
const lastArg = args[args.length - 1];
|
||||||
if (lastArg.charAt(0) !== '[') break;
|
if (!lastArg.startsWith('[')) break;
|
||||||
const bracketPos = lastArg.indexOf(']');
|
const bracketPos = lastArg.indexOf(']');
|
||||||
if (bracketPos <= 0) break;
|
if (bracketPos <= 0) break;
|
||||||
// default to '.' so it evaluates to boolean true
|
// default to '.' so it evaluates to boolean true
|
||||||
kwArgs[lastArg.slice(1, bracketPos)] = lastArg.slice(bracketPos + 1).trim() || '.';
|
kwArgs[lastArg.slice(1, bracketPos)] = lastArg.slice(bracketPos + 1).trim() || '.';
|
||||||
args.pop();
|
args.pop();
|
||||||
}
|
}
|
||||||
return BattleTextParser.upgradeArgs({args, kwArgs});
|
return BattleTextParser.upgradeArgs({ args, kwArgs });
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseNameParts(text: string) {
|
static parseNameParts(text: string) {
|
||||||
|
|
@ -102,7 +102,7 @@ export class BattleTextParser {
|
||||||
status = status.slice(1);
|
status = status.slice(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {group, name, away, status};
|
return { group, name, away, status };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -110,19 +110,19 @@ export class BattleTextParser {
|
||||||
* them to modern versions. Used to keep battle.ts itself cleaner. Not
|
* them to modern versions. Used to keep battle.ts itself cleaner. Not
|
||||||
* guaranteed to mutate or not mutate its inputs.
|
* guaranteed to mutate or not mutate its inputs.
|
||||||
*/
|
*/
|
||||||
static upgradeArgs({args, kwArgs}: {args: Args, kwArgs: KWArgs}): {args: Args, kwArgs: KWArgs} {
|
static upgradeArgs({ args, kwArgs }: { args: Args, kwArgs: KWArgs }): { args: Args, kwArgs: KWArgs } {
|
||||||
switch (args[0]) {
|
switch (args[0]) {
|
||||||
case '-activate': {
|
case '-activate': {
|
||||||
if (kwArgs.item || kwArgs.move || kwArgs.number || kwArgs.ability) return {args, kwArgs};
|
if (kwArgs.item || kwArgs.move || kwArgs.number || kwArgs.ability) return { args, kwArgs };
|
||||||
let [, pokemon, effect, arg3, arg4] = args;
|
let [, pokemon, effect, arg3, arg4] = args;
|
||||||
let target = kwArgs.of;
|
let target = kwArgs.of;
|
||||||
const id = BattleTextParser.effectId(effect);
|
const id = BattleTextParser.effectId(effect);
|
||||||
|
|
||||||
if (kwArgs.block) return {args: ['-fail', pokemon], kwArgs};
|
if (kwArgs.block) return { args: ['-fail', pokemon], kwArgs };
|
||||||
|
|
||||||
if (id === 'wonderguard') return {args: ['-immune', pokemon], kwArgs: {from: 'ability:Wonder Guard'}};
|
if (id === 'wonderguard') return { args: ['-immune', pokemon], kwArgs: { from: 'ability:Wonder Guard' } };
|
||||||
|
|
||||||
if (id === 'beatup' && kwArgs.of) return {args, kwArgs: {name: kwArgs.of}};
|
if (id === 'beatup' && kwArgs.of) return { args, kwArgs: { name: kwArgs.of } };
|
||||||
|
|
||||||
if ([
|
if ([
|
||||||
'ingrain', 'quickguard', 'wideguard', 'craftyshield', 'matblock', 'protect', 'mist', 'safeguard',
|
'ingrain', 'quickguard', 'wideguard', 'craftyshield', 'matblock', 'protect', 'mist', 'safeguard',
|
||||||
|
|
@ -131,22 +131,22 @@ export class BattleTextParser {
|
||||||
].includes(id)) {
|
].includes(id)) {
|
||||||
if (target) {
|
if (target) {
|
||||||
kwArgs.of = pokemon;
|
kwArgs.of = pokemon;
|
||||||
return {args: ['-block', target, effect, arg3], kwArgs};
|
return { args: ['-block', target, effect, arg3], kwArgs };
|
||||||
}
|
}
|
||||||
return {args: ['-block', pokemon, effect, arg3], kwArgs};
|
return { args: ['-block', pokemon, effect, arg3], kwArgs };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'charge') {
|
if (id === 'charge') {
|
||||||
return {args: ['-singlemove', pokemon, effect], kwArgs: {of: target}};
|
return { args: ['-singlemove', pokemon, effect], kwArgs: { of: target } };
|
||||||
}
|
}
|
||||||
if ([
|
if ([
|
||||||
'bind', 'wrap', 'clamp', 'whirlpool', 'firespin', 'magmastorm', 'sandtomb', 'infestation', 'snaptrap', 'thundercage', 'trapped',
|
'bind', 'wrap', 'clamp', 'whirlpool', 'firespin', 'magmastorm', 'sandtomb', 'infestation', 'snaptrap', 'thundercage', 'trapped',
|
||||||
].includes(id)) {
|
].includes(id)) {
|
||||||
return {args: ['-start', pokemon, effect], kwArgs: {of: target}};
|
return { args: ['-start', pokemon, effect], kwArgs: { of: target } };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'fairylock') {
|
if (id === 'fairylock') {
|
||||||
return {args: ['-fieldactivate', effect], kwArgs: {}};
|
return { args: ['-fieldactivate', effect], kwArgs: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id === 'symbiosis' || id === 'poltergeist') {
|
if (id === 'symbiosis' || id === 'poltergeist') {
|
||||||
|
|
@ -168,7 +168,7 @@ export class BattleTextParser {
|
||||||
|
|
||||||
case '-fail': {
|
case '-fail': {
|
||||||
if (kwArgs.from === 'ability: Flower Veil') {
|
if (kwArgs.from === 'ability: Flower Veil') {
|
||||||
return {args: ['-block', kwArgs.of, 'ability: Flower Veil'], kwArgs: {of: args[1]}};
|
return { args: ['-block', kwArgs.of, 'ability: Flower Veil'], kwArgs: { of: args[1] } };
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +187,7 @@ export class BattleTextParser {
|
||||||
let [, pokemon, effect, move] = args;
|
let [, pokemon, effect, move] = args;
|
||||||
if (['ability: Damp', 'ability: Dazzling', 'ability: Queenly Majesty', 'ability: Armor Tail'].includes(effect)) {
|
if (['ability: Damp', 'ability: Dazzling', 'ability: Queenly Majesty', 'ability: Armor Tail'].includes(effect)) {
|
||||||
args[0] = '-block';
|
args[0] = '-block';
|
||||||
return {args: ['-block', pokemon, effect, move, kwArgs.of], kwArgs: {}};
|
return { args: ['-block', pokemon, effect, move, kwArgs.of], kwArgs: {} };
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -211,15 +211,15 @@ export class BattleTextParser {
|
||||||
case '-nothing':
|
case '-nothing':
|
||||||
// OLD: |-nothing
|
// OLD: |-nothing
|
||||||
// NEW: |-activate||move:Splash
|
// NEW: |-activate||move:Splash
|
||||||
return {args: ['-activate', '', 'move:Splash'], kwArgs};
|
return { args: ['-activate', '', 'move:Splash'], kwArgs };
|
||||||
}
|
}
|
||||||
return {args, kwArgs};
|
return { args, kwArgs };
|
||||||
}
|
}
|
||||||
|
|
||||||
extractMessage(buf: string) {
|
extractMessage(buf: string) {
|
||||||
let out = '';
|
let out = '';
|
||||||
for (const line of buf.split('\n')) {
|
for (const line of buf.split('\n')) {
|
||||||
const {args, kwArgs} = BattleTextParser.parseBattleLine(line);
|
const { args, kwArgs } = BattleTextParser.parseBattleLine(line);
|
||||||
out += this.parseArgs(args, kwArgs) || '';
|
out += this.parseArgs(args, kwArgs) || '';
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
|
|
@ -229,13 +229,13 @@ export class BattleTextParser {
|
||||||
if (this.lowercaseRegExp === undefined) {
|
if (this.lowercaseRegExp === undefined) {
|
||||||
const prefixes = ['pokemon', 'opposingPokemon', 'team', 'opposingTeam', 'party', 'opposingParty'].map(templateId => {
|
const prefixes = ['pokemon', 'opposingPokemon', 'team', 'opposingTeam', 'party', 'opposingParty'].map(templateId => {
|
||||||
const template = BattleText.default[templateId];
|
const template = BattleText.default[templateId];
|
||||||
if (template.charAt(0) === template.charAt(0).toUpperCase()) return '';
|
if (template.startsWith(template.charAt(0).toUpperCase())) return '';
|
||||||
const bracketIndex = template.indexOf('[');
|
const bracketIndex = template.indexOf('[');
|
||||||
if (bracketIndex >= 0) return template.slice(0, bracketIndex);
|
if (bracketIndex >= 0) return template.slice(0, bracketIndex);
|
||||||
return template;
|
return template;
|
||||||
}).filter(prefix => prefix);
|
}).filter(prefix => prefix);
|
||||||
if (prefixes.length) {
|
if (prefixes.length) {
|
||||||
let buf = `((?:^|\n)(?: | \\\(|\\\[)?)(` +
|
let buf = `((?:^|\n)(?: | \\(|\\[)?)(` +
|
||||||
prefixes.map(BattleTextParser.escapeRegExp).join('|') +
|
prefixes.map(BattleTextParser.escapeRegExp).join('|') +
|
||||||
`)`;
|
`)`;
|
||||||
this.lowercaseRegExp = new RegExp(buf, 'g');
|
this.lowercaseRegExp = new RegExp(buf, 'g');
|
||||||
|
|
@ -302,7 +302,7 @@ export class BattleTextParser {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
team(side: string, isFar: boolean = false) {
|
team(side: string, isFar = false) {
|
||||||
side = side.slice(0, 2);
|
side = side.slice(0, 2);
|
||||||
if (side === this.perspective || side === BattleTextParser.allyID(side as SideID)) {
|
if (side === this.perspective || side === BattleTextParser.allyID(side as SideID)) {
|
||||||
return !isFar ? BattleText.default.team : BattleText.default.opposingTeam;
|
return !isFar ? BattleText.default.team : BattleText.default.opposingTeam;
|
||||||
|
|
@ -358,7 +358,7 @@ export class BattleTextParser {
|
||||||
let id = BattleTextParser.effectId(namespace);
|
let id = BattleTextParser.effectId(namespace);
|
||||||
if (BattleText[id] && type in BattleText[id]) {
|
if (BattleText[id] && type in BattleText[id]) {
|
||||||
if (BattleText[id][type].charAt(1) === '.') type = BattleText[id][type].slice(2) as ID;
|
if (BattleText[id][type].charAt(1) === '.') type = BattleText[id][type].slice(2) as ID;
|
||||||
if (BattleText[id][type].charAt(0) === '#') id = BattleText[id][type].slice(1) as ID;
|
if (BattleText[id][type].startsWith('#')) id = BattleText[id][type].slice(1) as ID;
|
||||||
if (!BattleText[id][type]) return '';
|
if (!BattleText[id][type]) return '';
|
||||||
return BattleText[id][type] + '\n';
|
return BattleText[id][type] + '\n';
|
||||||
}
|
}
|
||||||
|
|
@ -380,7 +380,7 @@ export class BattleTextParser {
|
||||||
|
|
||||||
static stat(stat: string) {
|
static stat(stat: string) {
|
||||||
const entry = BattleText[stat || "stats"];
|
const entry = BattleText[stat || "stats"];
|
||||||
if (!entry || !entry.statName) return `???stat:${stat}???`;
|
if (!entry?.statName) return `???stat:${stat}???`;
|
||||||
return entry.statName;
|
return entry.statName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -417,7 +417,7 @@ export class BattleTextParser {
|
||||||
return 'postMajor';
|
return 'postMajor';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (cmd.charAt(0) === '-' ? 'postMajor' : '');
|
return (cmd.startsWith('-') ? 'postMajor' : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
sectionBreak(args: Args, kwArgs: KWArgs) {
|
sectionBreak(args: Args, kwArgs: KWArgs) {
|
||||||
|
|
@ -601,7 +601,8 @@ export class BattleTextParser {
|
||||||
let id = BattleTextParser.effectId(effect);
|
let id = BattleTextParser.effectId(effect);
|
||||||
if (id === 'typechange') {
|
if (id === 'typechange') {
|
||||||
const template = this.template('typeChange', kwArgs.from);
|
const template = this.template('typeChange', kwArgs.from);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[TYPE]', arg3).replace('[SOURCE]', this.pokemon(kwArgs.of));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[TYPE]', arg3)
|
||||||
|
.replace('[SOURCE]', this.pokemon(kwArgs.of));
|
||||||
}
|
}
|
||||||
if (id === 'typeadd') {
|
if (id === 'typeadd') {
|
||||||
const template = this.template('typeAdd', kwArgs.from);
|
const template = this.template('typeAdd', kwArgs.from);
|
||||||
|
|
@ -629,13 +630,14 @@ export class BattleTextParser {
|
||||||
if (kwArgs.damage) templateId = 'activate';
|
if (kwArgs.damage) templateId = 'activate';
|
||||||
if (kwArgs.block) templateId = 'block';
|
if (kwArgs.block) templateId = 'block';
|
||||||
if (kwArgs.upkeep) templateId = 'upkeep';
|
if (kwArgs.upkeep) templateId = 'upkeep';
|
||||||
if (id === 'mist' && this.gen <= 2) templateId = 'startGen' + this.gen;
|
if (id === 'mist' && this.gen <= 2) templateId = `startGen${this.gen}`;
|
||||||
if (id === 'reflect' || id === 'lightscreen') templateId = 'startGen1';
|
if (id === 'reflect' || id === 'lightscreen') templateId = 'startGen1';
|
||||||
if (templateId === 'start' && kwArgs.from?.startsWith('item:')) {
|
if (templateId === 'start' && kwArgs.from?.startsWith('item:')) {
|
||||||
templateId += 'FromItem';
|
templateId += 'FromItem';
|
||||||
}
|
}
|
||||||
const template = this.template(templateId, kwArgs.from, effect);
|
const template = this.template(templateId, kwArgs.from, effect);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[EFFECT]', this.effect(effect)).replace('[MOVE]', arg3).replace('[SOURCE]', this.pokemon(kwArgs.of)).replace('[ITEM]', this.effect(kwArgs.from));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[EFFECT]', this.effect(effect))
|
||||||
|
.replace('[MOVE]', arg3).replace('[SOURCE]', this.pokemon(kwArgs.of)).replace('[ITEM]', this.effect(kwArgs.from));
|
||||||
}
|
}
|
||||||
|
|
||||||
case '-end': {
|
case '-end': {
|
||||||
|
|
@ -652,7 +654,8 @@ export class BattleTextParser {
|
||||||
template = this.template('endFromItem', effect);
|
template = this.template('endFromItem', effect);
|
||||||
}
|
}
|
||||||
if (!template) template = this.template(templateId, effect);
|
if (!template) template = this.template(templateId, effect);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[EFFECT]', this.effect(effect)).replace('[SOURCE]', this.pokemon(kwArgs.of)).replace('[ITEM]', this.effect(kwArgs.from));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[EFFECT]', this.effect(effect))
|
||||||
|
.replace('[SOURCE]', this.pokemon(kwArgs.of)).replace('[ITEM]', this.effect(kwArgs.from));
|
||||||
}
|
}
|
||||||
|
|
||||||
case '-ability': {
|
case '-ability': {
|
||||||
|
|
@ -671,7 +674,8 @@ export class BattleTextParser {
|
||||||
if (kwArgs.from) {
|
if (kwArgs.from) {
|
||||||
line1 = this.maybeAbility(kwArgs.from, pokemon) + line1;
|
line1 = this.maybeAbility(kwArgs.from, pokemon) + line1;
|
||||||
const template = this.template('changeAbility', kwArgs.from);
|
const template = this.template('changeAbility', kwArgs.from);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ABILITY]', this.effect(ability)).replace('[SOURCE]', this.pokemon(kwArgs.of));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ABILITY]', this.effect(ability))
|
||||||
|
.replace('[SOURCE]', this.pokemon(kwArgs.of));
|
||||||
}
|
}
|
||||||
const id = BattleTextParser.effectId(ability);
|
const id = BattleTextParser.effectId(ability);
|
||||||
if (id === 'unnerve') {
|
if (id === 'unnerve') {
|
||||||
|
|
@ -702,12 +706,14 @@ export class BattleTextParser {
|
||||||
const line1 = this.maybeAbility(kwArgs.from, kwArgs.of || pokemon);
|
const line1 = this.maybeAbility(kwArgs.from, kwArgs.of || pokemon);
|
||||||
if (['thief', 'covet', 'bestow', 'magician', 'pickpocket'].includes(id)) {
|
if (['thief', 'covet', 'bestow', 'magician', 'pickpocket'].includes(id)) {
|
||||||
const template = this.template('takeItem', kwArgs.from);
|
const template = this.template('takeItem', kwArgs.from);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(item)).replace('[SOURCE]', this.pokemon(target || kwArgs.of));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(item))
|
||||||
|
.replace('[SOURCE]', this.pokemon(target || kwArgs.of));
|
||||||
}
|
}
|
||||||
if (id === 'frisk') {
|
if (id === 'frisk') {
|
||||||
const hasTarget = kwArgs.of && pokemon && kwArgs.of !== pokemon;
|
const hasTarget = kwArgs.of && pokemon && kwArgs.of !== pokemon;
|
||||||
const template = this.template(hasTarget ? 'activate' : 'activateNoTarget', "Frisk");
|
const template = this.template(hasTarget ? 'activate' : 'activateNoTarget', "Frisk");
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(kwArgs.of)).replace('[ITEM]', this.effect(item)).replace('[TARGET]', this.pokemon(pokemon));
|
return line1 + template.replace('[POKEMON]', this.pokemon(kwArgs.of)).replace('[ITEM]', this.effect(item))
|
||||||
|
.replace('[TARGET]', this.pokemon(pokemon));
|
||||||
}
|
}
|
||||||
if (kwArgs.from) {
|
if (kwArgs.from) {
|
||||||
const template = this.template('addItem', kwArgs.from);
|
const template = this.template('addItem', kwArgs.from);
|
||||||
|
|
@ -727,7 +733,8 @@ export class BattleTextParser {
|
||||||
const id = BattleTextParser.effectId(kwArgs.from);
|
const id = BattleTextParser.effectId(kwArgs.from);
|
||||||
if (id === 'gem') {
|
if (id === 'gem') {
|
||||||
const template = this.template('useGem', item);
|
const template = this.template('useGem', item);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(item)).replace('[MOVE]', kwArgs.move);
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(item))
|
||||||
|
.replace('[MOVE]', kwArgs.move);
|
||||||
}
|
}
|
||||||
if (id === 'stealeat') {
|
if (id === 'stealeat') {
|
||||||
const template = this.template('removeItem', "Bug Bite");
|
const template = this.template('removeItem', "Bug Bite");
|
||||||
|
|
@ -735,7 +742,8 @@ export class BattleTextParser {
|
||||||
}
|
}
|
||||||
if (kwArgs.from) {
|
if (kwArgs.from) {
|
||||||
const template = this.template('removeItem', kwArgs.from);
|
const template = this.template('removeItem', kwArgs.from);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(item)).replace('[SOURCE]', this.pokemon(kwArgs.of));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(item))
|
||||||
|
.replace('[SOURCE]', this.pokemon(kwArgs.of));
|
||||||
}
|
}
|
||||||
if (kwArgs.weaken) {
|
if (kwArgs.weaken) {
|
||||||
const template = this.template('activateWeaken');
|
const template = this.template('activateWeaken');
|
||||||
|
|
@ -792,7 +800,8 @@ export class BattleTextParser {
|
||||||
}
|
}
|
||||||
let template = this.template('start', effect, 'NODEFAULT');
|
let template = this.template('start', effect, 'NODEFAULT');
|
||||||
if (!template) template = this.template('start').replace('[EFFECT]', this.effect(effect));
|
if (!template) template = this.template('start').replace('[EFFECT]', this.effect(effect));
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[SOURCE]', this.pokemon(kwArgs.of)).replace('[TEAM]', this.team(pokemon.slice(0, 2)));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[SOURCE]', this.pokemon(kwArgs.of))
|
||||||
|
.replace('[TEAM]', this.team(pokemon.slice(0, 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
case '-sidestart': {
|
case '-sidestart': {
|
||||||
|
|
@ -922,9 +931,11 @@ export class BattleTextParser {
|
||||||
line1 += this.ability(kwArgs.ability2, target);
|
line1 += this.ability(kwArgs.ability2, target);
|
||||||
}
|
}
|
||||||
if (kwArgs.move || kwArgs.number || kwArgs.item || kwArgs.name) {
|
if (kwArgs.move || kwArgs.number || kwArgs.item || kwArgs.name) {
|
||||||
template = template.replace('[MOVE]', kwArgs.move).replace('[NUMBER]', kwArgs.number).replace('[ITEM]', kwArgs.item).replace('[NAME]', kwArgs.name);
|
template = template.replace('[MOVE]', kwArgs.move).replace('[NUMBER]', kwArgs.number)
|
||||||
|
.replace('[ITEM]', kwArgs.item).replace('[NAME]', kwArgs.name);
|
||||||
}
|
}
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[TARGET]', this.pokemon(target)).replace('[SOURCE]', this.pokemon(kwArgs.of));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[TARGET]', this.pokemon(target))
|
||||||
|
.replace('[SOURCE]', this.pokemon(kwArgs.of));
|
||||||
}
|
}
|
||||||
|
|
||||||
case '-prepare': {
|
case '-prepare': {
|
||||||
|
|
@ -948,7 +959,8 @@ export class BattleTextParser {
|
||||||
}
|
}
|
||||||
if (kwArgs.from.startsWith('item:')) {
|
if (kwArgs.from.startsWith('item:')) {
|
||||||
template = this.template(kwArgs.of ? 'damageFromPokemon' : 'damageFromItem');
|
template = this.template(kwArgs.of ? 'damageFromPokemon' : 'damageFromItem');
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(kwArgs.from)).replace('[SOURCE]', this.pokemon(kwArgs.of));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[ITEM]', this.effect(kwArgs.from))
|
||||||
|
.replace('[SOURCE]', this.pokemon(kwArgs.of));
|
||||||
}
|
}
|
||||||
if (kwArgs.partiallytrapped || id === 'bind' || id === 'wrap') {
|
if (kwArgs.partiallytrapped || id === 'bind' || id === 'wrap') {
|
||||||
template = this.template('damageFromPartialTrapping');
|
template = this.template('damageFromPartialTrapping');
|
||||||
|
|
@ -964,7 +976,8 @@ export class BattleTextParser {
|
||||||
let template = this.template('heal', kwArgs.from, 'NODEFAULT');
|
let template = this.template('heal', kwArgs.from, 'NODEFAULT');
|
||||||
const line1 = this.maybeAbility(kwArgs.from, kwArgs.of || pokemon);
|
const line1 = this.maybeAbility(kwArgs.from, kwArgs.of || pokemon);
|
||||||
if (template) {
|
if (template) {
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[SOURCE]', this.pokemon(kwArgs.of)).replace('[NICKNAME]', kwArgs.wisher);
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[SOURCE]', this.pokemon(kwArgs.of))
|
||||||
|
.replace('[NICKNAME]', kwArgs.wisher);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kwArgs.from && !kwArgs.from.startsWith('ability:')) {
|
if (kwArgs.from && !kwArgs.from.startsWith('ability:')) {
|
||||||
|
|
@ -989,7 +1002,8 @@ export class BattleTextParser {
|
||||||
templateId += (kwArgs.multiple ? 'MultipleFromZEffect' : 'FromZEffect');
|
templateId += (kwArgs.multiple ? 'MultipleFromZEffect' : 'FromZEffect');
|
||||||
} else if (amount && kwArgs.from?.startsWith('item:')) {
|
} else if (amount && kwArgs.from?.startsWith('item:')) {
|
||||||
const template = this.template(templateId + 'FromItem', kwArgs.from);
|
const template = this.template(templateId + 'FromItem', kwArgs.from);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[STAT]', BattleTextParser.stat(stat)).replace('[ITEM]', this.effect(kwArgs.from));
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[STAT]', BattleTextParser.stat(stat))
|
||||||
|
.replace('[ITEM]', this.effect(kwArgs.from));
|
||||||
}
|
}
|
||||||
const template = this.template(templateId, kwArgs.from);
|
const template = this.template(templateId, kwArgs.from);
|
||||||
return line1 + template.replace(/\[POKEMON\]/g, this.pokemon(pokemon)).replace('[STAT]', BattleTextParser.stat(stat));
|
return line1 + template.replace(/\[POKEMON\]/g, this.pokemon(pokemon)).replace('[STAT]', BattleTextParser.stat(stat));
|
||||||
|
|
@ -1055,9 +1069,10 @@ export class BattleTextParser {
|
||||||
const line1 = this.maybeAbility(effect, kwArgs.of || pokemon);
|
const line1 = this.maybeAbility(effect, kwArgs.of || pokemon);
|
||||||
let id = BattleTextParser.effectId(effect);
|
let id = BattleTextParser.effectId(effect);
|
||||||
let templateId = 'block';
|
let templateId = 'block';
|
||||||
if (id === 'mist' && this.gen <= 2) templateId = 'blockGen' + this.gen;
|
if (id === 'mist' && this.gen <= 2) templateId = `blockGen${this.gen}`;
|
||||||
const template = this.template(templateId, effect);
|
const template = this.template(templateId, effect);
|
||||||
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon)).replace('[SOURCE]', this.pokemon(attacker || kwArgs.of)).replace('[MOVE]', move);
|
return line1 + template.replace('[POKEMON]', this.pokemon(pokemon))
|
||||||
|
.replace('[SOURCE]', this.pokemon(attacker || kwArgs.of)).replace('[MOVE]', move);
|
||||||
}
|
}
|
||||||
|
|
||||||
case '-fail': {
|
case '-fail': {
|
||||||
|
|
@ -1199,5 +1214,5 @@ declare const require: any;
|
||||||
declare const global: any;
|
declare const global: any;
|
||||||
if (typeof require === 'function') {
|
if (typeof require === 'function') {
|
||||||
// in Node
|
// in Node
|
||||||
(global as any).BattleTextParser = BattleTextParser;
|
global.BattleTextParser = BattleTextParser;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pokemon, type Battle, type ServerPokemon} from "./battle";
|
import { Pokemon, type Battle, type ServerPokemon } from "./battle";
|
||||||
import {Dex, ModdedDex, toID, type ID} from "./battle-dex";
|
import { Dex, type ModdedDex, toID, type ID } from "./battle-dex";
|
||||||
import type {BattleScene} from "./battle-animations";
|
import type { BattleScene } from "./battle-animations";
|
||||||
import {BattleLog} from "./battle-log";
|
import { BattleLog } from "./battle-log";
|
||||||
import {BattleNatures} from "./battle-dex-data";
|
import { BattleNatures } from "./battle-dex-data";
|
||||||
import {BattleTextParser} from "./battle-text-parser";
|
import { BattleTextParser } from "./battle-text-parser";
|
||||||
|
|
||||||
class ModifiableValue {
|
class ModifiableValue {
|
||||||
value = 0;
|
value = 0;
|
||||||
|
|
@ -197,7 +197,7 @@ export class BattleTooltips {
|
||||||
if (!BattleTooltips.isLocked) BattleTooltips.hideTooltip();
|
if (!BattleTooltips.isLocked) BattleTooltips.hideTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
listen(elem: HTMLElement | JQuery<HTMLElement>) {
|
listen(elem: HTMLElement | JQuery) {
|
||||||
const $elem = $(elem);
|
const $elem = $(elem);
|
||||||
$elem.on('mouseover', '.has-tooltip', this.showTooltipEvent);
|
$elem.on('mouseover', '.has-tooltip', this.showTooltipEvent);
|
||||||
$elem.on('click', '.has-tooltip', this.clickTooltipEvent);
|
$elem.on('click', '.has-tooltip', this.clickTooltipEvent);
|
||||||
|
|
@ -359,9 +359,9 @@ export class BattleTooltips {
|
||||||
// let side = this.battle.mySide.ally;
|
// let side = this.battle.mySide.ally;
|
||||||
let activeIndex = parseInt(args[1], 10);
|
let activeIndex = parseInt(args[1], 10);
|
||||||
let pokemon = null;
|
let pokemon = null;
|
||||||
/*if (activeIndex < side.pokemon.length) {
|
/* if (activeIndex < side.pokemon.length) {
|
||||||
pokemon = side.pokemon[activeIndex] || side.ally ? side.ally.pokemon[activeIndex] : null;
|
pokemon = side.pokemon[activeIndex] || side.ally ? side.ally.pokemon[activeIndex] : null;
|
||||||
}*/
|
} */
|
||||||
let serverPokemon = this.battle.myAllyPokemon ? this.battle.myAllyPokemon[activeIndex] : null;
|
let serverPokemon = this.battle.myAllyPokemon ? this.battle.myAllyPokemon[activeIndex] : null;
|
||||||
buf = this.showPokemonTooltip(pokemon, serverPokemon);
|
buf = this.showPokemonTooltip(pokemon, serverPokemon);
|
||||||
break;
|
break;
|
||||||
|
|
@ -373,7 +373,7 @@ export class BattleTooltips {
|
||||||
default:
|
default:
|
||||||
// "throws" an error without crashing
|
// "throws" an error without crashing
|
||||||
Promise.resolve(new Error(`unrecognized type`));
|
Promise.resolve(new Error(`unrecognized type`));
|
||||||
buf = `<p class="message-error" style="white-space: pre-wrap">${new Error(`unrecognized type`).stack}</p>`;
|
buf = `<p class="message-error" style="white-space: pre-wrap">${new Error(`unrecognized type`).stack!}</p>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.placeTooltip(buf, elem, ownHeight, type);
|
this.placeTooltip(buf, elem, ownHeight, type);
|
||||||
|
|
@ -412,7 +412,7 @@ export class BattleTooltips {
|
||||||
try {
|
try {
|
||||||
const selection = window.getSelection()!;
|
const selection = window.getSelection()!;
|
||||||
if (selection.type === 'Range') return;
|
if (selection.type === 'Range') return;
|
||||||
} catch (err) {}
|
} catch {}
|
||||||
BattleTooltips.hideTooltip();
|
BattleTooltips.hideTooltip();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -422,7 +422,7 @@ export class BattleTooltips {
|
||||||
left: x,
|
left: x,
|
||||||
top: y,
|
top: y,
|
||||||
});
|
});
|
||||||
innerHTML = `<div class="tooltipinner"><div class="tooltip tooltip-${type}">${innerHTML}</div></div>`;
|
innerHTML = `<div class="tooltipinner"><div class="tooltip tooltip-${type!}">${innerHTML}</div></div>`;
|
||||||
$wrapper.html(innerHTML).appendTo(document.body);
|
$wrapper.html(innerHTML).appendTo(document.body);
|
||||||
BattleTooltips.elem = $wrapper.find('.tooltip')[0] as HTMLDivElement;
|
BattleTooltips.elem = $wrapper.find('.tooltip')[0] as HTMLDivElement;
|
||||||
BattleTooltips.isLocked = false;
|
BattleTooltips.isLocked = false;
|
||||||
|
|
@ -461,7 +461,7 @@ export class BattleTooltips {
|
||||||
BattleTooltips.hideTooltip();
|
BattleTooltips.hideTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
static zMoveEffects: {[zEffect: string]: string} = {
|
static zMoveEffects: { [zEffect: string]: string } = {
|
||||||
'clearnegativeboost': "Restores negative stat stages to 0",
|
'clearnegativeboost': "Restores negative stat stages to 0",
|
||||||
'crit2': "Crit ratio +2",
|
'crit2': "Crit ratio +2",
|
||||||
'heal': "Restores HP 100%",
|
'heal': "Restores HP 100%",
|
||||||
|
|
@ -476,15 +476,14 @@ export class BattleTooltips {
|
||||||
}
|
}
|
||||||
let boostText = '';
|
let boostText = '';
|
||||||
if (move.zMove!.boost) {
|
if (move.zMove!.boost) {
|
||||||
let boosts = Object.keys(move.zMove!.boost) as Dex.StatName[];
|
boostText = Object.entries(move.zMove!.boost).map(([stat, boost]) =>
|
||||||
boostText = boosts.map(stat =>
|
`${BattleTextParser.stat(stat)} +${boost}`
|
||||||
BattleTextParser.stat(stat) + ' +' + move.zMove!.boost![stat]
|
|
||||||
).join(', ');
|
).join(', ');
|
||||||
}
|
}
|
||||||
return boostText;
|
return boostText;
|
||||||
}
|
}
|
||||||
|
|
||||||
static zMoveTable: {[type in Dex.TypeName]: string} = {
|
static zMoveTable: { [type in Dex.TypeName]: string } = {
|
||||||
Poison: "Acid Downpour",
|
Poison: "Acid Downpour",
|
||||||
Fighting: "All-Out Pummeling",
|
Fighting: "All-Out Pummeling",
|
||||||
Dark: "Black Hole Eclipse",
|
Dark: "Black Hole Eclipse",
|
||||||
|
|
@ -507,7 +506,7 @@ export class BattleTooltips {
|
||||||
"???": "",
|
"???": "",
|
||||||
};
|
};
|
||||||
|
|
||||||
static maxMoveTable: {[type in Dex.TypeName]: string} = {
|
static maxMoveTable: { [type in Dex.TypeName]: string } = {
|
||||||
Poison: "Max Ooze",
|
Poison: "Max Ooze",
|
||||||
Fighting: "Max Knuckle",
|
Fighting: "Max Knuckle",
|
||||||
Dark: "Max Darkness",
|
Dark: "Max Darkness",
|
||||||
|
|
@ -623,7 +622,7 @@ export class BattleTooltips {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
text += '<h2>' + move.name + '<br />';
|
text += `<h2>${move.name}<br />`;
|
||||||
|
|
||||||
text += Dex.getTypeIcon(moveType);
|
text += Dex.getTypeIcon(moveType);
|
||||||
text += ` ${Dex.getCategoryIcon(category)}</h2>`;
|
text += ` ${Dex.getCategoryIcon(category)}</h2>`;
|
||||||
|
|
@ -635,16 +634,16 @@ export class BattleTooltips {
|
||||||
// Otherwise, it is just shown as in singles.
|
// Otherwise, it is just shown as in singles.
|
||||||
// The trick is that we need to calculate it first for each Pokémon to see if it changes.
|
// The trick is that we need to calculate it first for each Pokémon to see if it changes.
|
||||||
let prevBasePower: string | null = null;
|
let prevBasePower: string | null = null;
|
||||||
let basePower: string = '';
|
let basePower = '';
|
||||||
let difference = false;
|
let difference = false;
|
||||||
let basePowers = [];
|
let basePowers = [];
|
||||||
for (const active of foeActive) {
|
for (const active of foeActive) {
|
||||||
if (!active) continue;
|
if (!active) continue;
|
||||||
value = this.getMoveBasePower(move, moveType, value, active);
|
value = this.getMoveBasePower(move, moveType, value, active);
|
||||||
basePower = '' + value;
|
basePower = `${value}`;
|
||||||
if (prevBasePower === null) prevBasePower = basePower;
|
if (prevBasePower === null) prevBasePower = basePower;
|
||||||
if (prevBasePower !== basePower) difference = true;
|
if (prevBasePower !== basePower) difference = true;
|
||||||
basePowers.push('Base power vs ' + active.name + ': ' + basePower);
|
basePowers.push(`Base power vs ${active.name}: ${basePower}`);
|
||||||
}
|
}
|
||||||
if (difference) {
|
if (difference) {
|
||||||
text += '<p>' + basePowers.join('<br />') + '</p>';
|
text += '<p>' + basePowers.join('<br />') + '</p>';
|
||||||
|
|
@ -655,7 +654,7 @@ export class BattleTooltips {
|
||||||
if (!showingMultipleBasePowers && category !== 'Status') {
|
if (!showingMultipleBasePowers && category !== 'Status') {
|
||||||
let activeTarget = foeActive[0] || foeActive[1] || foeActive[2];
|
let activeTarget = foeActive[0] || foeActive[1] || foeActive[2];
|
||||||
value = this.getMoveBasePower(move, moveType, value, activeTarget);
|
value = this.getMoveBasePower(move, moveType, value, activeTarget);
|
||||||
text += '<p>Base power: ' + value + '</p>';
|
text += `<p>Base power: ${value}</p>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let accuracy = this.getMoveAccuracy(move, value);
|
let accuracy = this.getMoveAccuracy(move, value);
|
||||||
|
|
@ -683,22 +682,22 @@ export class BattleTooltips {
|
||||||
calls = 'Swift';
|
calls = 'Swift';
|
||||||
}
|
}
|
||||||
let calledMove = this.battle.dex.moves.get(calls);
|
let calledMove = this.battle.dex.moves.get(calls);
|
||||||
text += 'Calls ' + Dex.getTypeIcon(this.getMoveType(calledMove, value)[0]) + ' ' + calledMove.name;
|
text += `Calls ${Dex.getTypeIcon(this.getMoveType(calledMove, value)[0])} ${calledMove.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
text += '<p>Accuracy: ' + accuracy + '</p>';
|
text += `<p>Accuracy: ${accuracy}</p>`;
|
||||||
if (zEffect) text += '<p>Z-Effect: ' + zEffect + '</p>';
|
if (zEffect) text += `<p>Z-Effect: ${zEffect}</p>`;
|
||||||
|
|
||||||
if (this.battle.hardcoreMode) {
|
if (this.battle.hardcoreMode) {
|
||||||
text += '<p class="tooltip-section">' + move.shortDesc + '</p>';
|
text += `<p class="tooltip-section">${move.shortDesc}</p>`;
|
||||||
} else {
|
} else {
|
||||||
text += '<p class="tooltip-section">';
|
text += '<p class="tooltip-section">';
|
||||||
if (move.priority > 1) {
|
if (move.priority > 1) {
|
||||||
text += 'Nearly always moves first <em>(priority +' + move.priority + ')</em>.</p><p>';
|
text += `Nearly always moves first <em>(priority +${move.priority})</em>.</p><p>`;
|
||||||
} else if (move.priority <= -1) {
|
} else if (move.priority <= -1) {
|
||||||
text += 'Nearly always moves last <em>(priority −' + (-move.priority) + ')</em>.</p><p>';
|
text += `Nearly always moves last <em>(priority −${-move.priority})</em>.</p><p>`;
|
||||||
} else if (move.priority === 1) {
|
} else if (move.priority === 1) {
|
||||||
text += 'Usually moves first <em>(priority +' + move.priority + ')</em>.</p><p>';
|
text += `Usually moves first <em>(priority +${move.priority})</em>.</p><p>`;
|
||||||
} else {
|
} else {
|
||||||
if (move.id === 'grassyglide' && this.battle.hasPseudoWeather('Grassy Terrain')) {
|
if (move.id === 'grassyglide' && this.battle.hasPseudoWeather('Grassy Terrain')) {
|
||||||
text += 'Usually moves first <em>(priority +1)</em>.</p><p>';
|
text += 'Usually moves first <em>(priority +1)</em>.</p><p>';
|
||||||
|
|
@ -803,7 +802,7 @@ export class BattleTooltips {
|
||||||
|
|
||||||
let name = BattleLog.escapeHTML(pokemon.name);
|
let name = BattleLog.escapeHTML(pokemon.name);
|
||||||
if (pokemon.speciesForme !== pokemon.name) {
|
if (pokemon.speciesForme !== pokemon.name) {
|
||||||
name += ' <small>(' + BattleLog.escapeHTML(pokemon.speciesForme) + ')</small>';
|
name += ` <small>(${BattleLog.escapeHTML(pokemon.speciesForme)})</small>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let levelBuf = (pokemon.level !== 100 ? ` <small>L${pokemon.level}</small>` : ``);
|
let levelBuf = (pokemon.level !== 100 ? ` <small>L${pokemon.level}</small>` : ``);
|
||||||
|
|
@ -843,25 +842,31 @@ export class BattleTooltips {
|
||||||
text += '<p><small>HP:</small> (fainted)</p>';
|
text += '<p><small>HP:</small> (fainted)</p>';
|
||||||
} else if (this.battle.hardcoreMode) {
|
} else if (this.battle.hardcoreMode) {
|
||||||
if (serverPokemon) {
|
if (serverPokemon) {
|
||||||
text += '<p><small>HP:</small> ' + serverPokemon.hp + '/' + serverPokemon.maxhp + (pokemon.status ? ' <span class="status ' + pokemon.status + '">' + pokemon.status.toUpperCase() + '</span>' : '') + '</p>';
|
const status = pokemon.status ? ` <span class="status ${pokemon.status}">${pokemon.status.toUpperCase()}</span>` : '';
|
||||||
|
text += `<p><small>HP:</small> ${serverPokemon.hp}/${serverPokemon.maxhp}${status}</p>`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let exacthp = '';
|
let exacthp = '';
|
||||||
if (serverPokemon) {
|
if (serverPokemon) {
|
||||||
exacthp = ' (' + serverPokemon.hp + '/' + serverPokemon.maxhp + ')';
|
exacthp = ` (${serverPokemon.hp}/${serverPokemon.maxhp})`;
|
||||||
} else if (pokemon.maxhp === 48) {
|
} else if (pokemon.maxhp === 48) {
|
||||||
exacthp = ' <small>(' + pokemon.hp + '/' + pokemon.maxhp + ' pixels)</small>';
|
exacthp = ` <small>(${pokemon.hp}/${pokemon.maxhp} pixels)</small>`;
|
||||||
}
|
}
|
||||||
text += '<p><small>HP:</small> ' + Pokemon.getHPText(pokemon, this.battle.reportExactHP) + exacthp + (pokemon.status ? ' <span class="status ' + pokemon.status + '">' + pokemon.status.toUpperCase() + '</span>' : '');
|
const status = pokemon.status ? ` <span class="status ${pokemon.status}">${pokemon.status.toUpperCase()}</span>` : '';
|
||||||
|
text += `<p><small>HP:</small> ${Pokemon.getHPText(pokemon, this.battle.reportExactHP)}${exacthp}${status}`;
|
||||||
if (clientPokemon) {
|
if (clientPokemon) {
|
||||||
if (pokemon.status === 'tox') {
|
if (pokemon.status === 'tox') {
|
||||||
if (pokemon.ability === 'Poison Heal' || pokemon.ability === 'Magic Guard') {
|
if (pokemon.ability === 'Poison Heal' || pokemon.ability === 'Magic Guard') {
|
||||||
text += ' <small>Would take if ability removed: ' + Math.floor(100 / 16 * Math.min(clientPokemon.statusData.toxicTurns + 1, 15)) + '%</small>';
|
text += ` <small>Would take if ability removed: ${Math.floor(
|
||||||
|
100 / 16 * Math.min(clientPokemon.statusData.toxicTurns + 1, 15)
|
||||||
|
)}%</small>`;
|
||||||
} else {
|
} else {
|
||||||
text += ' Next damage: ' + Math.floor(100 / (clientPokemon.volatiles['dynamax'] ? 32 : 16) * Math.min(clientPokemon.statusData.toxicTurns + 1, 15)) + '%';
|
text += ` Next damage: ${Math.floor(
|
||||||
|
100 / (clientPokemon.volatiles['dynamax'] ? 32 : 16) * Math.min(clientPokemon.statusData.toxicTurns + 1, 15)
|
||||||
|
)}%`;
|
||||||
}
|
}
|
||||||
} else if (pokemon.status === 'slp') {
|
} else if (pokemon.status === 'slp') {
|
||||||
text += ' Turns asleep: ' + clientPokemon.statusData.sleepTurns;
|
text += ` Turns asleep: ${clientPokemon.statusData.sleepTurns}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text += '</p>';
|
text += '</p>';
|
||||||
|
|
@ -940,7 +945,7 @@ export class BattleTooltips {
|
||||||
text += `${this.getPPUseText(row)}<br />`;
|
text += `${this.getPPUseText(row)}<br />`;
|
||||||
}
|
}
|
||||||
if (clientPokemon.moveTrack.filter(([moveName]) => {
|
if (clientPokemon.moveTrack.filter(([moveName]) => {
|
||||||
if (moveName.charAt(0) === '*') return false;
|
if (moveName.startsWith('*')) return false;
|
||||||
const move = this.battle.dex.moves.get(moveName);
|
const move = this.battle.dex.moves.get(moveName);
|
||||||
return !move.isZ && !move.isMax && move.name !== 'Mimic';
|
return !move.isZ && !move.isMax && move.name !== 'Mimic';
|
||||||
}).length > 4) {
|
}).length > 4) {
|
||||||
|
|
@ -995,7 +1000,7 @@ export class BattleTooltips {
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateModifiedStats(clientPokemon: Pokemon | null, serverPokemon: ServerPokemon, statStagesOnly?: boolean) {
|
calculateModifiedStats(clientPokemon: Pokemon | null, serverPokemon: ServerPokemon, statStagesOnly?: boolean) {
|
||||||
let stats = {...serverPokemon.stats};
|
let stats = { ...serverPokemon.stats };
|
||||||
let pokemon = clientPokemon || serverPokemon;
|
let pokemon = clientPokemon || serverPokemon;
|
||||||
const isPowerTrick = clientPokemon?.volatiles['powertrick'];
|
const isPowerTrick = clientPokemon?.volatiles['powertrick'];
|
||||||
for (const statName of Dex.statNamesExceptHP) {
|
for (const statName of Dex.statNamesExceptHP) {
|
||||||
|
|
@ -1049,7 +1054,9 @@ export class BattleTooltips {
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = toID(serverPokemon.item);
|
let item = toID(serverPokemon.item);
|
||||||
let speedHalvingEVItems = ['machobrace', 'poweranklet', 'powerband', 'powerbelt', 'powerbracer', 'powerlens', 'powerweight'];
|
let speedHalvingEVItems = [
|
||||||
|
'machobrace', 'poweranklet', 'powerband', 'powerbelt', 'powerbracer', 'powerlens', 'powerweight',
|
||||||
|
];
|
||||||
if (
|
if (
|
||||||
(ability === 'klutz' && !speedHalvingEVItems.includes(item)) ||
|
(ability === 'klutz' && !speedHalvingEVItems.includes(item)) ||
|
||||||
this.battle.hasPseudoWeather('Magic Room') ||
|
this.battle.hasPseudoWeather('Magic Room') ||
|
||||||
|
|
@ -1188,9 +1195,9 @@ export class BattleTooltips {
|
||||||
const isNFE = this.battle.dex.species.get(serverPokemon.speciesForme).evos?.some(evo => {
|
const isNFE = this.battle.dex.species.get(serverPokemon.speciesForme).evos?.some(evo => {
|
||||||
const evoSpecies = this.battle.dex.species.get(evo);
|
const evoSpecies = this.battle.dex.species.get(evo);
|
||||||
return !evoSpecies.isNonstandard ||
|
return !evoSpecies.isNonstandard ||
|
||||||
evoSpecies.isNonstandard === this.battle.dex.species.get(serverPokemon.speciesForme)?.isNonstandard ||
|
evoSpecies.isNonstandard === this.battle.dex.species.get(serverPokemon.speciesForme)?.isNonstandard ||
|
||||||
// Pokemon with Hisui evolutions
|
// Pokemon with Hisui evolutions
|
||||||
evoSpecies.isNonstandard === "Unobtainable";
|
evoSpecies.isNonstandard === "Unobtainable";
|
||||||
});
|
});
|
||||||
if (item === 'eviolite' && (isNFE || this.battle.dex.species.get(serverPokemon.speciesForme).id === 'dipplin')) {
|
if (item === 'eviolite' && (isNFE || this.battle.dex.species.get(serverPokemon.speciesForme).id === 'dipplin')) {
|
||||||
stats.def = Math.floor(stats.def * 1.5);
|
stats.def = Math.floor(stats.def * 1.5);
|
||||||
|
|
@ -1362,7 +1369,7 @@ export class BattleTooltips {
|
||||||
chainedSpeedModifier *= modifier;
|
chainedSpeedModifier *= modifier;
|
||||||
}
|
}
|
||||||
// Chained modifiers round down on 0.5
|
// Chained modifiers round down on 0.5
|
||||||
stats.spe = stats.spe * chainedSpeedModifier;
|
stats.spe *= chainedSpeedModifier;
|
||||||
stats.spe = stats.spe % 1 > 0.5 ? Math.ceil(stats.spe) : Math.floor(stats.spe);
|
stats.spe = stats.spe % 1 > 0.5 ? Math.ceil(stats.spe) : Math.floor(stats.spe);
|
||||||
|
|
||||||
if (pokemon.status === 'par' && ability !== 'quickfeet') {
|
if (pokemon.status === 'par' && ability !== 'quickfeet') {
|
||||||
|
|
@ -1381,7 +1388,7 @@ export class BattleTooltips {
|
||||||
if (!serverPokemon || isTransformed) {
|
if (!serverPokemon || isTransformed) {
|
||||||
if (!clientPokemon) throw new Error('Must pass either clientPokemon or serverPokemon');
|
if (!clientPokemon) throw new Error('Must pass either clientPokemon or serverPokemon');
|
||||||
let [min, max] = this.getSpeedRange(clientPokemon);
|
let [min, max] = this.getSpeedRange(clientPokemon);
|
||||||
return '<p><small>Spe</small> ' + min + ' to ' + max + ' <small>(before items/abilities/modifiers)</small></p>';
|
return `<p><small>Spe</small> ${min} to ${max} <small>(before items/abilities/modifiers)</small></p>`;
|
||||||
}
|
}
|
||||||
const stats = serverPokemon.stats;
|
const stats = serverPokemon.stats;
|
||||||
const modifiedStats = this.calculateModifiedStats(clientPokemon, serverPokemon);
|
const modifiedStats = this.calculateModifiedStats(clientPokemon, serverPokemon);
|
||||||
|
|
@ -1394,8 +1401,8 @@ export class BattleTooltips {
|
||||||
if (this.battle.gen === 1 && statName === 'spd') continue;
|
if (this.battle.gen === 1 && statName === 'spd') continue;
|
||||||
let statLabel = this.battle.gen === 1 && statName === 'spa' ? 'spc' : statName;
|
let statLabel = this.battle.gen === 1 && statName === 'spa' ? 'spc' : statName;
|
||||||
buf += statName === 'atk' ? '<small>' : '<small> / ';
|
buf += statName === 'atk' ? '<small>' : '<small> / ';
|
||||||
buf += '' + BattleText[statLabel].statShortName + ' </small>';
|
buf += `${BattleText[statLabel].statShortName} </small>`;
|
||||||
buf += '' + stats[statName];
|
buf += `${stats[statName]}`;
|
||||||
if (modifiedStats[statName] !== stats[statName]) hasModifiedStat = true;
|
if (modifiedStats[statName] !== stats[statName]) hasModifiedStat = true;
|
||||||
}
|
}
|
||||||
buf += '</p>';
|
buf += '</p>';
|
||||||
|
|
@ -1410,13 +1417,13 @@ export class BattleTooltips {
|
||||||
if (this.battle.gen === 1 && statName === 'spd') continue;
|
if (this.battle.gen === 1 && statName === 'spd') continue;
|
||||||
let statLabel = this.battle.gen === 1 && statName === 'spa' ? 'spc' : statName;
|
let statLabel = this.battle.gen === 1 && statName === 'spa' ? 'spc' : statName;
|
||||||
buf += statName === 'atk' ? '<small>' : '<small> / ';
|
buf += statName === 'atk' ? '<small>' : '<small> / ';
|
||||||
buf += '' + BattleText[statLabel].statShortName + ' </small>';
|
buf += `${BattleText[statLabel].statShortName} </small>`;
|
||||||
if (modifiedStats[statName] === stats[statName]) {
|
if (modifiedStats[statName] === stats[statName]) {
|
||||||
buf += '' + modifiedStats[statName];
|
buf += `${modifiedStats[statName]}`;
|
||||||
} else if (modifiedStats[statName] < stats[statName]) {
|
} else if (modifiedStats[statName] < stats[statName]) {
|
||||||
buf += '<strong class="stat-lowered">' + modifiedStats[statName] + '</strong>';
|
buf += `<strong class="stat-lowered">${modifiedStats[statName]}</strong>`;
|
||||||
} else {
|
} else {
|
||||||
buf += '<strong class="stat-boosted">' + modifiedStats[statName] + '</strong>';
|
buf += `<strong class="stat-boosted">${modifiedStats[statName]}</strong>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf += '</p>';
|
buf += '</p>';
|
||||||
|
|
@ -1427,7 +1434,7 @@ export class BattleTooltips {
|
||||||
let [moveName, ppUsed] = moveTrackRow;
|
let [moveName, ppUsed] = moveTrackRow;
|
||||||
let move;
|
let move;
|
||||||
let maxpp;
|
let maxpp;
|
||||||
if (moveName.charAt(0) === '*') {
|
if (moveName.startsWith('*')) {
|
||||||
// Transformed move
|
// Transformed move
|
||||||
move = this.battle.dex.moves.get(moveName.substr(1));
|
move = this.battle.dex.moves.get(moveName.substr(1));
|
||||||
maxpp = 5;
|
maxpp = 5;
|
||||||
|
|
@ -1436,11 +1443,11 @@ export class BattleTooltips {
|
||||||
maxpp = (move.pp === 1 || move.noPPBoosts ? move.pp : move.pp * 8 / 5);
|
maxpp = (move.pp === 1 || move.noPPBoosts ? move.pp : move.pp * 8 / 5);
|
||||||
if (this.battle.gen < 3) maxpp = Math.min(61, maxpp);
|
if (this.battle.gen < 3) maxpp = Math.min(61, maxpp);
|
||||||
}
|
}
|
||||||
const bullet = moveName.charAt(0) === '*' || move.isZ ? '<span style="color:#888">•</span>' : '•';
|
const bullet = moveName.startsWith('*') || move.isZ ? '<span style="color:#888">•</span>' : '•';
|
||||||
if (ppUsed === Infinity) {
|
if (ppUsed === Infinity) {
|
||||||
return `${bullet} ${move.name} <small>(0/${maxpp})</small>`;
|
return `${bullet} ${move.name} <small>(0/${maxpp})</small>`;
|
||||||
}
|
}
|
||||||
if (ppUsed || moveName.charAt(0) === '*') {
|
if (ppUsed || moveName.startsWith('*')) {
|
||||||
return `${bullet} ${move.name} <small>(${maxpp - ppUsed}/${maxpp})</small>`;
|
return `${bullet} ${move.name} <small>(${maxpp - ppUsed}/${maxpp})</small>`;
|
||||||
}
|
}
|
||||||
return `${bullet} ${move.name} ${showKnown ? ' <small>(revealed)</small>' : ''}`;
|
return `${bullet} ${move.name} ${showKnown ? ' <small>(revealed)</small>' : ''}`;
|
||||||
|
|
@ -1448,7 +1455,7 @@ export class BattleTooltips {
|
||||||
|
|
||||||
ppUsed(move: Dex.Move, pokemon: Pokemon) {
|
ppUsed(move: Dex.Move, pokemon: Pokemon) {
|
||||||
for (let [moveName, ppUsed] of pokemon.moveTrack) {
|
for (let [moveName, ppUsed] of pokemon.moveTrack) {
|
||||||
if (moveName.charAt(0) === '*') moveName = moveName.substr(1);
|
if (moveName.startsWith('*')) moveName = moveName.substr(1);
|
||||||
if (move.name === moveName) return ppUsed;
|
if (move.name === moveName) return ppUsed;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1472,7 +1479,7 @@ export class BattleTooltips {
|
||||||
if (rules['Frantic Fusions Mod']) {
|
if (rules['Frantic Fusions Mod']) {
|
||||||
const fusionSpecies = this.battle.dex.species.get(pokemon.name);
|
const fusionSpecies = this.battle.dex.species.get(pokemon.name);
|
||||||
if (fusionSpecies.exists && fusionSpecies.name !== species.name) {
|
if (fusionSpecies.exists && fusionSpecies.name !== species.name) {
|
||||||
baseSpe = baseSpe + tr(fusionSpecies.baseStats.spe / 4);
|
baseSpe += tr(fusionSpecies.baseStats.spe / 4);
|
||||||
if (baseSpe < 1) baseSpe = 1;
|
if (baseSpe < 1) baseSpe = 1;
|
||||||
if (baseSpe > 255) baseSpe = 255;
|
if (baseSpe > 255) baseSpe = 255;
|
||||||
}
|
}
|
||||||
|
|
@ -1648,7 +1655,7 @@ export class BattleTooltips {
|
||||||
// There aren't any max moves with the sound flag, but if there were, Liquid Voice would make them water type
|
// There aren't any max moves with the sound flag, but if there were, Liquid Voice would make them water type
|
||||||
const isSound = !!(
|
const isSound = !!(
|
||||||
forMaxMove ?
|
forMaxMove ?
|
||||||
this.getMaxMoveFromType(moveType, forMaxMove !== true && forMaxMove || undefined) : move
|
this.getMaxMoveFromType(moveType, forMaxMove !== true && forMaxMove || undefined) : move
|
||||||
).flags['sound'];
|
).flags['sound'];
|
||||||
if (isSound && value.abilityModify(0, 'Liquid Voice')) {
|
if (isSound && value.abilityModify(0, 'Liquid Voice')) {
|
||||||
moveType = 'Water';
|
moveType = 'Water';
|
||||||
|
|
@ -1716,7 +1723,7 @@ export class BattleTooltips {
|
||||||
getMoveAccuracy(move: Dex.Move, value: ModifiableValue, target?: Pokemon) {
|
getMoveAccuracy(move: Dex.Move, value: ModifiableValue, target?: Pokemon) {
|
||||||
value.reset(move.accuracy === true ? 0 : move.accuracy, true);
|
value.reset(move.accuracy === true ? 0 : move.accuracy, true);
|
||||||
|
|
||||||
let pokemon = value.pokemon!;
|
let pokemon = value.pokemon;
|
||||||
// Sure-hit accuracy
|
// Sure-hit accuracy
|
||||||
if (move.id === 'toxic' && this.battle.gen >= 6 && this.pokemonHasType(pokemon, 'Poison')) {
|
if (move.id === 'toxic' && this.battle.gen >= 6 && this.pokemonHasType(pokemon, 'Poison')) {
|
||||||
value.set(0, "Poison type");
|
value.set(0, "Poison type");
|
||||||
|
|
@ -1859,7 +1866,7 @@ export class BattleTooltips {
|
||||||
// Takes into account the target for some moves.
|
// Takes into account the target for some moves.
|
||||||
// If it is unsure of the actual base power, it gives an estimate.
|
// If it is unsure of the actual base power, it gives an estimate.
|
||||||
getMoveBasePower(move: Dex.Move, moveType: Dex.TypeName, value: ModifiableValue, target: Pokemon | null = null) {
|
getMoveBasePower(move: Dex.Move, moveType: Dex.TypeName, value: ModifiableValue, target: Pokemon | null = null) {
|
||||||
const pokemon = value.pokemon!;
|
const pokemon = value.pokemon;
|
||||||
const serverPokemon = value.serverPokemon;
|
const serverPokemon = value.serverPokemon;
|
||||||
|
|
||||||
// apply modifiers for moves that depend on the actual stats
|
// apply modifiers for moves that depend on the actual stats
|
||||||
|
|
@ -2059,9 +2066,9 @@ export class BattleTooltips {
|
||||||
// Base power based on times hit
|
// Base power based on times hit
|
||||||
if (move.id === 'ragefist') {
|
if (move.id === 'ragefist') {
|
||||||
value.set(Math.min(350, 50 + 50 * pokemon.timesAttacked),
|
value.set(Math.min(350, 50 + 50 * pokemon.timesAttacked),
|
||||||
pokemon.timesAttacked > 0
|
pokemon.timesAttacked > 0 ?
|
||||||
? `Hit ${pokemon.timesAttacked} time${pokemon.timesAttacked > 1 ? 's' : ''}`
|
`Hit ${pokemon.timesAttacked} time${pokemon.timesAttacked > 1 ? 's' : ''}` :
|
||||||
: undefined);
|
undefined);
|
||||||
}
|
}
|
||||||
if (!value.value) return value;
|
if (!value.value) return value;
|
||||||
|
|
||||||
|
|
@ -2236,16 +2243,16 @@ export class BattleTooltips {
|
||||||
if (this.battle.tier.includes('Super Staff Bros')) {
|
if (this.battle.tier.includes('Super Staff Bros')) {
|
||||||
if (move.id === 'bodycount') {
|
if (move.id === 'bodycount') {
|
||||||
value.set(50 + 50 * pokemon.side.faintCounter,
|
value.set(50 + 50 * pokemon.side.faintCounter,
|
||||||
pokemon.side.faintCounter > 0
|
pokemon.side.faintCounter > 0 ?
|
||||||
? `${pokemon.side.faintCounter} teammate${pokemon.side.faintCounter > 1 ? 's' : ''} KOed`
|
`${pokemon.side.faintCounter} teammate${pokemon.side.faintCounter > 1 ? 's' : ''} KOed` :
|
||||||
: undefined);
|
undefined);
|
||||||
}
|
}
|
||||||
// Base power based on times hit
|
// Base power based on times hit
|
||||||
if (move.id === 'vengefulmood') {
|
if (move.id === 'vengefulmood') {
|
||||||
value.set(Math.min(140, 60 + 20 * pokemon.timesAttacked),
|
value.set(Math.min(140, 60 + 20 * pokemon.timesAttacked),
|
||||||
pokemon.timesAttacked > 0
|
pokemon.timesAttacked > 0 ?
|
||||||
? `Hit ${pokemon.timesAttacked} time${pokemon.timesAttacked > 1 ? 's' : ''}`
|
`Hit ${pokemon.timesAttacked} time${pokemon.timesAttacked > 1 ? 's' : ''}` :
|
||||||
: undefined);
|
undefined);
|
||||||
}
|
}
|
||||||
if (move.id === 'alting' && pokemon.shiny) {
|
if (move.id === 'alting' && pokemon.shiny) {
|
||||||
value.set(69, 'Shiny');
|
value.set(69, 'Shiny');
|
||||||
|
|
@ -2305,14 +2312,14 @@ export class BattleTooltips {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static incenseTypes: {[itemName: string]: Dex.TypeName} = {
|
static incenseTypes: { [itemName: string]: Dex.TypeName } = {
|
||||||
'Odd Incense': 'Psychic',
|
'Odd Incense': 'Psychic',
|
||||||
'Rock Incense': 'Rock',
|
'Rock Incense': 'Rock',
|
||||||
'Rose Incense': 'Grass',
|
'Rose Incense': 'Grass',
|
||||||
'Sea Incense': 'Water',
|
'Sea Incense': 'Water',
|
||||||
'Wave Incense': 'Water',
|
'Wave Incense': 'Water',
|
||||||
};
|
};
|
||||||
static itemTypes: {[itemName: string]: Dex.TypeName} = {
|
static itemTypes: { [itemName: string]: Dex.TypeName } = {
|
||||||
'Black Belt': 'Fighting',
|
'Black Belt': 'Fighting',
|
||||||
'Black Glasses': 'Dark',
|
'Black Glasses': 'Dark',
|
||||||
'Charcoal': 'Fire',
|
'Charcoal': 'Fire',
|
||||||
|
|
@ -2332,7 +2339,7 @@ export class BattleTooltips {
|
||||||
'Spell Tag': 'Ghost',
|
'Spell Tag': 'Ghost',
|
||||||
'Twisted Spoon': 'Psychic',
|
'Twisted Spoon': 'Psychic',
|
||||||
};
|
};
|
||||||
static orbUsers: {[speciesForme: string]: string[]} = {
|
static orbUsers: { [speciesForme: string]: string[] } = {
|
||||||
'Latias': ['Soul Dew'],
|
'Latias': ['Soul Dew'],
|
||||||
'Latios': ['Soul Dew'],
|
'Latios': ['Soul Dew'],
|
||||||
'Dialga': ['Adamant Crystal', 'Adamant Orb'],
|
'Dialga': ['Adamant Crystal', 'Adamant Orb'],
|
||||||
|
|
@ -2340,7 +2347,7 @@ export class BattleTooltips {
|
||||||
'Giratina': ['Griseous Core', 'Griseous Orb'],
|
'Giratina': ['Griseous Core', 'Griseous Orb'],
|
||||||
'Venomicon': ['Vile Vial'],
|
'Venomicon': ['Vile Vial'],
|
||||||
};
|
};
|
||||||
static orbTypes: {[itemName: string]: Dex.TypeName[]} = {
|
static orbTypes: { [itemName: string]: Dex.TypeName[] } = {
|
||||||
'Soul Dew': ['Psychic', 'Dragon'],
|
'Soul Dew': ['Psychic', 'Dragon'],
|
||||||
'Adamant Crystal': ['Steel', 'Dragon'],
|
'Adamant Crystal': ['Steel', 'Dragon'],
|
||||||
'Adamant Orb': ['Steel', 'Dragon'],
|
'Adamant Orb': ['Steel', 'Dragon'],
|
||||||
|
|
@ -2399,12 +2406,14 @@ export class BattleTooltips {
|
||||||
}
|
}
|
||||||
if (speciesName === 'Ogerpon') {
|
if (speciesName === 'Ogerpon') {
|
||||||
const speciesForme = value.pokemon.getSpeciesForme();
|
const speciesForme = value.pokemon.getSpeciesForme();
|
||||||
if ((speciesForme.startsWith('Ogerpon-Wellspring') && itemName === 'Wellspring Mask') ||
|
if (
|
||||||
|
(speciesForme.startsWith('Ogerpon-Wellspring') && itemName === 'Wellspring Mask') ||
|
||||||
(speciesForme.startsWith('Ogerpon-Hearthflame') && itemName === 'Hearthflame Mask') ||
|
(speciesForme.startsWith('Ogerpon-Hearthflame') && itemName === 'Hearthflame Mask') ||
|
||||||
(speciesForme.startsWith('Ogerpon-Cornerstone') && itemName === 'Cornerstone Mask')) {
|
(speciesForme.startsWith('Ogerpon-Cornerstone') && itemName === 'Cornerstone Mask')
|
||||||
value.itemModify(1.2);
|
) {
|
||||||
return value;
|
value.itemModify(1.2);
|
||||||
}
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gems
|
// Gems
|
||||||
|
|
@ -2422,14 +2431,14 @@ export class BattleTooltips {
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
getPokemonTypes(pokemon: Pokemon | ServerPokemon, preterastallized = false): ReadonlyArray<Dex.TypeName> {
|
getPokemonTypes(pokemon: Pokemon | ServerPokemon, preterastallized = false): readonly Dex.TypeName[] {
|
||||||
if (!(pokemon as Pokemon).getTypes) {
|
if (!(pokemon as Pokemon).getTypes) {
|
||||||
return this.battle.dex.species.get(pokemon.speciesForme).types;
|
return this.battle.dex.species.get(pokemon.speciesForme).types;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (pokemon as Pokemon).getTypeList(undefined, preterastallized);
|
return (pokemon as Pokemon).getTypeList(undefined, preterastallized);
|
||||||
}
|
}
|
||||||
pokemonHasType(pokemon: Pokemon | ServerPokemon, type: Dex.TypeName, types?: ReadonlyArray<Dex.TypeName>) {
|
pokemonHasType(pokemon: Pokemon | ServerPokemon, type: Dex.TypeName, types?: readonly Dex.TypeName[]) {
|
||||||
if (!types) types = this.getPokemonTypes(pokemon);
|
if (!types) types = this.getPokemonTypes(pokemon);
|
||||||
for (const curType of types) {
|
for (const curType of types) {
|
||||||
if (curType === type) return true;
|
if (curType === type) return true;
|
||||||
|
|
@ -2446,7 +2455,7 @@ export class BattleTooltips {
|
||||||
return ally.effectiveAbility(serverPokemon);
|
return ally.effectiveAbility(serverPokemon);
|
||||||
}
|
}
|
||||||
getPokemonAbilityData(clientPokemon: Pokemon | null, serverPokemon: ServerPokemon | null | undefined) {
|
getPokemonAbilityData(clientPokemon: Pokemon | null, serverPokemon: ServerPokemon | null | undefined) {
|
||||||
const abilityData: {ability: string, baseAbility: string, possibilities: string[]} = {
|
const abilityData: { ability: string, baseAbility: string, possibilities: string[] } = {
|
||||||
ability: '', baseAbility: '', possibilities: [],
|
ability: '', baseAbility: '', possibilities: [],
|
||||||
};
|
};
|
||||||
if (clientPokemon) {
|
if (clientPokemon) {
|
||||||
|
|
@ -2459,16 +2468,14 @@ export class BattleTooltips {
|
||||||
const speciesForme = clientPokemon.getSpeciesForme() || serverPokemon?.speciesForme || '';
|
const speciesForme = clientPokemon.getSpeciesForme() || serverPokemon?.speciesForme || '';
|
||||||
const species = this.battle.dex.species.get(speciesForme);
|
const species = this.battle.dex.species.get(speciesForme);
|
||||||
if (species.exists && species.abilities) {
|
if (species.exists && species.abilities) {
|
||||||
abilityData.possibilities = [species.abilities['0']];
|
abilityData.possibilities = Object.values(species.abilities);
|
||||||
if (species.abilities['1']) abilityData.possibilities.push(species.abilities['1']);
|
|
||||||
if (species.abilities['H']) abilityData.possibilities.push(species.abilities['H']);
|
|
||||||
if (species.abilities['S']) abilityData.possibilities.push(species.abilities['S']);
|
|
||||||
if (this.battle.rules['Frantic Fusions Mod']) {
|
if (this.battle.rules['Frantic Fusions Mod']) {
|
||||||
const fusionSpecies = this.battle.dex.species.get(clientPokemon.name);
|
const fusionSpecies = this.battle.dex.species.get(clientPokemon.name);
|
||||||
if (fusionSpecies.exists && fusionSpecies.name !== species.name) {
|
if (fusionSpecies.exists && fusionSpecies.name !== species.name) {
|
||||||
abilityData.possibilities = Array.from(
|
for (const newAbility of Object.values(fusionSpecies.abilities)) {
|
||||||
new Set(abilityData.possibilities.concat(Object.values(fusionSpecies.abilities)))
|
if (abilityData.possibilities.includes(newAbility)) continue;
|
||||||
);
|
abilityData.possibilities.push(newAbility);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2537,13 +2544,13 @@ class BattleStatGuesser {
|
||||||
guess(set: Dex.PokemonSet) {
|
guess(set: Dex.PokemonSet) {
|
||||||
let role = this.guessRole(set);
|
let role = this.guessRole(set);
|
||||||
let comboEVs = this.guessEVs(set, role);
|
let comboEVs = this.guessEVs(set, role);
|
||||||
let evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
let evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
||||||
for (let stat in evs) {
|
for (let stat in evs) {
|
||||||
evs[stat as Dex.StatName] = comboEVs[stat as Dex.StatName] || 0;
|
evs[stat as Dex.StatName] = comboEVs[stat as Dex.StatName] || 0;
|
||||||
}
|
}
|
||||||
let plusStat = comboEVs.plusStat || '';
|
let plusStat = comboEVs.plusStat || '';
|
||||||
let minusStat = comboEVs.minusStat || '';
|
let minusStat = comboEVs.minusStat || '';
|
||||||
return {role, evs, plusStat, minusStat, moveCount: this.moveCount, hasMove: this.hasMove};
|
return { role, evs, plusStat, minusStat, moveCount: this.moveCount, hasMove: this.hasMove };
|
||||||
}
|
}
|
||||||
guessRole(set: Dex.PokemonSet) {
|
guessRole(set: Dex.PokemonSet) {
|
||||||
if (!set) return '?';
|
if (!set) return '?';
|
||||||
|
|
@ -2569,7 +2576,7 @@ class BattleStatGuesser {
|
||||||
'specialBulk': 0,
|
'specialBulk': 0,
|
||||||
'physicalBulk': 0,
|
'physicalBulk': 0,
|
||||||
};
|
};
|
||||||
let hasMove: {[moveid: string]: 1} = {};
|
let hasMove: { [moveid: string]: 1 } = {};
|
||||||
let itemid = toID(set.item);
|
let itemid = toID(set.item);
|
||||||
let item = this.dex.items.get(itemid);
|
let item = this.dex.items.get(itemid);
|
||||||
let abilityid = toID(set.ability);
|
let abilityid = toID(set.ability);
|
||||||
|
|
@ -2835,7 +2842,7 @@ class BattleStatGuesser {
|
||||||
diff -= change;
|
diff -= change;
|
||||||
}
|
}
|
||||||
if (diff <= 0) return evTotal;
|
if (diff <= 0) return evTotal;
|
||||||
let evPriority = {def: 1, spd: 1, hp: 1, atk: 1, spa: 1, spe: 1};
|
let evPriority = { def: 1, spd: 1, hp: 1, atk: 1, spa: 1, spe: 1 };
|
||||||
let prioStat: Dex.StatName;
|
let prioStat: Dex.StatName;
|
||||||
for (prioStat in evPriority) {
|
for (prioStat in evPriority) {
|
||||||
if (prioStat === stat) continue;
|
if (prioStat === stat) continue;
|
||||||
|
|
@ -2857,7 +2864,7 @@ class BattleStatGuesser {
|
||||||
}
|
}
|
||||||
guessEVs(
|
guessEVs(
|
||||||
set: Dex.PokemonSet, role: string
|
set: Dex.PokemonSet, role: string
|
||||||
): Partial<Dex.StatsTable> & {plusStat?: Dex.StatName | '', minusStat?: Dex.StatName | ''} {
|
): Partial<Dex.StatsTable> & { plusStat?: Dex.StatName | '', minusStat?: Dex.StatName | '' } {
|
||||||
if (!set) return {};
|
if (!set) return {};
|
||||||
if (role === '?') return {};
|
if (role === '?') return {};
|
||||||
let species = this.dex.species.get(set.species || set.name!);
|
let species = this.dex.species.get(set.species || set.name!);
|
||||||
|
|
@ -2866,13 +2873,13 @@ class BattleStatGuesser {
|
||||||
let hasMove = this.hasMove;
|
let hasMove = this.hasMove;
|
||||||
let moveCount = this.moveCount;
|
let moveCount = this.moveCount;
|
||||||
|
|
||||||
let evs: Dex.StatsTable & {plusStat?: Dex.StatName | '', minusStat?: Dex.StatName | ''} = {
|
let evs: Dex.StatsTable & { plusStat?: Dex.StatName | '', minusStat?: Dex.StatName | '' } = {
|
||||||
hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0,
|
hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0,
|
||||||
};
|
};
|
||||||
let plusStat: Dex.StatName | '' = '';
|
let plusStat: Dex.StatName | '' = '';
|
||||||
let minusStat: Dex.StatName | '' = '';
|
let minusStat: Dex.StatName | '' = '';
|
||||||
|
|
||||||
let statChart: {[role: string]: [Dex.StatName, Dex.StatName]} = {
|
let statChart: { [role: string]: [Dex.StatName, Dex.StatName] } = {
|
||||||
'Bulky Band': ['atk', 'hp'],
|
'Bulky Band': ['atk', 'hp'],
|
||||||
'Fast Band': ['spe', 'atk'],
|
'Fast Band': ['spe', 'atk'],
|
||||||
'Bulky Specs': ['spa', 'hp'],
|
'Bulky Specs': ['spa', 'hp'],
|
||||||
|
|
@ -2906,7 +2913,7 @@ class BattleStatGuesser {
|
||||||
|
|
||||||
if (this.supportsAVs) {
|
if (this.supportsAVs) {
|
||||||
// Let's Go, AVs enabled
|
// Let's Go, AVs enabled
|
||||||
evs = {hp: 200, atk: 200, def: 200, spa: 200, spd: 200, spe: 200};
|
evs = { hp: 200, atk: 200, def: 200, spa: 200, spd: 200, spe: 200 };
|
||||||
if (!moveCount['PhysicalAttack']) evs.atk = 0;
|
if (!moveCount['PhysicalAttack']) evs.atk = 0;
|
||||||
if (!moveCount['SpecialAttack']) evs.spa = 0;
|
if (!moveCount['SpecialAttack']) evs.spa = 0;
|
||||||
if (hasMove['gyroball'] || hasMove['trickroom']) evs.spe = 0;
|
if (hasMove['gyroball'] || hasMove['trickroom']) evs.spe = 0;
|
||||||
|
|
@ -2915,7 +2922,7 @@ class BattleStatGuesser {
|
||||||
// no change
|
// no change
|
||||||
} else if (this.ignoreEVLimits) {
|
} else if (this.ignoreEVLimits) {
|
||||||
// Gen 1-2, hackable EVs (like Hackmons)
|
// Gen 1-2, hackable EVs (like Hackmons)
|
||||||
evs = {hp: 252, atk: 252, def: 252, spa: 252, spd: 252, spe: 252};
|
evs = { hp: 252, atk: 252, def: 252, spa: 252, spd: 252, spe: 252 };
|
||||||
if (!moveCount['PhysicalAttack']) evs.atk = 0;
|
if (!moveCount['PhysicalAttack']) evs.atk = 0;
|
||||||
if (!moveCount['SpecialAttack'] && this.dex.gen > 1) evs.spa = 0;
|
if (!moveCount['SpecialAttack'] && this.dex.gen > 1) evs.spa = 0;
|
||||||
if (hasMove['gyroball'] || hasMove['trickroom']) evs.spe = 0;
|
if (hasMove['gyroball'] || hasMove['trickroom']) evs.spe = 0;
|
||||||
|
|
@ -2946,14 +2953,14 @@ class BattleStatGuesser {
|
||||||
let SRresistances = ['Ground', 'Steel', 'Fighting'];
|
let SRresistances = ['Ground', 'Steel', 'Fighting'];
|
||||||
let SRweak = 0;
|
let SRweak = 0;
|
||||||
if (set.ability !== 'Magic Guard' && set.ability !== 'Mountaineer') {
|
if (set.ability !== 'Magic Guard' && set.ability !== 'Mountaineer') {
|
||||||
if (SRweaknesses.indexOf(species.types[0]) >= 0) {
|
if (SRweaknesses.includes(species.types[0])) {
|
||||||
SRweak++;
|
SRweak++;
|
||||||
} else if (SRresistances.indexOf(species.types[0]) >= 0) {
|
} else if (SRresistances.includes(species.types[0])) {
|
||||||
SRweak--;
|
SRweak--;
|
||||||
}
|
}
|
||||||
if (SRweaknesses.indexOf(species.types[1]) >= 0) {
|
if (SRweaknesses.includes(species.types[1])) {
|
||||||
SRweak++;
|
SRweak++;
|
||||||
} else if (SRresistances.indexOf(species.types[1]) >= 0) {
|
} else if (SRresistances.includes(species.types[1])) {
|
||||||
SRweak--;
|
SRweak--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2965,10 +2972,10 @@ class BattleStatGuesser {
|
||||||
hpDivisibility = 4;
|
hpDivisibility = 4;
|
||||||
} else if (set.item === 'Leftovers' || set.item === 'Black Sludge') {
|
} else if (set.item === 'Leftovers' || set.item === 'Black Sludge') {
|
||||||
hpDivisibility = 0;
|
hpDivisibility = 0;
|
||||||
} else if (hasMove['bellydrum'] && (set.item || '').slice(-5) === 'Berry') {
|
} else if (hasMove['bellydrum'] && (set.item || '').endsWith('Berry')) {
|
||||||
hpDivisibility = 2;
|
hpDivisibility = 2;
|
||||||
hpShouldBeDivisible = true;
|
hpShouldBeDivisible = true;
|
||||||
} else if (hasMove['substitute'] && (set.item || '').slice(-5) === 'Berry') {
|
} else if (hasMove['substitute'] && (set.item || '').endsWith('Berry')) {
|
||||||
hpDivisibility = 4;
|
hpDivisibility = 4;
|
||||||
hpShouldBeDivisible = true;
|
hpShouldBeDivisible = true;
|
||||||
} else if (SRweak >= 2 || hasMove['bellydrum']) {
|
} else if (SRweak >= 2 || hasMove['bellydrum']) {
|
||||||
|
|
@ -3044,7 +3051,6 @@ class BattleStatGuesser {
|
||||||
if (ev) evs['spe'] = ev;
|
if (ev) evs['spe'] = ev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasMove['gyroball'] || hasMove['trickroom']) {
|
if (hasMove['gyroball'] || hasMove['trickroom']) {
|
||||||
|
|
@ -3085,11 +3091,11 @@ class BattleStatGuesser {
|
||||||
|
|
||||||
let baseStat = species.baseStats[stat];
|
let baseStat = species.baseStats[stat];
|
||||||
|
|
||||||
let iv = (set.ivs && set.ivs[stat]);
|
let iv = set.ivs?.[stat];
|
||||||
if (typeof iv !== 'number') iv = 31;
|
if (typeof iv !== 'number') iv = 31;
|
||||||
if (this.dex.gen <= 2) iv &= 30;
|
if (this.dex.gen <= 2) iv &= 30;
|
||||||
|
|
||||||
let ev = (set.evs && set.evs[stat]);
|
let ev = set.evs?.[stat];
|
||||||
if (typeof ev !== 'number') ev = (this.dex.gen > 2 ? 0 : 252);
|
if (typeof ev !== 'number') ev = (this.dex.gen > 2 ? 0 : 252);
|
||||||
if (evOverride !== undefined) ev = evOverride;
|
if (evOverride !== undefined) ev = evOverride;
|
||||||
|
|
||||||
|
|
@ -3160,7 +3166,7 @@ function BattleStatOptimizer(set: Dex.PokemonSet, formatid: ID) {
|
||||||
return ev;
|
return ev;
|
||||||
};
|
};
|
||||||
|
|
||||||
const origSpread = {evs: set.evs, ...origNature};
|
const origSpread = { evs: set.evs, ...origNature };
|
||||||
let origLeftoverEVs = 508;
|
let origLeftoverEVs = 508;
|
||||||
for (const stat of Dex.statNames) {
|
for (const stat of Dex.statNames) {
|
||||||
origLeftoverEVs -= origSpread.evs?.[stat] || 0;
|
origLeftoverEVs -= origSpread.evs?.[stat] || 0;
|
||||||
|
|
@ -3185,12 +3191,12 @@ function BattleStatOptimizer(set: Dex.PokemonSet, formatid: ID) {
|
||||||
if (!minusTooLow) {
|
if (!minusTooLow) {
|
||||||
for (const stat of Dex.statNamesExceptHP) {
|
for (const stat of Dex.statNamesExceptHP) {
|
||||||
if (origStats[stat] < origStats[bestMinus]) {
|
if (origStats[stat] < origStats[bestMinus]) {
|
||||||
const minEVs = getMinEVs(stat, {minus: stat});
|
const minEVs = getMinEVs(stat, { minus: stat });
|
||||||
if (minEVs > 252) continue;
|
if (minEVs > 252) continue;
|
||||||
// This number can go negative at this point, but we'll make up for it later (and check to make sure)
|
// This number can go negative at this point, but we'll make up for it later (and check to make sure)
|
||||||
savedEVs = (origSpread.evs[stat] || 0) - minEVs;
|
savedEVs = (origSpread.evs[stat] || 0) - minEVs;
|
||||||
if (origNature.minus) {
|
if (origNature.minus) {
|
||||||
savedEVs += (origSpread.evs[origNature.minus] || 0) - getMinEVs(origNature.minus, {minus: stat});
|
savedEVs += (origSpread.evs[origNature.minus] || 0) - getMinEVs(origNature.minus, { minus: stat });
|
||||||
}
|
}
|
||||||
bestMinus = stat;
|
bestMinus = stat;
|
||||||
bestMinusMinEVs = minEVs;
|
bestMinusMinEVs = minEVs;
|
||||||
|
|
@ -3201,17 +3207,17 @@ function BattleStatOptimizer(set: Dex.PokemonSet, formatid: ID) {
|
||||||
for (const stat of Dex.statNamesExceptHP) {
|
for (const stat of Dex.statNamesExceptHP) {
|
||||||
// Don't move the plus to an uninvested stat
|
// Don't move the plus to an uninvested stat
|
||||||
if (stat !== origNature.plus && origSpread.evs[stat] && stat !== bestMinus) {
|
if (stat !== origNature.plus && origSpread.evs[stat] && stat !== bestMinus) {
|
||||||
const minEVs = getMinEVs(stat, {plus: stat});
|
const minEVs = getMinEVs(stat, { plus: stat });
|
||||||
let plusEVsSaved = (origNature.minus === stat ? getMinEVs(stat, {}) : origSpread.evs[stat] || 0) - minEVs;
|
let plusEVsSaved = (origNature.minus === stat ? getMinEVs(stat, {}) : origSpread.evs[stat] || 0) - minEVs;
|
||||||
if (bestPlus && bestPlus !== bestMinus) {
|
if (bestPlus && bestPlus !== bestMinus) {
|
||||||
plusEVsSaved += bestPlusMinEVs! - getMinEVs(bestPlus, {plus: stat, minus: bestMinus});
|
plusEVsSaved += bestPlusMinEVs! - getMinEVs(bestPlus, { plus: stat, minus: bestMinus });
|
||||||
}
|
}
|
||||||
if (plusEVsSaved > 0 && savedEVs + plusEVsSaved > 0) {
|
if (plusEVsSaved > 0 && savedEVs + plusEVsSaved > 0) {
|
||||||
savedEVs += plusEVsSaved;
|
savedEVs += plusEVsSaved;
|
||||||
bestPlus = stat;
|
bestPlus = stat;
|
||||||
bestPlusMinEVs = minEVs;
|
bestPlusMinEVs = minEVs;
|
||||||
} else if (plusEVsSaved === 0 && (bestPlus || savedEVs > 0) || plusEVsSaved > 0 && savedEVs + plusEVsSaved === 0) {
|
} else if (plusEVsSaved === 0 && (bestPlus || savedEVs > 0) || plusEVsSaved > 0 && savedEVs + plusEVsSaved === 0) {
|
||||||
if (!bestPlus || getStat(stat, getMinEVs(stat, {plus: stat}), {plus: stat}) > origStats[stat]) {
|
if (!bestPlus || getStat(stat, getMinEVs(stat, { plus: stat }), { plus: stat }) > origStats[stat]) {
|
||||||
savedEVs += plusEVsSaved;
|
savedEVs += plusEVsSaved;
|
||||||
bestPlus = stat;
|
bestPlus = stat;
|
||||||
bestPlusMinEVs = minEVs;
|
bestPlusMinEVs = minEVs;
|
||||||
|
|
@ -3226,7 +3232,7 @@ function BattleStatOptimizer(set: Dex.PokemonSet, formatid: ID) {
|
||||||
evs: Partial<Dex.StatsTable>,
|
evs: Partial<Dex.StatsTable>,
|
||||||
plus?: Dex.StatNameExceptHP,
|
plus?: Dex.StatNameExceptHP,
|
||||||
minus?: Dex.StatNameExceptHP,
|
minus?: Dex.StatNameExceptHP,
|
||||||
} = {evs: {...origSpread.evs}, plus: bestPlus, minus: bestMinus};
|
} = { evs: { ...origSpread.evs }, plus: bestPlus, minus: bestMinus };
|
||||||
if (bestPlus !== origNature.plus || bestMinus !== origNature.minus) {
|
if (bestPlus !== origNature.plus || bestMinus !== origNature.minus) {
|
||||||
newSpread.evs[bestPlus] = bestPlusMinEVs!;
|
newSpread.evs[bestPlus] = bestPlusMinEVs!;
|
||||||
newSpread.evs[bestMinus] = bestMinusMinEVs!;
|
newSpread.evs[bestMinus] = bestMinusMinEVs!;
|
||||||
|
|
@ -3239,7 +3245,7 @@ function BattleStatOptimizer(set: Dex.PokemonSet, formatid: ID) {
|
||||||
for (const stat of Dex.statNames) {
|
for (const stat of Dex.statNames) {
|
||||||
if (!newSpread.evs[stat]) delete newSpread.evs[stat];
|
if (!newSpread.evs[stat]) delete newSpread.evs[stat];
|
||||||
}
|
}
|
||||||
return {...newSpread, savedEVs};
|
return { ...newSpread, savedEVs };
|
||||||
} else if (!plusTooHigh && !minusTooLow) {
|
} else if (!plusTooHigh && !minusTooLow) {
|
||||||
if (Math.floor(getStat(bestPlus, bestMinusMinEVs!, newSpread) / 11) <= Math.ceil(origStats[bestMinus] / 9)) {
|
if (Math.floor(getStat(bestPlus, bestMinusMinEVs!, newSpread) / 11) <= Math.ceil(origStats[bestMinus] / 9)) {
|
||||||
// We're not gaining more points from our plus than we're losing to our minus
|
// We're not gaining more points from our plus than we're losing to our minus
|
||||||
|
|
@ -3254,7 +3260,7 @@ function BattleStatOptimizer(set: Dex.PokemonSet, formatid: ID) {
|
||||||
for (const stat of Dex.statNames) {
|
for (const stat of Dex.statNames) {
|
||||||
if (!newSpread.evs[stat]) delete newSpread.evs[stat];
|
if (!newSpread.evs[stat]) delete newSpread.evs[stat];
|
||||||
}
|
}
|
||||||
return {...newSpread, savedEVs};
|
return { ...newSpread, savedEVs };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3266,6 +3272,6 @@ declare const require: any;
|
||||||
declare const global: any;
|
declare const global: any;
|
||||||
if (typeof require === 'function') {
|
if (typeof require === 'function') {
|
||||||
// in Node
|
// in Node
|
||||||
(global as any).BattleStatGuesser = BattleStatGuesser;
|
global.BattleStatGuesser = BattleStatGuesser;
|
||||||
(global as any).BattleStatOptimizer = BattleStatOptimizer;
|
global.BattleStatOptimizer = BattleStatOptimizer;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,16 +28,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// import $ from 'jquery';
|
// import $ from 'jquery';
|
||||||
import {BattleSceneStub} from './battle-scene-stub';
|
import { BattleSceneStub } from './battle-scene-stub';
|
||||||
import {BattleLog} from './battle-log';
|
import { BattleLog } from './battle-log';
|
||||||
import {BattleScene, PokemonSprite, BattleStatusAnims} from './battle-animations';
|
import { BattleScene, type PokemonSprite, BattleStatusAnims } from './battle-animations';
|
||||||
import {Dex, Teams, toID, toUserid, type ID, type ModdedDex} from './battle-dex';
|
import { Dex, Teams, toID, toUserid, type ID, type ModdedDex } from './battle-dex';
|
||||||
import {BattleTextParser, type Args, type KWArgs, type SideID} from './battle-text-parser';
|
import { BattleTextParser, type Args, type KWArgs, type SideID } from './battle-text-parser';
|
||||||
|
declare const app: { user: AnyObject, rooms: AnyObject, ignore?: AnyObject } | undefined;
|
||||||
|
|
||||||
/** [id, element?, ...misc] */
|
/** [id, element?, ...misc] */
|
||||||
export type EffectState = any[] & {0: ID};
|
export type EffectState = any[] & { 0: ID };
|
||||||
/** [name, minTimeLeft, maxTimeLeft] */
|
export type WeatherState = [name: string, minTimeLeft: number, maxTimeLeft: number];
|
||||||
export type WeatherState = [string, number, number];
|
|
||||||
export type HPColor = 'r' | 'y' | 'g';
|
export type HPColor = 'r' | 'y' | 'g';
|
||||||
|
|
||||||
export class Pokemon implements PokemonDetails, PokemonHealth {
|
export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
|
|
@ -93,20 +93,20 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
itemEffect = '';
|
itemEffect = '';
|
||||||
prevItem = '';
|
prevItem = '';
|
||||||
prevItemEffect = '';
|
prevItemEffect = '';
|
||||||
terastallized: string | '' = '';
|
terastallized = '';
|
||||||
teraType = '';
|
teraType = '';
|
||||||
|
|
||||||
boosts: {[stat: string]: number} = {};
|
boosts: { [stat: string]: number } = {};
|
||||||
status: Dex.StatusName | 'tox' | '' | '???' = '';
|
status: Dex.StatusName | 'tox' | '' | '???' = '';
|
||||||
statusStage = 0;
|
statusStage = 0;
|
||||||
volatiles: {[effectid: string]: EffectState} = {};
|
volatiles: { [effectid: string]: EffectState } = {};
|
||||||
turnstatuses: {[effectid: string]: EffectState} = {};
|
turnstatuses: { [effectid: string]: EffectState } = {};
|
||||||
movestatuses: {[effectid: string]: EffectState} = {};
|
movestatuses: { [effectid: string]: EffectState } = {};
|
||||||
lastMove = '';
|
lastMove = '';
|
||||||
|
|
||||||
/** [[moveName, ppUsed]] */
|
/** [[moveName, ppUsed]] */
|
||||||
moveTrack: [string, number][] = [];
|
moveTrack: [string, number][] = [];
|
||||||
statusData = {sleepTurns: 0, toxicTurns: 0};
|
statusData = { sleepTurns: 0, toxicTurns: 0 };
|
||||||
timesAttacked = 0;
|
timesAttacked = 0;
|
||||||
|
|
||||||
sprite: PokemonSprite;
|
sprite: PokemonSprite;
|
||||||
|
|
@ -174,7 +174,7 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
if (range[0] === range[1]) {
|
if (range[0] === range[1]) {
|
||||||
let percentage = Math.abs(range[0] * 100);
|
let percentage = Math.abs(range[0] * 100);
|
||||||
if (Math.floor(percentage) === percentage) {
|
if (Math.floor(percentage) === percentage) {
|
||||||
return percentage + '%';
|
return `${percentage}%`;
|
||||||
}
|
}
|
||||||
return percentage.toFixed(precision) + '%';
|
return percentage.toFixed(precision) + '%';
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +187,7 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
lower = (range[0] * 100).toFixed(precision);
|
lower = (range[0] * 100).toFixed(precision);
|
||||||
upper = (range[1] * 100).toFixed(precision);
|
upper = (range[1] * 100).toFixed(precision);
|
||||||
}
|
}
|
||||||
return '' + lower + separator + upper + '%';
|
return `${lower}${separator}${upper}%`;
|
||||||
}
|
}
|
||||||
// Returns [min, max] damage dealt as a proportion of total HP from 0 to 1
|
// Returns [min, max] damage dealt as a proportion of total HP from 0 to 1
|
||||||
getDamageRange(damage: any): [number, number] {
|
getDamageRange(damage: any): [number, number] {
|
||||||
|
|
@ -216,7 +216,7 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
healthParse(hpstring: string, parsedamage?: boolean, heal?: boolean):
|
healthParse(hpstring: string, parsedamage?: boolean, heal?: boolean):
|
||||||
[number, number, number] | [number, number, number, number, HPColor] | null {
|
[number, number, number] | [number, number, number, number, HPColor] | null {
|
||||||
// returns [delta, denominator, percent(, oldnum, oldcolor)] or null
|
// returns [delta, denominator, percent(, oldnum, oldcolor)] or null
|
||||||
if (!hpstring || !hpstring.length) return null;
|
if (!hpstring?.length) return null;
|
||||||
let parenIndex = hpstring.lastIndexOf('(');
|
let parenIndex = hpstring.lastIndexOf('(');
|
||||||
if (parenIndex >= 0) {
|
if (parenIndex >= 0) {
|
||||||
// old style damage and health reporting
|
// old style damage and health reporting
|
||||||
|
|
@ -266,7 +266,7 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
if (!details) return false;
|
if (!details) return false;
|
||||||
if (details === this.details) return true;
|
if (details === this.details) return true;
|
||||||
if (this.searchid) return false;
|
if (this.searchid) return false;
|
||||||
if (details.indexOf(', shiny') >= 0) {
|
if (details.includes(', shiny')) {
|
||||||
if (this.checkDetails(details.replace(', shiny', ''))) return true;
|
if (this.checkDetails(details.replace(', shiny', ''))) return true;
|
||||||
}
|
}
|
||||||
// the actual forme was hidden on Team Preview
|
// the actual forme was hidden on Team Preview
|
||||||
|
|
@ -340,7 +340,7 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
rememberMove(moveName: string, pp = 1, recursionSource?: string) {
|
rememberMove(moveName: string, pp = 1, recursionSource?: string) {
|
||||||
if (recursionSource === this.ident) return;
|
if (recursionSource === this.ident) return;
|
||||||
moveName = Dex.moves.get(moveName).name;
|
moveName = Dex.moves.get(moveName).name;
|
||||||
if (moveName.charAt(0) === '*') return;
|
if (moveName.startsWith('*')) return;
|
||||||
if (moveName === 'Struggle') return;
|
if (moveName === 'Struggle') return;
|
||||||
if (this.volatiles.transform) {
|
if (this.volatiles.transform) {
|
||||||
// make sure there is no infinite recursion if both Pokemon are transformed into each other
|
// make sure there is no infinite recursion if both Pokemon are transformed into each other
|
||||||
|
|
@ -422,7 +422,7 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
this.boosts = {};
|
this.boosts = {};
|
||||||
this.clearVolatiles();
|
this.clearVolatiles();
|
||||||
for (let i = 0; i < this.moveTrack.length; i++) {
|
for (let i = 0; i < this.moveTrack.length; i++) {
|
||||||
if (this.moveTrack[i][0].charAt(0) === '*') {
|
if (this.moveTrack[i][0].startsWith('*')) {
|
||||||
this.moveTrack.splice(i, 1);
|
this.moveTrack.splice(i, 1);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
|
|
@ -477,8 +477,8 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
this.removeVolatile('typeadd' as ID);
|
this.removeVolatile('typeadd' as ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getTypes(serverPokemon?: ServerPokemon, preterastallized = false): [ReadonlyArray<Dex.TypeName>, Dex.TypeName | ''] {
|
getTypes(serverPokemon?: ServerPokemon, preterastallized = false): [readonly Dex.TypeName[], Dex.TypeName | ''] {
|
||||||
let types: ReadonlyArray<Dex.TypeName>;
|
let types: readonly Dex.TypeName[];
|
||||||
if (!preterastallized && this.terastallized && this.terastallized !== 'Stellar') {
|
if (!preterastallized && this.terastallized && this.terastallized !== 'Stellar') {
|
||||||
types = [this.terastallized as Dex.TypeName];
|
types = [this.terastallized as Dex.TypeName];
|
||||||
} else if (this.volatiles.typechange) {
|
} else if (this.volatiles.typechange) {
|
||||||
|
|
@ -588,8 +588,8 @@ export class Pokemon implements PokemonDetails, PokemonHealth {
|
||||||
return Pokemon.getHPText(this, this.side.battle.reportExactHP, precision);
|
return Pokemon.getHPText(this, this.side.battle.reportExactHP, precision);
|
||||||
}
|
}
|
||||||
static getHPText(pokemon: PokemonHealth, exactHP: boolean, precision = 1) {
|
static getHPText(pokemon: PokemonHealth, exactHP: boolean, precision = 1) {
|
||||||
if (exactHP) return pokemon.hp + '/' + pokemon.maxhp;
|
if (exactHP) return `${pokemon.hp}/${pokemon.maxhp}`;
|
||||||
if (pokemon.maxhp === 100) return pokemon.hp + '%';
|
if (pokemon.maxhp === 100) return `${pokemon.hp}%`;
|
||||||
if (pokemon.maxhp !== 48) return (100 * pokemon.hp / pokemon.maxhp).toFixed(precision) + '%';
|
if (pokemon.maxhp !== 48) return (100 * pokemon.hp / pokemon.maxhp).toFixed(precision) + '%';
|
||||||
let range = Pokemon.getPixelRange(pokemon.hp, pokemon.hpcolor);
|
let range = Pokemon.getPixelRange(pokemon.hp, pokemon.hpcolor);
|
||||||
return Pokemon.getFormattedRange(range, precision, '–');
|
return Pokemon.getFormattedRange(range, precision, '–');
|
||||||
|
|
@ -610,9 +610,9 @@ export class Side {
|
||||||
isFar: boolean;
|
isFar: boolean;
|
||||||
foe: Side = null!;
|
foe: Side = null!;
|
||||||
ally: Side | null = null;
|
ally: Side | null = null;
|
||||||
avatar: string = 'unknown';
|
avatar = 'unknown';
|
||||||
badges: string[] = [];
|
badges: string[] = [];
|
||||||
rating: string = '';
|
rating = '';
|
||||||
totalPokemon = 6;
|
totalPokemon = 6;
|
||||||
x = 0;
|
x = 0;
|
||||||
y = 0;
|
y = 0;
|
||||||
|
|
@ -625,8 +625,9 @@ export class Side {
|
||||||
lastPokemon = null as Pokemon | null;
|
lastPokemon = null as Pokemon | null;
|
||||||
pokemon = [] as Pokemon[];
|
pokemon = [] as Pokemon[];
|
||||||
|
|
||||||
/** [effectName, levels, minDuration, maxDuration] */
|
sideConditions: {
|
||||||
sideConditions: {[id: string]: [string, number, number, number]} = {};
|
[id: string]: [effectName: string, levels: number, minDuration: number, maxDuration: number],
|
||||||
|
} = {};
|
||||||
faintCounter = 0;
|
faintCounter = 0;
|
||||||
|
|
||||||
constructor(battle: Battle, n: number) {
|
constructor(battle: Battle, n: number) {
|
||||||
|
|
@ -765,7 +766,7 @@ export class Side {
|
||||||
}
|
}
|
||||||
if (this.pokemon.length > this.totalPokemon || this.battle.speciesClause) {
|
if (this.pokemon.length > this.totalPokemon || this.battle.speciesClause) {
|
||||||
// check for Illusion
|
// check for Illusion
|
||||||
let existingTable: {[searchid: string]: number} = {};
|
let existingTable: { [searchid: string]: number } = {};
|
||||||
let toRemove = -1;
|
let toRemove = -1;
|
||||||
for (let poke1i = 0; poke1i < this.pokemon.length; poke1i++) {
|
for (let poke1i = 0; poke1i < this.pokemon.length; poke1i++) {
|
||||||
let poke1 = this.pokemon[poke1i];
|
let poke1 = this.pokemon[poke1i];
|
||||||
|
|
@ -777,9 +778,9 @@ export class Side {
|
||||||
toRemove = poke2i;
|
toRemove = poke2i;
|
||||||
} else if (poke === poke2) {
|
} else if (poke === poke2) {
|
||||||
toRemove = poke1i;
|
toRemove = poke1i;
|
||||||
} else if (this.active.indexOf(poke1) >= 0) {
|
} else if (this.active.includes(poke1)) {
|
||||||
toRemove = poke2i;
|
toRemove = poke2i;
|
||||||
} else if (this.active.indexOf(poke2) >= 0) {
|
} else if (this.active.includes(poke2)) {
|
||||||
toRemove = poke1i;
|
toRemove = poke1i;
|
||||||
} else if (poke1.fainted && !poke2.fainted) {
|
} else if (poke1.fainted && !poke2.fainted) {
|
||||||
toRemove = poke2i;
|
toRemove = poke2i;
|
||||||
|
|
@ -797,7 +798,7 @@ export class Side {
|
||||||
for (const curPoke of this.pokemon) {
|
for (const curPoke of this.pokemon) {
|
||||||
if (curPoke === poke) continue;
|
if (curPoke === poke) continue;
|
||||||
if (curPoke.fainted) continue;
|
if (curPoke.fainted) continue;
|
||||||
if (this.active.indexOf(curPoke) >= 0) continue;
|
if (this.active.includes(curPoke)) continue;
|
||||||
if (curPoke.speciesForme === 'Zoroark' || curPoke.speciesForme === 'Zorua' || curPoke.ability === 'Illusion') {
|
if (curPoke.speciesForme === 'Zoroark' || curPoke.speciesForme === 'Zorua' || curPoke.ability === 'Illusion') {
|
||||||
illusionFound = curPoke;
|
illusionFound = curPoke;
|
||||||
break;
|
break;
|
||||||
|
|
@ -811,7 +812,7 @@ export class Side {
|
||||||
for (const curPoke of this.pokemon) {
|
for (const curPoke of this.pokemon) {
|
||||||
if (curPoke === poke) continue;
|
if (curPoke === poke) continue;
|
||||||
if (curPoke.fainted) continue;
|
if (curPoke.fainted) continue;
|
||||||
if (this.active.indexOf(curPoke) >= 0) continue;
|
if (this.active.includes(curPoke)) continue;
|
||||||
illusionFound = curPoke;
|
illusionFound = curPoke;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -871,7 +872,7 @@ export class Side {
|
||||||
pokemon.hpcolor = oldpokemon.hpcolor;
|
pokemon.hpcolor = oldpokemon.hpcolor;
|
||||||
pokemon.status = oldpokemon.status;
|
pokemon.status = oldpokemon.status;
|
||||||
pokemon.copyVolatileFrom(oldpokemon, true);
|
pokemon.copyVolatileFrom(oldpokemon, true);
|
||||||
pokemon.statusData = {...oldpokemon.statusData};
|
pokemon.statusData = { ...oldpokemon.statusData };
|
||||||
if (oldpokemon.terastallized) {
|
if (oldpokemon.terastallized) {
|
||||||
pokemon.terastallized = oldpokemon.terastallized;
|
pokemon.terastallized = oldpokemon.terastallized;
|
||||||
pokemon.teraType = oldpokemon.terastallized;
|
pokemon.teraType = oldpokemon.terastallized;
|
||||||
|
|
@ -901,7 +902,7 @@ export class Side {
|
||||||
pokemon.removeVolatile('formechange' as ID);
|
pokemon.removeVolatile('formechange' as ID);
|
||||||
}
|
}
|
||||||
if (!['batonpass', 'zbatonpass', 'shedtail', 'teleport'].includes(effect.id)) {
|
if (!['batonpass', 'zbatonpass', 'shedtail', 'teleport'].includes(effect.id)) {
|
||||||
this.battle.log(['switchout', pokemon.ident], {from: effect.id});
|
this.battle.log(['switchout', pokemon.ident], { from: effect.id });
|
||||||
}
|
}
|
||||||
pokemon.statusData.toxicTurns = 0;
|
pokemon.statusData.toxicTurns = 0;
|
||||||
if (this.battle.gen === 5) pokemon.statusData.sleepTurns = 0;
|
if (this.battle.gen === 5) pokemon.statusData.sleepTurns = 0;
|
||||||
|
|
@ -1099,7 +1100,7 @@ export class Battle {
|
||||||
gameType: 'singles' | 'doubles' | 'triples' | 'multi' | 'freeforall' = 'singles';
|
gameType: 'singles' | 'doubles' | 'triples' | 'multi' | 'freeforall' = 'singles';
|
||||||
compatMode = true;
|
compatMode = true;
|
||||||
rated: string | boolean = false;
|
rated: string | boolean = false;
|
||||||
rules: {[ruleName: string]: 1 | 0} = {};
|
rules: { [ruleName: string]: 1 | undefined } = {};
|
||||||
isBlitz = false;
|
isBlitz = false;
|
||||||
reportExactHP = false;
|
reportExactHP = false;
|
||||||
endLastTurnPending = false;
|
endLastTurnPending = false;
|
||||||
|
|
@ -1131,8 +1132,8 @@ export class Battle {
|
||||||
paused: boolean;
|
paused: boolean;
|
||||||
|
|
||||||
constructor(options: {
|
constructor(options: {
|
||||||
$frame?: JQuery<HTMLElement>,
|
$frame?: JQuery,
|
||||||
$logFrame?: JQuery<HTMLElement>,
|
$logFrame?: JQuery,
|
||||||
id?: ID,
|
id?: ID,
|
||||||
log?: string[] | string,
|
log?: string[] | string,
|
||||||
paused?: boolean,
|
paused?: boolean,
|
||||||
|
|
@ -1184,9 +1185,9 @@ export class Battle {
|
||||||
}
|
}
|
||||||
if (width && width < 640) {
|
if (width && width < 640) {
|
||||||
const scale = (width / 640);
|
const scale = (width / 640);
|
||||||
this.scene.$frame?.css('transform', 'scale(' + scale + ')');
|
this.scene.$frame?.css('transform', `scale(${scale})`);
|
||||||
this.scene.$frame?.css('transform-origin', 'top left');
|
this.scene.$frame?.css('transform-origin', 'top left');
|
||||||
this.scene.$frame?.css('margin-bottom', '' + (360 * scale - 360) + 'px');
|
this.scene.$frame?.css('margin-bottom', `${360 * scale - 360}px`);
|
||||||
// this.$foeHint.css('transform', 'scale(' + scale + ')');
|
// this.$foeHint.css('transform', 'scale(' + scale + ')');
|
||||||
} else {
|
} else {
|
||||||
this.scene.$frame?.css('transform', 'none');
|
this.scene.$frame?.css('transform', 'none');
|
||||||
|
|
@ -1433,25 +1434,24 @@ export class Battle {
|
||||||
if (this.gameType === 'freeforall') {
|
if (this.gameType === 'freeforall') {
|
||||||
// TODO: Add FFA support
|
// TODO: Add FFA support
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
let side1 = this.sides[0];
|
let side1 = this.sides[0];
|
||||||
let side2 = this.sides[1];
|
let side2 = this.sides[1];
|
||||||
for (const id of sideConditions) {
|
for (const id of sideConditions) {
|
||||||
if (side1.sideConditions[id] && side2.sideConditions[id]) {
|
if (side1.sideConditions[id] && side2.sideConditions[id]) {
|
||||||
[side1.sideConditions[id], side2.sideConditions[id]] = [
|
[side1.sideConditions[id], side2.sideConditions[id]] = [
|
||||||
side2.sideConditions[id], side1.sideConditions[id],
|
side2.sideConditions[id], side1.sideConditions[id],
|
||||||
];
|
];
|
||||||
this.scene.addSideCondition(side1.n, id as ID);
|
this.scene.addSideCondition(side1.n, id as ID);
|
||||||
this.scene.addSideCondition(side2.n, id as ID);
|
this.scene.addSideCondition(side2.n, id as ID);
|
||||||
} else if (side1.sideConditions[id] && !side2.sideConditions[id]) {
|
} else if (side1.sideConditions[id] && !side2.sideConditions[id]) {
|
||||||
side2.sideConditions[id] = side1.sideConditions[id];
|
side2.sideConditions[id] = side1.sideConditions[id];
|
||||||
this.scene.addSideCondition(side2.n, id as ID);
|
this.scene.addSideCondition(side2.n, id as ID);
|
||||||
side1.removeSideCondition(id);
|
side1.removeSideCondition(id);
|
||||||
} else if (side2.sideConditions[id] && !side1.sideConditions[id]) {
|
} else if (side2.sideConditions[id] && !side1.sideConditions[id]) {
|
||||||
side1.sideConditions[id] = side2.sideConditions[id];
|
side1.sideConditions[id] = side2.sideConditions[id];
|
||||||
this.scene.addSideCondition(side1.n, id as ID);
|
this.scene.addSideCondition(side1.n, id as ID);
|
||||||
side2.removeSideCondition(id);
|
side2.removeSideCondition(id);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1498,7 +1498,7 @@ export class Battle {
|
||||||
pokemon.item = move.isZ;
|
pokemon.item = move.isZ;
|
||||||
let item = Dex.items.get(move.isZ);
|
let item = Dex.items.get(move.isZ);
|
||||||
if (item.zMoveFrom) moveName = item.zMoveFrom;
|
if (item.zMoveFrom) moveName = item.zMoveFrom;
|
||||||
} else if (move.name.slice(0, 2) === 'Z-') {
|
} else if (move.name.startsWith('Z-')) {
|
||||||
moveName = moveName.slice(2);
|
moveName = moveName.slice(2);
|
||||||
move = Dex.moves.get(moveName);
|
move = Dex.moves.get(moveName);
|
||||||
if (window.BattleItems) {
|
if (window.BattleItems) {
|
||||||
|
|
@ -1733,8 +1733,7 @@ export class Battle {
|
||||||
}
|
}
|
||||||
let damageinfo = '' + Pokemon.getFormattedRange(range, damage[1] === 100 ? 0 : 1, '\u2013');
|
let damageinfo = '' + Pokemon.getFormattedRange(range, damage[1] === 100 ? 0 : 1, '\u2013');
|
||||||
if (damage[1] !== 100) {
|
if (damage[1] !== 100) {
|
||||||
let hover = '' + ((damage[0] < 0) ? '\u2212' : '') +
|
let hover = `${(damage[0] < 0) ? '\u2212' : ''}${Math.abs(damage[0])}/${damage[1]}`;
|
||||||
Math.abs(damage[0]) + '/' + damage[1];
|
|
||||||
if (damage[1] === 48) { // this is a hack
|
if (damage[1] === 48) { // this is a hack
|
||||||
hover += ' pixels';
|
hover += ' pixels';
|
||||||
}
|
}
|
||||||
|
|
@ -1780,7 +1779,7 @@ export class Battle {
|
||||||
break;
|
break;
|
||||||
case 'revivalblessing':
|
case 'revivalblessing':
|
||||||
this.scene.runResidualAnim('wish' as ID, poke);
|
this.scene.runResidualAnim('wish' as ID, poke);
|
||||||
const {siden} = this.parsePokemonId(args[1]);
|
const { siden } = this.parsePokemonId(args[1]);
|
||||||
const side = this.sides[siden];
|
const side = this.sides[siden];
|
||||||
poke.fainted = false;
|
poke.fainted = false;
|
||||||
poke.status = '';
|
poke.status = '';
|
||||||
|
|
@ -2216,7 +2215,6 @@ export class Battle {
|
||||||
}
|
}
|
||||||
this.log(args, kwArgs);
|
this.log(args, kwArgs);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
case '-cureteam': { // For old gens when the whole team was always cured
|
case '-cureteam': { // For old gens when the whole team was always cured
|
||||||
let poke = this.getPokemon(args[1])!;
|
let poke = this.getPokemon(args[1])!;
|
||||||
|
|
@ -2240,7 +2238,7 @@ export class Battle {
|
||||||
if (possibleTargets.length === 1) {
|
if (possibleTargets.length === 1) {
|
||||||
poke = possibleTargets[0]!;
|
poke = possibleTargets[0]!;
|
||||||
} else {
|
} else {
|
||||||
this.activateAbility(ofpoke!, "Frisk");
|
this.activateAbility(ofpoke, "Frisk");
|
||||||
this.log(args, kwArgs);
|
this.log(args, kwArgs);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -2263,7 +2261,7 @@ export class Battle {
|
||||||
this.scene.resultAnim(poke, item.name, 'neutral');
|
this.scene.resultAnim(poke, item.name, 'neutral');
|
||||||
break;
|
break;
|
||||||
case 'frisk':
|
case 'frisk':
|
||||||
this.activateAbility(ofpoke!, "Frisk");
|
this.activateAbility(ofpoke, "Frisk");
|
||||||
if (poke && poke !== ofpoke) { // used for gen 6
|
if (poke && poke !== ofpoke) { // used for gen 6
|
||||||
poke.itemEffect = 'frisked';
|
poke.itemEffect = 'frisked';
|
||||||
this.scene.resultAnim(poke, item.name, 'neutral');
|
this.scene.resultAnim(poke, item.name, 'neutral');
|
||||||
|
|
@ -2448,7 +2446,7 @@ export class Battle {
|
||||||
let commaIndex = newSpeciesForme.indexOf(',');
|
let commaIndex = newSpeciesForme.indexOf(',');
|
||||||
if (commaIndex !== -1) {
|
if (commaIndex !== -1) {
|
||||||
let level = newSpeciesForme.substr(commaIndex + 1).trim();
|
let level = newSpeciesForme.substr(commaIndex + 1).trim();
|
||||||
if (level.charAt(0) === 'L') {
|
if (level.startsWith('L')) {
|
||||||
poke.level = parseInt(level.substr(1), 10);
|
poke.level = parseInt(level.substr(1), 10);
|
||||||
}
|
}
|
||||||
newSpeciesForme = args[2].substr(0, commaIndex);
|
newSpeciesForme = args[2].substr(0, commaIndex);
|
||||||
|
|
@ -2483,7 +2481,7 @@ export class Battle {
|
||||||
this.activateAbility(poke, effect);
|
this.activateAbility(poke, effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
poke.boosts = {...tpoke.boosts};
|
poke.boosts = { ...tpoke.boosts };
|
||||||
poke.copyTypesFrom(tpoke, true);
|
poke.copyTypesFrom(tpoke, true);
|
||||||
poke.ability = tpoke.ability;
|
poke.ability = tpoke.ability;
|
||||||
poke.timesAttacked = tpoke.timesAttacked;
|
poke.timesAttacked = tpoke.timesAttacked;
|
||||||
|
|
@ -2742,7 +2740,7 @@ export class Battle {
|
||||||
break;
|
break;
|
||||||
case 'skydrop':
|
case 'skydrop':
|
||||||
if (kwArgs.interrupt) {
|
if (kwArgs.interrupt) {
|
||||||
this.scene.anim(poke, {time: 100});
|
this.scene.anim(poke, { time: 100 });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'confusion':
|
case 'confusion':
|
||||||
|
|
@ -2953,7 +2951,7 @@ export class Battle {
|
||||||
case 'gravity':
|
case 'gravity':
|
||||||
poke.removeVolatile('magnetrise' as ID);
|
poke.removeVolatile('magnetrise' as ID);
|
||||||
poke.removeVolatile('telekinesis' as ID);
|
poke.removeVolatile('telekinesis' as ID);
|
||||||
this.scene.anim(poke, {time: 100});
|
this.scene.anim(poke, { time: 100 });
|
||||||
break;
|
break;
|
||||||
case 'skillswap': case 'wanderingspirit':
|
case 'skillswap': case 'wanderingspirit':
|
||||||
if (this.gen <= 4) break;
|
if (this.gen <= 4) break;
|
||||||
|
|
@ -3139,7 +3137,8 @@ export class Battle {
|
||||||
default: {
|
default: {
|
||||||
throw new Error(`Unrecognized minor action: ${args[0]}`);
|
throw new Error(`Unrecognized minor action: ${args[0]}`);
|
||||||
break;
|
break;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
parseSpriteData(name) {
|
parseSpriteData(name) {
|
||||||
|
|
@ -3256,17 +3255,17 @@ export class Battle {
|
||||||
siden = parseInt(name.charAt(1), 10) - 1;
|
siden = parseInt(name.charAt(1), 10) - 1;
|
||||||
name = name.slice(4);
|
name = name.slice(4);
|
||||||
} else if (/^p[1-9][a-f]: /.test(name)) {
|
} else if (/^p[1-9][a-f]: /.test(name)) {
|
||||||
const slotChart: {[k: string]: number} = {a: 0, b: 1, c: 2, d: 3, e: 4, f: 5};
|
const slotChart: { [k: string]: number } = { a: 0, b: 1, c: 2, d: 3, e: 4, f: 5 };
|
||||||
siden = parseInt(name.charAt(1), 10) - 1;
|
siden = parseInt(name.charAt(1), 10) - 1;
|
||||||
slot = slotChart[name.charAt(2)];
|
slot = slotChart[name.charAt(2)];
|
||||||
name = name.slice(5);
|
name = name.slice(5);
|
||||||
pokemonid = `p${siden + 1}: ${name}`;
|
pokemonid = `p${siden + 1}: ${name}`;
|
||||||
}
|
}
|
||||||
return {name, siden, slot, pokemonid};
|
return { name, siden, slot, pokemonid };
|
||||||
}
|
}
|
||||||
getSwitchedPokemon(pokemonid: string, details: string) {
|
getSwitchedPokemon(pokemonid: string, details: string) {
|
||||||
if (pokemonid === '??') throw new Error(`pokemonid not passed`);
|
if (pokemonid === '??') throw new Error(`pokemonid not passed`);
|
||||||
const {name, siden, slot, pokemonid: parsedPokemonid} = this.parsePokemonId(pokemonid);
|
const { name, siden, slot, pokemonid: parsedPokemonid } = this.parsePokemonId(pokemonid);
|
||||||
pokemonid = parsedPokemonid;
|
pokemonid = parsedPokemonid;
|
||||||
|
|
||||||
const searchid = `${pokemonid}|${details}`;
|
const searchid = `${pokemonid}|${details}`;
|
||||||
|
|
@ -3300,12 +3299,12 @@ export class Battle {
|
||||||
return pokemon;
|
return pokemon;
|
||||||
}
|
}
|
||||||
rememberTeamPreviewPokemon(sideid: string, details: string) {
|
rememberTeamPreviewPokemon(sideid: string, details: string) {
|
||||||
const {siden} = this.parsePokemonId(sideid);
|
const { siden } = this.parsePokemonId(sideid);
|
||||||
|
|
||||||
return this.sides[siden].addPokemon('', '', details);
|
return this.sides[siden].addPokemon('', '', details);
|
||||||
}
|
}
|
||||||
findCorrespondingPokemon(serverPokemon: {ident: string, details: string}) {
|
findCorrespondingPokemon(serverPokemon: { ident: string, details: string }) {
|
||||||
const {siden} = this.parsePokemonId(serverPokemon.ident);
|
const { siden } = this.parsePokemonId(serverPokemon.ident);
|
||||||
const searchid = `${serverPokemon.ident}|${serverPokemon.details}`;
|
const searchid = `${serverPokemon.ident}|${serverPokemon.details}`;
|
||||||
for (const pokemon of this.sides[siden].pokemon) {
|
for (const pokemon of this.sides[siden].pokemon) {
|
||||||
if (pokemon.searchid === searchid) {
|
if (pokemon.searchid === searchid) {
|
||||||
|
|
@ -3318,7 +3317,7 @@ export class Battle {
|
||||||
if (!pokemonid || pokemonid === '??' || pokemonid === 'null' || pokemonid === 'false') {
|
if (!pokemonid || pokemonid === '??' || pokemonid === 'null' || pokemonid === 'false') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const {siden, slot, pokemonid: parsedPokemonid} = this.parsePokemonId(pokemonid);
|
const { siden, slot, pokemonid: parsedPokemonid } = this.parsePokemonId(pokemonid);
|
||||||
pokemonid = parsedPokemonid;
|
pokemonid = parsedPokemonid;
|
||||||
|
|
||||||
/** if true, don't match an active pokemon */
|
/** if true, don't match an active pokemon */
|
||||||
|
|
@ -3400,10 +3399,10 @@ export class Battle {
|
||||||
}
|
}
|
||||||
case 'tier': {
|
case 'tier': {
|
||||||
this.tier = args[1];
|
this.tier = args[1];
|
||||||
if (this.tier.slice(-13) === 'Random Battle') {
|
if (this.tier.endsWith('Random Battle')) {
|
||||||
this.speciesClause = true;
|
this.speciesClause = true;
|
||||||
}
|
}
|
||||||
if (this.tier.slice(-8) === ' (Blitz)') {
|
if (this.tier.endsWith(' (Blitz)')) {
|
||||||
this.messageFadeTime = 40;
|
this.messageFadeTime = 40;
|
||||||
this.isBlitz = true;
|
this.isBlitz = true;
|
||||||
}
|
}
|
||||||
|
|
@ -3480,26 +3479,26 @@ export class Battle {
|
||||||
}
|
}
|
||||||
case 'inactive': {
|
case 'inactive': {
|
||||||
if (!this.kickingInactive) this.kickingInactive = true;
|
if (!this.kickingInactive) this.kickingInactive = true;
|
||||||
if (args[1].slice(0, 11) === "Time left: ") {
|
if (args[1].startsWith("Time left: ")) {
|
||||||
let [time, totalTime, graceTime] = args[1].split(' | ');
|
let [time, totalTime, graceTime] = args[1].split(' | ');
|
||||||
this.kickingInactive = parseInt(time.slice(11), 10) || true;
|
this.kickingInactive = parseInt(time.slice(11), 10) || true;
|
||||||
this.totalTimeLeft = parseInt(totalTime, 10);
|
this.totalTimeLeft = parseInt(totalTime, 10);
|
||||||
this.graceTimeLeft = parseInt(graceTime || '', 10) || 0;
|
this.graceTimeLeft = parseInt(graceTime || '', 10) || 0;
|
||||||
if (this.totalTimeLeft === this.kickingInactive) this.totalTimeLeft = 0;
|
if (this.totalTimeLeft === this.kickingInactive) this.totalTimeLeft = 0;
|
||||||
return;
|
return;
|
||||||
} else if (args[1].slice(0, 9) === "You have ") {
|
} else if (args[1].startsWith("You have ")) {
|
||||||
// this is ugly but parseInt is documented to work this way
|
// this is ugly but parseInt is documented to work this way
|
||||||
// so I'm going to be lazy and not chop off the rest of the
|
// so I'm going to be lazy and not chop off the rest of the
|
||||||
// sentence
|
// sentence
|
||||||
this.kickingInactive = parseInt(args[1].slice(9), 10) || true;
|
this.kickingInactive = parseInt(args[1].slice(9), 10) || true;
|
||||||
return;
|
return;
|
||||||
} else if (args[1].slice(-14) === ' seconds left.') {
|
} else if (args[1].endsWith(' seconds left.')) {
|
||||||
let hasIndex = args[1].indexOf(' has ');
|
let hasIndex = args[1].indexOf(' has ');
|
||||||
let userid = window.app?.user?.get('userid');
|
let userid = window.app?.user?.get('userid');
|
||||||
if (toID(args[1].slice(0, hasIndex)) === userid) {
|
if (toID(args[1].slice(0, hasIndex)) === userid) {
|
||||||
this.kickingInactive = parseInt(args[1].slice(hasIndex + 5), 10) || true;
|
this.kickingInactive = parseInt(args[1].slice(hasIndex + 5), 10) || true;
|
||||||
}
|
}
|
||||||
} else if (args[1].slice(-27) === ' 15 seconds left this turn.') {
|
} else if (args[1].endsWith(' 15 seconds left this turn.')) {
|
||||||
if (this.isBlitz) return;
|
if (this.isBlitz) return;
|
||||||
}
|
}
|
||||||
this.log(args, undefined, preempt);
|
this.log(args, undefined, preempt);
|
||||||
|
|
@ -3598,7 +3597,7 @@ export class Battle {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'poke': {
|
case 'poke': {
|
||||||
let pokemon = this.rememberTeamPreviewPokemon(args[1], args[2])!;
|
let pokemon = this.rememberTeamPreviewPokemon(args[1], args[2]);
|
||||||
if (args[3] === 'mail') {
|
if (args[3] === 'mail') {
|
||||||
pokemon.item = '(mail)';
|
pokemon.item = '(mail)';
|
||||||
} else if (args[3] === 'item') {
|
} else if (args[3] === 'item') {
|
||||||
|
|
@ -3607,7 +3606,7 @@ export class Battle {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'updatepoke': {
|
case 'updatepoke': {
|
||||||
const {siden} = this.parsePokemonId(args[1]);
|
const { siden } = this.parsePokemonId(args[1]);
|
||||||
const side = this.sides[siden];
|
const side = this.sides[siden];
|
||||||
for (let i = 0; i < side.pokemon.length; i++) {
|
for (let i = 0; i < side.pokemon.length; i++) {
|
||||||
const pokemon = side.pokemon[i];
|
const pokemon = side.pokemon[i];
|
||||||
|
|
@ -3629,8 +3628,8 @@ export class Battle {
|
||||||
const side = this.getSide(args[1]);
|
const side = this.getSide(args[1]);
|
||||||
side.clearPokemon();
|
side.clearPokemon();
|
||||||
for (const set of team) {
|
for (const set of team) {
|
||||||
const details = set.species + (!set.level || set.level === 100 ? '' : ', L' + set.level) +
|
const details = set.species + (!set.level || set.level === 100 ? '' : `, L${set.level}`) +
|
||||||
(!set.gender || set.gender === 'N' ? '' : ', ' + set.gender) + (set.shiny ? ', shiny' : '');
|
(!set.gender || set.gender === 'N' ? '' : `, ${set.gender}`) + (set.shiny ? ', shiny' : '');
|
||||||
const pokemon = side.addPokemon('', '', details);
|
const pokemon = side.addPokemon('', '', details);
|
||||||
if (set.item) pokemon.item = set.item;
|
if (set.item) pokemon.item = set.item;
|
||||||
if (set.ability) pokemon.rememberAbility(set.ability);
|
if (set.ability) pokemon.rememberAbility(set.ability);
|
||||||
|
|
@ -3644,14 +3643,14 @@ export class Battle {
|
||||||
}
|
}
|
||||||
case 'switch': case 'drag': case 'replace': {
|
case 'switch': case 'drag': case 'replace': {
|
||||||
this.endLastTurn();
|
this.endLastTurn();
|
||||||
let poke = this.getSwitchedPokemon(args[1], args[2])!;
|
let poke = this.getSwitchedPokemon(args[1], args[2]);
|
||||||
let slot = poke.slot;
|
let slot = poke.slot;
|
||||||
poke.healthParse(args[3]);
|
poke.healthParse(args[3]);
|
||||||
poke.removeVolatile('itemremoved' as ID);
|
poke.removeVolatile('itemremoved' as ID);
|
||||||
poke.terastallized = args[2].match(/tera:([a-z]+)$/i)?.[1] || '';
|
poke.terastallized = (/tera:([a-z]+)$/i.exec(args[2]))?.[1] || '';
|
||||||
if (args[0] === 'switch') {
|
if (args[0] === 'switch') {
|
||||||
if (poke.side.active[slot]) {
|
if (poke.side.active[slot]) {
|
||||||
poke.side.switchOut(poke.side.active[slot]!, kwArgs);
|
poke.side.switchOut(poke.side.active[slot], kwArgs);
|
||||||
}
|
}
|
||||||
poke.side.switchIn(poke, kwArgs);
|
poke.side.switchIn(poke, kwArgs);
|
||||||
} else if (args[0] === 'replace') {
|
} else if (args[0] === 'replace') {
|
||||||
|
|
@ -3746,7 +3745,8 @@ export class Battle {
|
||||||
default: {
|
default: {
|
||||||
this.log(args, kwArgs, preempt);
|
this.log(args, kwArgs, preempt);
|
||||||
break;
|
break;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run(str: string, preempt?: boolean) {
|
run(str: string, preempt?: boolean) {
|
||||||
|
|
@ -3756,7 +3756,7 @@ export class Battle {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!str) return;
|
if (!str) return;
|
||||||
const {args, kwArgs} = BattleTextParser.parseBattleLine(str);
|
const { args, kwArgs } = BattleTextParser.parseBattleLine(str);
|
||||||
|
|
||||||
if (this.scene.maybeCloseMessagebar(args, kwArgs)) {
|
if (this.scene.maybeCloseMessagebar(args, kwArgs)) {
|
||||||
this.currentStep--;
|
this.currentStep--;
|
||||||
|
|
@ -3768,19 +3768,19 @@ export class Battle {
|
||||||
let nextArgs: Args = [''];
|
let nextArgs: Args = [''];
|
||||||
let nextKwargs: KWArgs = {};
|
let nextKwargs: KWArgs = {};
|
||||||
const nextLine = this.stepQueue[this.currentStep + 1] || '';
|
const nextLine = this.stepQueue[this.currentStep + 1] || '';
|
||||||
if (nextLine.slice(0, 2) === '|-') {
|
if (nextLine.startsWith('|-')) {
|
||||||
({args: nextArgs, kwArgs: nextKwargs} = BattleTextParser.parseBattleLine(nextLine));
|
({ args: nextArgs, kwArgs: nextKwargs } = BattleTextParser.parseBattleLine(nextLine));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
if (args[0].charAt(0) === '-' || args[0] === 'detailschange') {
|
if (args[0].startsWith('-') || args[0] === 'detailschange') {
|
||||||
this.runMinor(args, kwArgs, nextArgs, nextKwargs);
|
this.runMinor(args, kwArgs, nextArgs, nextKwargs);
|
||||||
} else {
|
} else {
|
||||||
this.runMajor(args, kwArgs, preempt);
|
this.runMajor(args, kwArgs, preempt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
if (args[0].charAt(0) === '-' || args[0] === 'detailschange') {
|
if (args[0].startsWith('-') || args[0] === 'detailschange') {
|
||||||
this.runMinor(args, kwArgs, nextArgs, nextKwargs);
|
this.runMinor(args, kwArgs, nextArgs, nextKwargs);
|
||||||
} else {
|
} else {
|
||||||
this.runMajor(args, kwArgs, preempt);
|
this.runMajor(args, kwArgs, preempt);
|
||||||
|
|
@ -3893,7 +3893,8 @@ export class Battle {
|
||||||
|
|
||||||
let interruptionCount: number;
|
let interruptionCount: number;
|
||||||
do {
|
do {
|
||||||
this.waitForAnimations = true;
|
// modified in this.run() but idk how to tell TS that
|
||||||
|
this.waitForAnimations = true as this['waitForAnimations'];
|
||||||
if (this.currentStep >= this.stepQueue.length) {
|
if (this.currentStep >= this.stepQueue.length) {
|
||||||
this.atQueueEnd = true;
|
this.atQueueEnd = true;
|
||||||
if (!this.ended && this.isReplay) this.prematureEnd();
|
if (!this.ended && this.isReplay) this.prematureEnd();
|
||||||
|
|
@ -3954,6 +3955,6 @@ declare const require: any;
|
||||||
declare const global: any;
|
declare const global: any;
|
||||||
if (typeof require === 'function') {
|
if (typeof require === 'function') {
|
||||||
// in Node
|
// in Node
|
||||||
(global as any).Battle = Battle;
|
global.Battle = Battle;
|
||||||
(global as any).Pokemon = Pokemon;
|
global.Pokemon = Pokemon;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PS} from "./client-main";
|
import { PS } from "./client-main";
|
||||||
|
|
||||||
declare var SockJS: any;
|
declare const SockJS: any;
|
||||||
|
declare const POKEMON_SHOWDOWN_TESTCLIENT_KEY: string | undefined;
|
||||||
|
|
||||||
export class PSConnection {
|
export class PSConnection {
|
||||||
socket: any = null;
|
socket: any = null;
|
||||||
|
|
@ -20,7 +21,7 @@ export class PSConnection {
|
||||||
const server = PS.server;
|
const server = PS.server;
|
||||||
const port = server.protocol === 'https' ? '' : ':' + server.port;
|
const port = server.protocol === 'https' ? '' : ':' + server.port;
|
||||||
const url = server.protocol + '://' + server.host + port + server.prefix;
|
const url = server.protocol + '://' + server.host + port + server.prefix;
|
||||||
const socket = this.socket = new SockJS(url, [], {timeout: 5 * 60 * 1000});
|
const socket = this.socket = new SockJS(url, [], { timeout: 5 * 60 * 1000 });
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
console.log('\u2705 (CONNECTED)');
|
console.log('\u2705 (CONNECTED)');
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
|
|
@ -60,17 +61,15 @@ export class PSConnection {
|
||||||
PS.connection = new PSConnection();
|
PS.connection = new PSConnection();
|
||||||
|
|
||||||
export const PSLoginServer = new class {
|
export const PSLoginServer = new class {
|
||||||
query(data: PostData): Promise<{[k: string]: any} | null> {
|
query(data: PostData): Promise<{ [k: string]: any } | null> {
|
||||||
let url = '/~~' + PS.server.id + '/action.php';
|
let url = '/~~' + PS.server.id + '/action.php';
|
||||||
if (location.pathname.endsWith('.html')) {
|
if (location.pathname.endsWith('.html')) {
|
||||||
url = 'https://' + Config.routes.client + url;
|
url = 'https://' + Config.routes.client + url;
|
||||||
// @ts-ignore
|
|
||||||
if (typeof POKEMON_SHOWDOWN_TESTCLIENT_KEY === 'string') {
|
if (typeof POKEMON_SHOWDOWN_TESTCLIENT_KEY === 'string') {
|
||||||
// @ts-ignore
|
data.sid = POKEMON_SHOWDOWN_TESTCLIENT_KEY.replace(/%2C/g, ',');
|
||||||
data.sid = POKEMON_SHOWDOWN_TESTCLIENT_KEY.replace(/\%2C/g, ',');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Net(url).get({method: data ? 'POST' : 'GET', body: data}).then(
|
return Net(url).get({ method: data ? 'POST' : 'GET', body: data }).then(
|
||||||
res => res ? JSON.parse(res.slice(1)) : null
|
res => res ? JSON.parse(res.slice(1)) : null
|
||||||
).catch(
|
).catch(
|
||||||
() => null
|
() => null
|
||||||
|
|
@ -96,7 +95,7 @@ class HttpError extends Error {
|
||||||
this.body = body;
|
this.body = body;
|
||||||
try {
|
try {
|
||||||
(Error as any).captureStackTrace(this, HttpError);
|
(Error as any).captureStackTrace(this, HttpError);
|
||||||
} catch (err) {}
|
} catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class NetRequest {
|
class NetRequest {
|
||||||
|
|
|
||||||
|
|
@ -13,76 +13,12 @@
|
||||||
* @license AGPLv3
|
* @license AGPLv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* Polyfills
|
|
||||||
*********************************************************************/
|
|
||||||
|
|
||||||
if (!Array.prototype.indexOf) {
|
|
||||||
Array.prototype.indexOf = function (searchElement, fromIndex) {
|
|
||||||
for (let i = (fromIndex || 0); i < this.length; i++) {
|
|
||||||
if (this[i] === searchElement) return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!Array.prototype.includes) {
|
|
||||||
Array.prototype.includes = function (thing) {
|
|
||||||
return this.indexOf(thing) !== -1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!String.prototype.includes) {
|
|
||||||
String.prototype.includes = function (thing) {
|
|
||||||
return this.indexOf(thing) !== -1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!String.prototype.startsWith) {
|
|
||||||
String.prototype.startsWith = function (thing) {
|
|
||||||
return this.slice(0, thing.length) === thing;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!String.prototype.endsWith) {
|
|
||||||
String.prototype.endsWith = function (thing) {
|
|
||||||
return this.slice(-thing.length) === thing;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!String.prototype.trim) {
|
|
||||||
String.prototype.trim = function () {
|
|
||||||
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!Object.assign) {
|
|
||||||
Object.assign = function (thing: any, rest: any) {
|
|
||||||
for (let i = 1; i < arguments.length; i++) {
|
|
||||||
let source = arguments[i];
|
|
||||||
for (let k in source) {
|
|
||||||
thing[k] = source[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return thing;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!Object.create) {
|
|
||||||
Object.create = function (proto: any) {
|
|
||||||
function F() {}
|
|
||||||
F.prototype = proto;
|
|
||||||
return new (F as any)();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!window.console) {
|
|
||||||
// in IE8, the console object is only defined when devtools is open
|
|
||||||
// I don't actually know if this will cause problems when you open devtools,
|
|
||||||
// but that's something I can figure out if I ever bother testing in IE8
|
|
||||||
(window as any).console = {
|
|
||||||
log() {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* PS Models
|
* PS Models
|
||||||
*********************************************************************/
|
*********************************************************************/
|
||||||
// PS's model classes are defined here
|
// PS's model classes are defined here
|
||||||
|
|
||||||
const PSURL = `${document.location!.protocol !== 'http:' ? 'https:' : ''}//${Config.routes.client}/`;
|
const PSURL = `${document.location.protocol !== 'http:' ? 'https:' : ''}//${Config.routes.client}/`;
|
||||||
|
|
||||||
export class PSSubscription {
|
export class PSSubscription {
|
||||||
observable: PSModel | PSStreamModel<any>;
|
observable: PSModel | PSStreamModel<any>;
|
||||||
|
|
@ -179,7 +115,7 @@ declare const ColorThief: any;
|
||||||
const PSBackground = new class extends PSStreamModel {
|
const PSBackground = new class extends PSStreamModel {
|
||||||
id = '';
|
id = '';
|
||||||
curId = '';
|
curId = '';
|
||||||
attrib: {url: string, title: string, artist: string} | null = null;
|
attrib: { url: string, title: string, artist: string } | null = null;
|
||||||
changeCount = 0;
|
changeCount = 0;
|
||||||
menuColors: string[] | null = null;
|
menuColors: string[] | null = null;
|
||||||
|
|
||||||
|
|
@ -318,7 +254,7 @@ const PSBackground = new class extends PSStreamModel {
|
||||||
"",
|
"",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (!menuColors && bgUrl.charAt(0) === '#') {
|
if (!menuColors && bgUrl.startsWith('#')) {
|
||||||
const r = parseInt(bgUrl.slice(1, 3), 16) / 255;
|
const r = parseInt(bgUrl.slice(1, 3), 16) / 255;
|
||||||
const g = parseInt(bgUrl.slice(3, 5), 16) / 255;
|
const g = parseInt(bgUrl.slice(3, 5), 16) / 255;
|
||||||
const b = parseInt(bgUrl.slice(5, 7), 16) / 255;
|
const b = parseInt(bgUrl.slice(5, 7), 16) / 255;
|
||||||
|
|
@ -396,7 +332,7 @@ PSBackground.subscribe(bgUrl => {
|
||||||
|
|
||||||
if (bgUrl !== null) {
|
if (bgUrl !== null) {
|
||||||
let background;
|
let background;
|
||||||
if (bgUrl.charAt(0) === '#') {
|
if (bgUrl.startsWith('#')) {
|
||||||
background = bgUrl;
|
background = bgUrl;
|
||||||
} else if (PSBackground.curId !== 'custom') {
|
} else if (PSBackground.curId !== 'custom') {
|
||||||
background = `#546bac url(${bgUrl}) no-repeat left center fixed`;
|
background = `#546bac url(${bgUrl}) no-repeat left center fixed`;
|
||||||
|
|
@ -424,7 +360,7 @@ PSBackground.subscribe(bgUrl => {
|
||||||
buttonStyleElem = new HTMLStyleElement();
|
buttonStyleElem = new HTMLStyleElement();
|
||||||
buttonStyleElem.id = 'mainmenubuttoncolors';
|
buttonStyleElem.id = 'mainmenubuttoncolors';
|
||||||
buttonStyleElem.textContent = cssBuf;
|
buttonStyleElem.textContent = cssBuf;
|
||||||
document.head!.appendChild(buttonStyleElem);
|
document.head.appendChild(buttonStyleElem);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buttonStyleElem.textContent = cssBuf;
|
buttonStyleElem.textContent = cssBuf;
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@
|
||||||
* @license AGPLv3
|
* @license AGPLv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { PSConnection, PSLoginServer } from './client-connection';
|
import { type PSConnection, PSLoginServer } from './client-connection';
|
||||||
import {PSModel, PSStreamModel} from './client-core';
|
import { PSModel, PSStreamModel } from './client-core';
|
||||||
import type {PSRouter} from './panels';
|
import type { PSRouter } from './panels';
|
||||||
import type {ChatRoom} from './panel-chat';
|
import type { ChatRoom } from './panel-chat';
|
||||||
import type {MainMenuRoom} from './panel-mainmenu';
|
import type { MainMenuRoom } from './panel-mainmenu';
|
||||||
import {toID, type ID} from './battle-dex';
|
import { toID, type ID } from './battle-dex';
|
||||||
import {BattleTextParser, type Args} from './battle-text-parser';
|
import { BattleTextParser, type Args } from './battle-text-parser';
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* Prefs
|
* Prefs
|
||||||
|
|
@ -24,9 +24,9 @@ import {BattleTextParser, type Args} from './battle-text-parser';
|
||||||
/**
|
/**
|
||||||
* String that contains only lowercase alphanumeric characters.
|
* String that contains only lowercase alphanumeric characters.
|
||||||
*/
|
*/
|
||||||
export type RoomID = string & {__isRoomID: true};
|
export type RoomID = string & { __isRoomID: true };
|
||||||
|
|
||||||
const PSPrefsDefaults: {[key: string]: any} = {};
|
const PSPrefsDefaults: { [key: string]: any } = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks user preferences, stored in localStorage. Contains most local
|
* Tracks user preferences, stored in localStorage. Contains most local
|
||||||
|
|
@ -54,7 +54,7 @@ class PSPrefs extends PSStreamModel<string | null> {
|
||||||
* table. Uses 1 and 0 instead of true/false for JSON packing
|
* table. Uses 1 and 0 instead of true/false for JSON packing
|
||||||
* reasons.
|
* reasons.
|
||||||
*/
|
*/
|
||||||
showjoins: {[serverid: string]: {[roomid: string]: 1 | 0}} | null = null;
|
showjoins: { [serverid: string]: { [roomid: string]: 1 | 0 } } | null = null;
|
||||||
/**
|
/**
|
||||||
* true = one panel, false = two panels, left and right
|
* true = one panel, false = two panels, left and right
|
||||||
*/
|
*/
|
||||||
|
|
@ -66,7 +66,7 @@ class PSPrefs extends PSStreamModel<string | null> {
|
||||||
notifvolume = 50;
|
notifvolume = 50;
|
||||||
|
|
||||||
storageEngine: 'localStorage' | 'iframeLocalStorage' | '' = '';
|
storageEngine: 'localStorage' | 'iframeLocalStorage' | '' = '';
|
||||||
storage: {[k: string]: any} = {};
|
storage: { [k: string]: any } = {};
|
||||||
readonly origin = `https://${Config.routes.client}`;
|
readonly origin = `https://${Config.routes.client}`;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -116,9 +116,9 @@ class PSPrefs extends PSStreamModel<string | null> {
|
||||||
fixPrefs(newPrefs: any) {
|
fixPrefs(newPrefs: any) {
|
||||||
const oldShowjoins = newPrefs['showjoins'];
|
const oldShowjoins = newPrefs['showjoins'];
|
||||||
if (oldShowjoins !== undefined && typeof oldShowjoins !== 'object') {
|
if (oldShowjoins !== undefined && typeof oldShowjoins !== 'object') {
|
||||||
const showjoins: {[serverid: string]: {[roomid: string]: 1 | 0}} = {};
|
const showjoins: { [serverid: string]: { [roomid: string]: 1 | 0 } } = {};
|
||||||
const serverShowjoins: {[roomid: string]: 1 | 0} = {global: (oldShowjoins ? 1 : 0)};
|
const serverShowjoins: { [roomid: string]: 1 | 0 } = { global: (oldShowjoins ? 1 : 0) };
|
||||||
const showroomjoins = newPrefs['showroomjoins'] as {[roomid: string]: boolean};
|
const showroomjoins = newPrefs['showroomjoins'] as { [roomid: string]: boolean };
|
||||||
for (const roomid in showroomjoins) {
|
for (const roomid in showroomjoins) {
|
||||||
serverShowjoins[roomid] = (showroomjoins[roomid] ? 1 : 0);
|
serverShowjoins[roomid] = (showroomjoins[roomid] ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
@ -172,7 +172,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||||
/** false if it uses the ladder in the website */
|
/** false if it uses the ladder in the website */
|
||||||
usesLocalLadder = false;
|
usesLocalLadder = false;
|
||||||
list: Team[] = [];
|
list: Team[] = [];
|
||||||
byKey: {[key: string]: Team | undefined} = {};
|
byKey: { [key: string]: Team | undefined } = {};
|
||||||
deletedTeams: [Team, number][] = [];
|
deletedTeams: [Team, number][] = [];
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -204,7 +204,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer.charAt(0) === '[' && !buffer.trim().includes('\n')) {
|
if (buffer.startsWith('[') && !buffer.trim().includes('\n')) {
|
||||||
this.unpackOldBuffer(buffer);
|
this.unpackOldBuffer(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -243,7 +243,6 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||||
unpackOldBuffer(buffer: string) {
|
unpackOldBuffer(buffer: string) {
|
||||||
alert(`Your team storage format is too old for PS. You'll need to upgrade it at https://${Config.routes.client}/recoverteams.html`);
|
alert(`Your team storage format is too old for PS. You'll need to upgrade it at https://${Config.routes.client}/recoverteams.html`);
|
||||||
this.list = [];
|
this.list = [];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
packAll(teams: Team[]) {
|
packAll(teams: Team[]) {
|
||||||
return teams.map(team => (
|
return teams.map(team => (
|
||||||
|
|
@ -264,7 +263,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||||
let slashIndex = line.lastIndexOf('/', pipeIndex);
|
let slashIndex = line.lastIndexOf('/', pipeIndex);
|
||||||
if (slashIndex < 0) slashIndex = bracketIndex; // line.slice(slashIndex + 1, pipeIndex) will be ''
|
if (slashIndex < 0) slashIndex = bracketIndex; // line.slice(slashIndex + 1, pipeIndex) will be ''
|
||||||
let format = bracketIndex > 0 ? line.slice(0, bracketIndex) : 'gen7';
|
let format = bracketIndex > 0 ? line.slice(0, bracketIndex) : 'gen7';
|
||||||
if (format.slice(0, 3) !== 'gen') format = 'gen6' + format;
|
if (!format.startsWith('gen')) format = 'gen6' + format;
|
||||||
const name = line.slice(slashIndex + 1, pipeIndex);
|
const name = line.slice(slashIndex + 1, pipeIndex);
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
|
|
@ -290,7 +289,7 @@ class PSUser extends PSModel {
|
||||||
avatar = "1";
|
avatar = "1";
|
||||||
setName(fullName: string, named: boolean, avatar: string) {
|
setName(fullName: string, named: boolean, avatar: string) {
|
||||||
const loggingIn = (!this.named && named);
|
const loggingIn = (!this.named && named);
|
||||||
const {name, group} = BattleTextParser.parseNameParts(fullName);
|
const { name, group } = BattleTextParser.parseNameParts(fullName);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.userid = toID(name);
|
this.userid = toID(name);
|
||||||
|
|
@ -340,7 +339,7 @@ class PSServer {
|
||||||
registered = Config.defaultserver.registered;
|
registered = Config.defaultserver.registered;
|
||||||
prefix = '/showdown';
|
prefix = '/showdown';
|
||||||
protocol: 'http' | 'https' = Config.defaultserver.httpport ? 'https' : 'http';
|
protocol: 'http' | 'https' = Config.defaultserver.httpport ? 'https' : 'http';
|
||||||
groups: {[symbol: string]: PSGroup} = {
|
groups: { [symbol: string]: PSGroup } = {
|
||||||
'~': {
|
'~': {
|
||||||
name: "Administrator (~)",
|
name: "Administrator (~)",
|
||||||
type: 'leadership',
|
type: 'leadership',
|
||||||
|
|
@ -458,14 +457,14 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
* In particular, this is `true` after sending `/join`, and `false`
|
* In particular, this is `true` after sending `/join`, and `false`
|
||||||
* after sending `/leave`, even before the server responds.
|
* after sending `/leave`, even before the server responds.
|
||||||
*/
|
*/
|
||||||
connected: boolean = false;
|
connected = false;
|
||||||
/**
|
/**
|
||||||
* Can this room even be connected to at all?
|
* Can this room even be connected to at all?
|
||||||
* `true` = pass messages from the server to subscribers
|
* `true` = pass messages from the server to subscribers
|
||||||
* `false` = throw an error if we receive messages from the server
|
* `false` = throw an error if we receive messages from the server
|
||||||
*/
|
*/
|
||||||
readonly canConnect: boolean = false;
|
readonly canConnect: boolean = false;
|
||||||
connectWhenLoggedIn: boolean = false;
|
connectWhenLoggedIn = false;
|
||||||
onParentEvent: ((eventId: 'focus' | 'keydown', e?: Event) => false | void) | null = null;
|
onParentEvent: ((eventId: 'focus' | 'keydown', e?: Event) => false | void) | null = null;
|
||||||
|
|
||||||
width = 0;
|
width = 0;
|
||||||
|
|
@ -491,7 +490,7 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
if (options.rightPopup) this.rightPopup = true;
|
if (options.rightPopup) this.rightPopup = true;
|
||||||
if (options.connected) this.connected = true;
|
if (options.connected) this.connected = true;
|
||||||
}
|
}
|
||||||
notify(options: {title: string, body?: string, noAutoDismiss?: boolean, id?: string}) {
|
notify(options: { title: string, body?: string, noAutoDismiss?: boolean, id?: string }) {
|
||||||
if (options.noAutoDismiss && !options.id) {
|
if (options.noAutoDismiss && !options.id) {
|
||||||
throw new Error(`Must specify id for manual dismissing`);
|
throw new Error(`Must specify id for manual dismissing`);
|
||||||
}
|
}
|
||||||
|
|
@ -528,7 +527,7 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
break;
|
break;
|
||||||
} case 'tempnotify': {
|
} case 'tempnotify': {
|
||||||
const [, id, title, body, toHighlight] = args;
|
const [, id, title, body, toHighlight] = args;
|
||||||
this.notify({title, body, id});
|
this.notify({ title, body, id });
|
||||||
break;
|
break;
|
||||||
} case 'tempnotifyoff': {
|
} case 'tempnotifyoff': {
|
||||||
const [, id] = args;
|
const [, id] = args;
|
||||||
|
|
@ -540,7 +539,8 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`This room is not designed to receive messages`);
|
throw new Error(`This room is not designed to receive messages`);
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handleMessage(line: string) {
|
handleMessage(line: string) {
|
||||||
if (!line.startsWith('/') || line.startsWith('//')) return false;
|
if (!line.startsWith('/') || line.startsWith('//')) return false;
|
||||||
|
|
@ -551,7 +551,8 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
case 'logout': {
|
case 'logout': {
|
||||||
PS.user.logOut();
|
PS.user.logOut();
|
||||||
return true;
|
return true;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
send(msg: string, direct?: boolean) {
|
send(msg: string, direct?: boolean) {
|
||||||
|
|
@ -570,7 +571,7 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
|
|
||||||
class PlaceholderRoom extends PSRoom {
|
class PlaceholderRoom extends PSRoom {
|
||||||
queue = [] as Args[];
|
queue = [] as Args[];
|
||||||
override readonly classType: 'placeholder' = 'placeholder';
|
override readonly classType = 'placeholder';
|
||||||
override receiveLine(args: Args) {
|
override receiveLine(args: Args) {
|
||||||
this.queue.push(args);
|
this.queue.push(args);
|
||||||
}
|
}
|
||||||
|
|
@ -580,7 +581,7 @@ class PlaceholderRoom extends PSRoom {
|
||||||
* PS
|
* PS
|
||||||
*********************************************************************/
|
*********************************************************************/
|
||||||
|
|
||||||
type RoomType = {Model?: typeof PSRoom, Component: any, title?: string};
|
type RoomType = { Model?: typeof PSRoom, Component: any, title?: string };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This model updates:
|
* This model updates:
|
||||||
|
|
@ -609,7 +610,7 @@ export const PS = new class extends PSModel {
|
||||||
|
|
||||||
router: PSRouter = null!;
|
router: PSRouter = null!;
|
||||||
|
|
||||||
rooms: {[roomid: string]: PSRoom | undefined} = {};
|
rooms: { [roomid: string]: PSRoom | undefined } = {};
|
||||||
roomTypes: {
|
roomTypes: {
|
||||||
[type: string]: RoomType | undefined,
|
[type: string]: RoomType | undefined,
|
||||||
} = {};
|
} = {};
|
||||||
|
|
@ -667,7 +668,7 @@ export const PS = new class extends PSModel {
|
||||||
* the Rooms panel and clicking "Hide")
|
* the Rooms panel and clicking "Hide")
|
||||||
*
|
*
|
||||||
* Will NOT be true if only one panel fits onto the screen at the
|
* Will NOT be true if only one panel fits onto the screen at the
|
||||||
* moment, but resizing will display multiple panels – for that,
|
* moment, but resizing will display multiple panels – for that,
|
||||||
* check `PS.leftRoomWidth === 0`
|
* check `PS.leftRoomWidth === 0`
|
||||||
*/
|
*/
|
||||||
onePanelMode = false;
|
onePanelMode = false;
|
||||||
|
|
@ -688,7 +689,7 @@ export const PS = new class extends PSModel {
|
||||||
* for security reasons it's impossible to know what they are until
|
* for security reasons it's impossible to know what they are until
|
||||||
* they're dropped.
|
* they're dropped.
|
||||||
*/
|
*/
|
||||||
dragging: {type: 'room', roomid: RoomID} | null = null;
|
dragging: { type: 'room', roomid: RoomID } | null = null;
|
||||||
|
|
||||||
/** Tracks whether or not to display the "Use arrow keys" hint */
|
/** Tracks whether or not to display the "Use arrow keys" hint */
|
||||||
arrowKeysUsed = false;
|
arrowKeysUsed = false;
|
||||||
|
|
@ -841,7 +842,8 @@ export const PS = new class extends PSModel {
|
||||||
}
|
}
|
||||||
this.update();
|
this.update();
|
||||||
continue;
|
continue;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
if (room) room.receiveLine(args);
|
if (room) room.receiveLine(args);
|
||||||
}
|
}
|
||||||
if (room) room.update(isInit ? [`initdone`] : null);
|
if (room) room.update(isInit ? [`initdone`] : null);
|
||||||
|
|
@ -1062,7 +1064,7 @@ export const PS = new class extends PSModel {
|
||||||
options.id = `pm-${options.id.slice(10)}` as RoomID;
|
options.id = `pm-${options.id.slice(10)}` as RoomID;
|
||||||
options.challengeMenuOpen = true;
|
options.challengeMenuOpen = true;
|
||||||
}
|
}
|
||||||
if (options.id.startsWith('pm-') && options.id.indexOf('-', 3) < 0) {
|
if (options.id.startsWith('pm-') && !options.id.includes('-', 3)) {
|
||||||
const userid1 = PS.user.userid;
|
const userid1 = PS.user.userid;
|
||||||
const userid2 = options.id.slice(3);
|
const userid2 = options.id.slice(3);
|
||||||
options.id = `pm-${[userid1, userid2].sort().join('-')}` as RoomID;
|
options.id = `pm-${[userid1, userid2].sort().join('-')}` as RoomID;
|
||||||
|
|
@ -1174,7 +1176,7 @@ export const PS = new class extends PSModel {
|
||||||
}
|
}
|
||||||
join(roomid: RoomID, side?: PSRoomLocation | null, noFocus?: boolean) {
|
join(roomid: RoomID, side?: PSRoomLocation | null, noFocus?: boolean) {
|
||||||
if (this.room.id === roomid) return;
|
if (this.room.id === roomid) return;
|
||||||
this.addRoom({id: roomid, side}, noFocus);
|
this.addRoom({ id: roomid, side }, noFocus);
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
leave(roomid: RoomID) {
|
leave(roomid: RoomID) {
|
||||||
|
|
|
||||||
34
play.pokemonshowdown.com/src/globals.d.ts
vendored
34
play.pokemonshowdown.com/src/globals.d.ts
vendored
|
|
@ -1,26 +1,28 @@
|
||||||
|
/* eslint-disable @typescript-eslint/consistent-type-imports */
|
||||||
|
|
||||||
// dex data
|
// dex data
|
||||||
///////////
|
///////////
|
||||||
|
|
||||||
declare var BattleText: {[id: string]: {[templateName: string]: string}};
|
declare const BattleText: { [id: string]: { [templateName: string]: string } };
|
||||||
declare var BattleFormats: {[id: string]: import('./panel-teamdropdown').FormatData};
|
declare const BattleFormats: { [id: string]: import('./panel-teamdropdown').FormatData };
|
||||||
declare var BattlePokedex: any;
|
declare const BattlePokedex: any;
|
||||||
declare var BattleMovedex: any;
|
declare const BattleMovedex: any;
|
||||||
declare var BattleAbilities: any;
|
declare const BattleAbilities: any;
|
||||||
declare var BattleItems: any;
|
declare const BattleItems: any;
|
||||||
declare var BattleAliases: any;
|
declare const BattleAliases: any;
|
||||||
declare var BattleStatuses: any;
|
declare const BattleStatuses: any;
|
||||||
declare var BattlePokemonSprites: any;
|
declare const BattlePokemonSprites: any;
|
||||||
declare var BattlePokemonSpritesBW: any;
|
declare const BattlePokemonSpritesBW: any;
|
||||||
declare var NonBattleGames: {[id: string]: string};
|
declare const NonBattleGames: { [id: string]: string };
|
||||||
|
|
||||||
// PS globals
|
// PS globals
|
||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
declare var Config: any;
|
declare const Config: any;
|
||||||
declare var Replays: any;
|
declare const Replays: any;
|
||||||
declare var exports: any;
|
declare const exports: any;
|
||||||
type AnyObject = {[k: string]: any};
|
type AnyObject = { [k: string]: any };
|
||||||
declare var app: {user: AnyObject, rooms: AnyObject, ignore?: AnyObject};
|
declare const app: { user: AnyObject, rooms: AnyObject, ignore?: AnyObject };
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
const MAX_UNDO_HISTORY = 100;
|
const MAX_UNDO_HISTORY = 100;
|
||||||
export type MiniEditPlugin = new (editor: MiniEdit) => unknown;
|
export type MiniEditPlugin = new (editor: MiniEdit) => unknown;
|
||||||
export type MiniEditSelection = {start: number, end: number} | null;
|
export type MiniEditSelection = { start: number, end: number } | null;
|
||||||
export class MiniEdit {
|
export class MiniEdit {
|
||||||
static plugins: MiniEditPlugin[] = [];
|
static plugins: MiniEditPlugin[] = [];
|
||||||
|
|
||||||
|
|
@ -31,7 +31,6 @@ export class MiniEdit {
|
||||||
* it doesn't already exist and the user types a newline at the end
|
* it doesn't already exist and the user types a newline at the end
|
||||||
* of the text, it wouldn't appear.
|
* of the text, it wouldn't appear.
|
||||||
*/
|
*/
|
||||||
// tslint:disable-next-line
|
|
||||||
_setContent: (text: string) => void;
|
_setContent: (text: string) => void;
|
||||||
pushHistory?: (text: string, selection: MiniEditSelection) => void;
|
pushHistory?: (text: string, selection: MiniEditSelection) => void;
|
||||||
onKeyDown = (ev: KeyboardEvent) => {
|
onKeyDown = (ev: KeyboardEvent) => {
|
||||||
|
|
@ -41,7 +40,9 @@ export class MiniEdit {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(el: HTMLElement, options: {setContent: MiniEdit['_setContent'], onKeyDown?: (ev: KeyboardEvent) => void}) {
|
constructor(
|
||||||
|
el: HTMLElement, options: { setContent: MiniEdit['_setContent'], onKeyDown?: (ev: KeyboardEvent) => void }
|
||||||
|
) {
|
||||||
this.element = el;
|
this.element = el;
|
||||||
|
|
||||||
this._setContent = options.setContent;
|
this._setContent = options.setContent;
|
||||||
|
|
@ -55,7 +56,6 @@ export class MiniEdit {
|
||||||
});
|
});
|
||||||
this.element.addEventListener('keydown', this.onKeyDown);
|
this.element.addEventListener('keydown', this.onKeyDown);
|
||||||
|
|
||||||
// tslint:disable-next-line
|
|
||||||
for (const Plugin of MiniEdit.plugins) new Plugin(this);
|
for (const Plugin of MiniEdit.plugins) new Plugin(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ export class MiniEdit {
|
||||||
this.pushHistory?.(text, selection);
|
this.pushHistory?.(text, selection);
|
||||||
}
|
}
|
||||||
getValue(): string {
|
getValue(): string {
|
||||||
let text = this.element.textContent || '';
|
const text = this.element.textContent || '';
|
||||||
if (text.endsWith('\n')) return text.slice(0, -1);
|
if (text.endsWith('\n')) return text.slice(0, -1);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ export class MiniEdit {
|
||||||
const selection = this.getSelection()!;
|
const selection = this.getSelection()!;
|
||||||
const oldContent = this.getValue();
|
const oldContent = this.getValue();
|
||||||
const newText = oldContent.slice(0, selection.start) + text + oldContent.slice(selection.end);
|
const newText = oldContent.slice(0, selection.start) + text + oldContent.slice(selection.end);
|
||||||
this.setValue(newText, {start: selection.start + text.length, end: selection.start + text.length});
|
this.setValue(newText, { start: selection.start + text.length, end: selection.start + text.length });
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelection(): MiniEditSelection {
|
getSelection(): MiniEditSelection {
|
||||||
|
|
@ -116,7 +116,7 @@ export class MiniEdit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (start === null || end === null) ? null : {start, end};
|
return (start === null || end === null) ? null : { start, end };
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelection(sel: MiniEditSelection): void {
|
setSelection(sel: MiniEditSelection): void {
|
||||||
|
|
@ -149,7 +149,7 @@ export class MiniEdit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select(): void {
|
select(): void {
|
||||||
this.setSelection({start: 0, end: this.getValue().length});
|
this.setSelection({ start: 0, end: this.getValue().length });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,11 +174,11 @@ export class MiniEditUndoPlugin {
|
||||||
editor: MiniEdit;
|
editor: MiniEdit;
|
||||||
undoIndex: number | null = null;
|
undoIndex: number | null = null;
|
||||||
ignoreInput = false;
|
ignoreInput = false;
|
||||||
history: {text: string, selection: MiniEditSelection}[] = [];
|
history: { text: string, selection: MiniEditSelection }[] = [];
|
||||||
|
|
||||||
constructor(editor: MiniEdit) {
|
constructor(editor: MiniEdit) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
this.history.push({text: editor.getValue(), selection: {start: 0, end: 0}});
|
this.history.push({ text: editor.getValue(), selection: { start: 0, end: 0 } });
|
||||||
|
|
||||||
this.editor.pushHistory = this.onPushHistory;
|
this.editor.pushHistory = this.onPushHistory;
|
||||||
editor.element.addEventListener('keydown', this.onKeyDown);
|
editor.element.addEventListener('keydown', this.onKeyDown);
|
||||||
|
|
@ -197,7 +197,7 @@ export class MiniEditUndoPlugin {
|
||||||
this.undoIndex = null;
|
this.undoIndex = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.history.push({text, selection});
|
this.history.push({ text, selection });
|
||||||
|
|
||||||
if (this.history.length > MAX_UNDO_HISTORY) this.history.shift();
|
if (this.history.length > MAX_UNDO_HISTORY) this.history.shift();
|
||||||
};
|
};
|
||||||
|
|
@ -227,7 +227,7 @@ export class MiniEditUndoPlugin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {text, selection} = this.history[this.undoIndex];
|
const { text, selection } = this.history[this.undoIndex];
|
||||||
this.ignoreInput = true;
|
this.ignoreInput = true;
|
||||||
this.editor.setValue(text, selection);
|
this.editor.setValue(text, selection);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import preact from "../js/lib/preact";
|
import preact from "../js/lib/preact";
|
||||||
import {PS, PSRoom, type RoomOptions, type RoomID} from "./client-main";
|
import { PS, PSRoom, type RoomOptions, type RoomID } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
import {ChatLog, ChatRoom, ChatTextEntry, ChatUserList} from "./panel-chat";
|
import { ChatLog, ChatRoom, ChatTextEntry, ChatUserList } from "./panel-chat";
|
||||||
import {FormatDropdown} from "./panel-mainmenu";
|
import { FormatDropdown } from "./panel-mainmenu";
|
||||||
import {Battle, Pokemon, type ServerPokemon} from "./battle";
|
import { Battle, type Pokemon, type ServerPokemon } from "./battle";
|
||||||
import {BattleScene} from "./battle-animations";
|
import { BattleScene } from "./battle-animations";
|
||||||
import { Dex, toID } from "./battle-dex";
|
import { Dex, toID } from "./battle-dex";
|
||||||
import {
|
import {
|
||||||
BattleChoiceBuilder, type BattleMoveRequest, type BattleRequest, type BattleRequestSideInfo,
|
BattleChoiceBuilder, type BattleMoveRequest, type BattleRequest, type BattleRequestSideInfo,
|
||||||
type BattleSwitchRequest, type BattleTeamRequest
|
type BattleSwitchRequest, type BattleTeamRequest,
|
||||||
} from "./battle-choices";
|
} from "./battle-choices";
|
||||||
import type {Args} from "./battle-text-parser";
|
import type { Args } from "./battle-text-parser";
|
||||||
|
|
||||||
type BattleDesc = {
|
type BattleDesc = {
|
||||||
id: RoomID,
|
id: RoomID,
|
||||||
|
|
@ -69,29 +69,41 @@ class BattlesPanel extends PSRoomPanel<BattlesRoom> {
|
||||||
override render() {
|
override render() {
|
||||||
const room = this.props.room;
|
const room = this.props.room;
|
||||||
return <PSPanelWrapper room={room} scrollable><div class="pad">
|
return <PSPanelWrapper room={room} scrollable><div class="pad">
|
||||||
<button class="button" style="float:right;font-size:10pt;margin-top:3px" name="close"><i class="fa fa-times"></i> Close</button>
|
<button class="button" style="float:right;font-size:10pt;margin-top:3px" name="close">
|
||||||
|
<i class="fa fa-times"></i> Close
|
||||||
|
</button>
|
||||||
<div class="roomlist">
|
<div class="roomlist">
|
||||||
<p>
|
<p>
|
||||||
<button class="button" name="refresh" onClick={this.refresh}><i class="fa fa-refresh"></i> Refresh</button> <span style={Dex.getPokemonIcon('meloetta-pirouette') + ';display:inline-block;vertical-align:middle'} class="picon" title="Meloetta is PS's mascot! The Pirouette forme is Fighting-type, and represents our battles."></span>
|
<button class="button" name="refresh" onClick={this.refresh}><i class="fa fa-refresh"></i> Refresh</button> {}
|
||||||
|
<span
|
||||||
|
style={Dex.getPokemonIcon('meloetta-pirouette') + ';display:inline-block;vertical-align:middle'} class="picon"
|
||||||
|
title="Meloetta is PS's mascot! The Pirouette forme is Fighting-type, and represents our battles."
|
||||||
|
></span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label class="label">Format:</label><FormatDropdown onChange={this.changeFormat} />
|
<label class="label">Format:</label><FormatDropdown onChange={this.changeFormat} />
|
||||||
</p>
|
</p>
|
||||||
{/* <label>Minimum Elo: <select name="elofilter"><option value="none">None</option><option value="1100">1100</option><option value="1300">1300</option><option value="1500">1500</option><option value="1700">1700</option><option value="1900">1900</option></select></label>
|
{/* <label>
|
||||||
|
Minimum Elo: <select name="elofilter">
|
||||||
|
<option value="none">None</option><option value="1100">1100</option><option value="1300">1300</option>
|
||||||
|
<option value="1500">1500</option><option value="1700">1700</option><option value="1900">1900</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
<form class="search">
|
<form class="search">
|
||||||
<p>
|
<p>
|
||||||
<input type="text" name="prefixsearch" class="textbox" placeholder="Username prefix"/><button type="submit" class="button">Search</button>
|
<input type="text" name="prefixsearch" class="textbox" placeholder="Username prefix"/>
|
||||||
|
<button type="submit" class="button">Search</button>
|
||||||
</p>
|
</p>
|
||||||
</form> */}
|
</form> */}
|
||||||
<div class="list">{!room.battles ?
|
<div class="list">{!room.battles ? (
|
||||||
<p>Loading...</p>
|
<p>Loading...</p>
|
||||||
: !room.battles.length ?
|
) : !room.battles.length ? (
|
||||||
<p>No battles are going on</p>
|
<p>No battles are going on</p>
|
||||||
:
|
) : (
|
||||||
room.battles.map(battle => this.renderBattleLink(battle))
|
room.battles.map(battle => this.renderBattleLink(battle))
|
||||||
}</div>
|
)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div></PSPanelWrapper>;
|
</div></PSPanelWrapper>;
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +141,7 @@ class BattleRoom extends ChatRoom {
|
||||||
return true;
|
return true;
|
||||||
} case 'ffto': case 'fastfowardto': {
|
} case 'ffto': case 'fastfowardto': {
|
||||||
let turnNum = Number(target);
|
let turnNum = Number(target);
|
||||||
if (target.charAt(0) === '+' || turnNum < 0) {
|
if (target.startsWith('+') || turnNum < 0) {
|
||||||
turnNum += this.battle.turn;
|
turnNum += this.battle.turn;
|
||||||
if (turnNum < 0) turnNum = 0;
|
if (turnNum < 0) turnNum = 0;
|
||||||
} else if (target === 'end') {
|
} else if (target === 'end') {
|
||||||
|
|
@ -169,7 +181,8 @@ class BattleRoom extends ChatRoom {
|
||||||
if (this.choices.isDone()) this.send(`/choose ${this.choices.toString()}`, true);
|
if (this.choices.isDone()) this.send(`/choose ${this.choices.toString()}`, true);
|
||||||
this.update(null);
|
this.update(null);
|
||||||
return true;
|
return true;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
return super.handleMessage(line);
|
return super.handleMessage(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +197,7 @@ class BattleDiv extends preact.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
function MoveButton(props: {
|
function MoveButton(props: {
|
||||||
children: string, cmd: string, moveData: {pp: number, maxpp: number}, type: Dex.TypeName, tooltip: string,
|
children: string, cmd: string, moveData: { pp: number, maxpp: number }, type: Dex.TypeName, tooltip: string,
|
||||||
}) {
|
}) {
|
||||||
return <button name="cmd" value={props.cmd} class={`type-${props.type} has-tooltip`} data-tooltip={props.tooltip}>
|
return <button name="cmd" value={props.cmd} class={`type-${props.type} has-tooltip`} data-tooltip={props.tooltip}>
|
||||||
{props.children}<br />
|
{props.children}<br />
|
||||||
|
|
@ -198,7 +211,7 @@ function PokemonButton(props: {
|
||||||
if (!pokemon) {
|
if (!pokemon) {
|
||||||
return <button
|
return <button
|
||||||
name="cmd" value={props.cmd} class={`${props.disabled ? 'disabled ' : ''}has-tooltip`}
|
name="cmd" value={props.cmd} class={`${props.disabled ? 'disabled ' : ''}has-tooltip`}
|
||||||
style={{opacity: props.disabled === 'fade' ? 0.5 : 1}} data-tooltip={props.tooltip}
|
style={{ opacity: props.disabled === 'fade' ? 0.5 : 1 }} data-tooltip={props.tooltip}
|
||||||
>
|
>
|
||||||
(empty slot)
|
(empty slot)
|
||||||
</button>;
|
</button>;
|
||||||
|
|
@ -213,14 +226,14 @@ function PokemonButton(props: {
|
||||||
|
|
||||||
return <button
|
return <button
|
||||||
name="cmd" value={props.cmd} class={`${props.disabled ? 'disabled ' : ''}has-tooltip`}
|
name="cmd" value={props.cmd} class={`${props.disabled ? 'disabled ' : ''}has-tooltip`}
|
||||||
style={{opacity: props.disabled === 'fade' ? 0.5 : 1}} data-tooltip={props.tooltip}
|
style={{ opacity: props.disabled === 'fade' ? 0.5 : 1 }} data-tooltip={props.tooltip}
|
||||||
>
|
>
|
||||||
<span class="picon" style={Dex.getPokemonIcon(pokemon)}></span>
|
<span class="picon" style={Dex.getPokemonIcon(pokemon)}></span>
|
||||||
{pokemon.name}
|
{pokemon.name}
|
||||||
{
|
{
|
||||||
!props.noHPBar && !pokemon.fainted &&
|
!props.noHPBar && !pokemon.fainted &&
|
||||||
<span class={hpColorClass}>
|
<span class={hpColorClass}>
|
||||||
<span style={{width: Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1}}></span>
|
<span style={{ width: Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1 }}></span>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
{!props.noHPBar && pokemon.status && <span class={`status ${pokemon.status}`}></span>}
|
{!props.noHPBar && pokemon.status && <span class={`status ${pokemon.status}`}></span>}
|
||||||
|
|
@ -343,20 +356,30 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
||||||
const atEnd = room.battle.atQueueEnd;
|
const atEnd = room.battle.atQueueEnd;
|
||||||
return <div class="controls">
|
return <div class="controls">
|
||||||
<p>
|
<p>
|
||||||
{atEnd ?
|
{atEnd ? (
|
||||||
<button class="button disabled" name="cmd" value="/play"><i class="fa fa-play"></i><br />Play</button>
|
<button class="button disabled" name="cmd" value="/play"><i class="fa fa-play"></i><br />Play</button>
|
||||||
: room.battle.paused ?
|
) : room.battle.paused ? (
|
||||||
<button class="button" name="cmd" value="/play"><i class="fa fa-play"></i><br />Play</button>
|
<button class="button" name="cmd" value="/play"><i class="fa fa-play"></i><br />Play</button>
|
||||||
:
|
) : (
|
||||||
<button class="button" name="cmd" value="/pause"><i class="fa fa-pause"></i><br />Pause</button>
|
<button class="button" name="cmd" value="/pause"><i class="fa fa-pause"></i><br />Pause</button>
|
||||||
} {}
|
)} {}
|
||||||
<button class="button" name="cmd" value="/ffto -1"><i class="fa fa-step-backward"></i><br />Last turn</button>
|
<button class="button" name="cmd" value="/ffto -1">
|
||||||
<button class={"button" + (atEnd ? " disabled" : "")} name="cmd" value="/ffto +1"><i class="fa fa-step-forward"></i><br />Skip turn</button> {}
|
<i class="fa fa-step-backward"></i><br />Last turn
|
||||||
<button class="button" name="cmd" value="/ffto 0"><i class="fa fa-undo"></i><br />First turn</button>
|
</button>
|
||||||
<button class={"button" + (atEnd ? " disabled" : "")} name="cmd" value="/ffto end"><i class="fa fa-fast-forward"></i><br />Skip to end</button>
|
<button class={"button" + (atEnd ? " disabled" : "")} name="cmd" value="/ffto +1">
|
||||||
|
<i class="fa fa-step-forward"></i><br />Skip turn
|
||||||
|
</button> {}
|
||||||
|
<button class="button" name="cmd" value="/ffto 0">
|
||||||
|
<i class="fa fa-undo"></i><br />First turn
|
||||||
|
</button>
|
||||||
|
<button class={"button" + (atEnd ? " disabled" : "")} name="cmd" value="/ffto end">
|
||||||
|
<i class="fa fa-fast-forward"></i><br />Skip to end
|
||||||
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<button class="button" name="cmd" value="/switchsides"><i class="fa fa-random"></i> Switch sides</button>
|
<button class="button" name="cmd" value="/switchsides">
|
||||||
|
<i class="fa fa-random"></i> Switch sides
|
||||||
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
@ -392,7 +415,7 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
||||||
return <button disabled> </button>;
|
return <button disabled> </button>;
|
||||||
}
|
}
|
||||||
const tooltip = `zmove|${moveData.name}|${pokemonIndex}`;
|
const tooltip = `zmove|${moveData.name}|${pokemonIndex}`;
|
||||||
return <MoveButton cmd={`/move ${i + 1} zmove`} type={move.type} tooltip={tooltip} moveData={{pp: 1, maxpp: 1}}>
|
return <MoveButton cmd={`/move ${i + 1} zmove`} type={move.type} tooltip={tooltip} moveData={{ pp: 1, maxpp: 1 }}>
|
||||||
{zMoveData.name}
|
{zMoveData.name}
|
||||||
</MoveButton>;
|
</MoveButton>;
|
||||||
});
|
});
|
||||||
|
|
@ -475,7 +498,7 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
||||||
<div class="switchmenu">
|
<div class="switchmenu">
|
||||||
{team.map((serverPokemon, i) => {
|
{team.map((serverPokemon, i) => {
|
||||||
return <PokemonButton
|
return <PokemonButton
|
||||||
pokemon={serverPokemon} cmd={``} noHPBar disabled={true} tooltip={`switchpokemon|${i}`}
|
pokemon={serverPokemon} cmd="" noHPBar disabled tooltip={`switchpokemon|${i}`}
|
||||||
/>;
|
/>;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -660,15 +683,17 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
||||||
return <div class="controls">
|
return <div class="controls">
|
||||||
<div class="whatdo">
|
<div class="whatdo">
|
||||||
<button name="openTimer" class="button disabled timerbutton"><i class="fa fa-hourglass-start"></i> Timer</button>
|
<button name="openTimer" class="button disabled timerbutton"><i class="fa fa-hourglass-start"></i> Timer</button>
|
||||||
{choices.alreadySwitchingIn.length > 0 ?
|
{choices.alreadySwitchingIn.length > 0 ? (
|
||||||
[<button name="cmd" value="/cancel" class="button"><i class="fa fa-chevron-left"></i> Back</button>,
|
[<button name="cmd" value="/cancel" class="button"><i class="fa fa-chevron-left"></i> Back</button>,
|
||||||
" What about the rest of your team? "]
|
" What about the rest of your team? "]
|
||||||
:
|
) : (
|
||||||
"How will you start the battle? "
|
"How will you start the battle? "
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="switchcontrols">
|
<div class="switchcontrols">
|
||||||
<h3 class="switchselect">Choose {choices.alreadySwitchingIn.length <= 0 ? `lead` : `slot ${choices.alreadySwitchingIn.length + 1}`}</h3>
|
<h3 class="switchselect">
|
||||||
|
Choose {choices.alreadySwitchingIn.length <= 0 ? `lead` : `slot ${choices.alreadySwitchingIn.length + 1}`}
|
||||||
|
</h3>
|
||||||
<div class="switchmenu">
|
<div class="switchmenu">
|
||||||
{this.renderTeamControls(request, choices)}
|
{this.renderTeamControls(request, choices)}
|
||||||
<div style="clear:left"></div>
|
<div style="clear:left"></div>
|
||||||
|
|
@ -681,7 +706,8 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
override render() {
|
override render() {
|
||||||
|
|
@ -689,7 +715,9 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
||||||
|
|
||||||
return <PSPanelWrapper room={room}>
|
return <PSPanelWrapper room={room}>
|
||||||
<BattleDiv></BattleDiv>
|
<BattleDiv></BattleDiv>
|
||||||
<ChatLog class="battle-log hasuserlist" room={this.props.room} onClick={this.focusIfNoSelection} left={640} noSubscription>
|
<ChatLog
|
||||||
|
class="battle-log hasuserlist" room={this.props.room} onClick={this.focusIfNoSelection} left={640} noSubscription
|
||||||
|
>
|
||||||
{}
|
{}
|
||||||
</ChatLog>
|
</ChatLog>
|
||||||
<ChatTextEntry room={this.props.room} onMessage={this.send} onKey={this.onKey} left={640} />
|
<ChatTextEntry room={this.props.room} onMessage={this.send} onKey={this.onKey} left={640} />
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,20 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import preact from "../js/lib/preact";
|
import preact from "../js/lib/preact";
|
||||||
import type {PSSubscription} from "./client-core";
|
import type { PSSubscription } from "./client-core";
|
||||||
import {PS, PSRoom, type RoomOptions, type RoomID, type Team} from "./client-main";
|
import { PS, PSRoom, type RoomOptions, type RoomID, type Team } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
import {TeamForm} from "./panel-mainmenu";
|
import { TeamForm } from "./panel-mainmenu";
|
||||||
import {BattleLog} from "./battle-log";
|
import { BattleLog } from "./battle-log";
|
||||||
import type {Battle} from "./battle";
|
import type { Battle } from "./battle";
|
||||||
import {MiniEdit} from "./miniedit";
|
import { MiniEdit } from "./miniedit";
|
||||||
import {PSUtils, toID, type ID} from "./battle-dex";
|
import { PSUtils, toID, type ID } from "./battle-dex";
|
||||||
|
|
||||||
declare const formatText: any;
|
declare const formatText: any; // from js/server/chat-formatter.js
|
||||||
|
|
||||||
export class ChatRoom extends PSRoom {
|
export class ChatRoom extends PSRoom {
|
||||||
override readonly classType: 'chat' | 'battle' = 'chat';
|
override readonly classType: 'chat' | 'battle' = 'chat';
|
||||||
users: {[userid: string]: string} = {};
|
users: { [userid: string]: string } = {};
|
||||||
userCount = 0;
|
userCount = 0;
|
||||||
override readonly canConnect = true;
|
override readonly canConnect = true;
|
||||||
|
|
||||||
|
|
@ -92,7 +92,8 @@ export class ChatRoom extends PSRoom {
|
||||||
this.challengedFormat = null;
|
this.challengedFormat = null;
|
||||||
this.update(null);
|
this.update(null);
|
||||||
return false;
|
return false;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
return super.handleMessage(line);
|
return super.handleMessage(line);
|
||||||
}
|
}
|
||||||
openChallenge() {
|
openChallenge() {
|
||||||
|
|
@ -219,7 +220,7 @@ export class ChatTextEntry extends preact.Component<{
|
||||||
getValue() {
|
getValue() {
|
||||||
return this.miniedit ? this.miniedit.getValue() : this.textbox.value;
|
return this.miniedit ? this.miniedit.getValue() : this.textbox.value;
|
||||||
}
|
}
|
||||||
setValue(value: string, selection?: {start: number, end: number}) {
|
setValue(value: string, selection?: { start: number, end: number }) {
|
||||||
if (this.miniedit) {
|
if (this.miniedit) {
|
||||||
this.miniedit.setValue(value, selection);
|
this.miniedit.setValue(value, selection);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -256,7 +257,7 @@ export class ChatTextEntry extends preact.Component<{
|
||||||
}
|
}
|
||||||
handleKey(ev: KeyboardEvent) {
|
handleKey(ev: KeyboardEvent) {
|
||||||
const cmdKey = ((ev.metaKey ? 1 : 0) + (ev.ctrlKey ? 1 : 0) === 1) && !ev.altKey && !ev.shiftKey;
|
const cmdKey = ((ev.metaKey ? 1 : 0) + (ev.ctrlKey ? 1 : 0) === 1) && !ev.altKey && !ev.shiftKey;
|
||||||
const anyModifier = ev.ctrlKey || ev.altKey || ev.metaKey || ev.shiftKey;
|
// const anyModifier = ev.ctrlKey || ev.altKey || ev.metaKey || ev.shiftKey;
|
||||||
if (ev.keyCode === 13 && !ev.shiftKey) { // Enter key
|
if (ev.keyCode === 13 && !ev.shiftKey) { // Enter key
|
||||||
return this.submit();
|
return this.submit();
|
||||||
} else if (ev.keyCode === 13 && this.miniedit) { // enter
|
} else if (ev.keyCode === 13 && this.miniedit) { // enter
|
||||||
|
|
@ -285,19 +286,19 @@ export class ChatTextEntry extends preact.Component<{
|
||||||
}
|
}
|
||||||
getSelection() {
|
getSelection() {
|
||||||
return this.miniedit ?
|
return this.miniedit ?
|
||||||
(this.miniedit.getSelection() || {start: 0, end: 0}) :
|
(this.miniedit.getSelection() || { start: 0, end: 0 }) :
|
||||||
{start: this.textbox.selectionStart, end: this.textbox.selectionEnd};
|
{ start: this.textbox.selectionStart, end: this.textbox.selectionEnd };
|
||||||
}
|
}
|
||||||
setSelection(start: number, end: number) {
|
setSelection(start: number, end: number) {
|
||||||
if (this.miniedit) {
|
if (this.miniedit) {
|
||||||
this.miniedit.setSelection({start, end});
|
this.miniedit.setSelection({ start, end });
|
||||||
} else {
|
} else {
|
||||||
this.textbox.setSelectionRange?.(start, end);
|
this.textbox.setSelectionRange?.(start, end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toggleFormatChar(formatChar: string) {
|
toggleFormatChar(formatChar: string) {
|
||||||
let value = this.getValue();
|
let value = this.getValue();
|
||||||
let {start, end} = this.getSelection();
|
let { start, end } = this.getSelection();
|
||||||
|
|
||||||
// make sure start and end aren't midway through the syntax
|
// make sure start and end aren't midway through the syntax
|
||||||
if (value.charAt(start) === formatChar && value.charAt(start - 1) === formatChar &&
|
if (value.charAt(start) === formatChar && value.charAt(start - 1) === formatChar &&
|
||||||
|
|
@ -333,23 +334,23 @@ export class ChatTextEntry extends preact.Component<{
|
||||||
end -= 2;
|
end -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setValue(value, {start, end});
|
this.setValue(value, { start, end });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
override render() {
|
override render() {
|
||||||
const OLD_TEXTBOX = false;
|
const OLD_TEXTBOX = false;
|
||||||
return <div
|
return <div
|
||||||
class="chat-log-add hasuserlist" onClick={this.focusIfNoSelection} style={{left: this.props.left || 0}}
|
class="chat-log-add hasuserlist" onClick={this.focusIfNoSelection} style={{ left: this.props.left || 0 }}
|
||||||
>
|
>
|
||||||
<form class="chatbox">
|
<form class="chatbox">
|
||||||
<label style={{color: BattleLog.usernameColor(PS.user.userid)}}>{PS.user.name}:</label>
|
<label style={{ color: BattleLog.usernameColor(PS.user.userid) }}>{PS.user.name}:</label>
|
||||||
{OLD_TEXTBOX ? <textarea
|
{OLD_TEXTBOX ? <textarea
|
||||||
class={this.props.room.connected ? 'textbox' : 'textbox disabled'}
|
class={this.props.room.connected ? 'textbox' : 'textbox disabled'}
|
||||||
autofocus
|
autofocus
|
||||||
rows={1}
|
rows={1}
|
||||||
onInput={this.update}
|
onInput={this.update}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
style={{resize: 'none', width: '100%', height: '16px', padding: '2px 3px 1px 3px'}}
|
style={{ resize: 'none', width: '100%', height: '16px', padding: '2px 3px 1px 3px' }}
|
||||||
placeholder={PS.focusPreview(this.props.room)}
|
placeholder={PS.focusPreview(this.props.room)}
|
||||||
/> : <ChatTextBox
|
/> : <ChatTextBox
|
||||||
class={this.props.room.connected ? 'textbox' : 'textbox disabled'}
|
class={this.props.room.connected ? 'textbox' : 'textbox disabled'}
|
||||||
|
|
@ -360,7 +361,7 @@ export class ChatTextEntry extends preact.Component<{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatTextBox extends preact.Component<{placeholder: string, class: string}> {
|
class ChatTextBox extends preact.Component<{ placeholder: string, class: string }> {
|
||||||
override shouldComponentUpdate() {
|
override shouldComponentUpdate() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -380,7 +381,7 @@ class ChatPanel extends PSRoomPanel<ChatRoom> {
|
||||||
// `display: none` element, but contentEditable boxes are pickier.
|
// `display: none` element, but contentEditable boxes are pickier.
|
||||||
// Waiting for a 0 timeout turns out to be enough.
|
// Waiting for a 0 timeout turns out to be enough.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
(this.base!.querySelector('textarea, pre.textbox') as HTMLElement).focus();
|
this.base!.querySelector<HTMLElement>('textarea, pre.textbox')!.focus();
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
focusIfNoSelection = () => {
|
focusIfNoSelection = () => {
|
||||||
|
|
@ -452,13 +453,13 @@ class ChatPanel extends PSRoomPanel<ChatRoom> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChatUserList extends preact.Component<{room: ChatRoom, left?: number, minimized?: boolean}> {
|
export class ChatUserList extends preact.Component<{ room: ChatRoom, left?: number, minimized?: boolean }> {
|
||||||
subscription: PSSubscription | null = null;
|
subscription: PSSubscription | null = null;
|
||||||
override state = {
|
override state = {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
};
|
};
|
||||||
toggleExpanded = () => {
|
toggleExpanded = () => {
|
||||||
this.setState({expanded: !this.state.expanded});
|
this.setState({ expanded: !this.state.expanded });
|
||||||
};
|
};
|
||||||
override componentDidMount() {
|
override componentDidMount() {
|
||||||
this.subscription = this.props.room.subscribe(msg => {
|
this.subscription = this.props.room.subscribe(msg => {
|
||||||
|
|
@ -474,11 +475,14 @@ export class ChatUserList extends preact.Component<{room: ChatRoom, left?: numbe
|
||||||
PSUtils.sortBy(userList, ([id, name]) => (
|
PSUtils.sortBy(userList, ([id, name]) => (
|
||||||
[PS.server.getGroup(name.charAt(0)).order, !name.endsWith('@!'), id]
|
[PS.server.getGroup(name.charAt(0)).order, !name.endsWith('@!'), id]
|
||||||
));
|
));
|
||||||
return <ul class={'userlist' + (this.props.minimized ? (this.state.expanded ? ' userlist-maximized' : ' userlist-minimized') : '')} style={{left: this.props.left || 0}}>
|
return <ul
|
||||||
|
class={'userlist' + (this.props.minimized ? (this.state.expanded ? ' userlist-maximized' : ' userlist-minimized') : '')}
|
||||||
|
style={{ left: this.props.left || 0 }}
|
||||||
|
>
|
||||||
<li class="userlist-count" onClick={this.toggleExpanded}><small>{room.userCount} users</small></li>
|
<li class="userlist-count" onClick={this.toggleExpanded}><small>{room.userCount} users</small></li>
|
||||||
{userList.map(([userid, name]) => {
|
{userList.map(([userid, name]) => {
|
||||||
const groupSymbol = name.charAt(0);
|
const groupSymbol = name.charAt(0);
|
||||||
const group = PS.server.groups[groupSymbol] || {type: 'user', order: 0};
|
const group = PS.server.groups[groupSymbol] || { type: 'user', order: 0 };
|
||||||
let color;
|
let color;
|
||||||
if (name.endsWith('@!')) {
|
if (name.endsWith('@!')) {
|
||||||
name = name.slice(0, -2);
|
name = name.slice(0, -2);
|
||||||
|
|
@ -490,13 +494,13 @@ export class ChatUserList extends preact.Component<{room: ChatRoom, left?: numbe
|
||||||
<em class={`group${['leadership', 'staff'].includes(group.type!) ? ' staffgroup' : ''}`}>
|
<em class={`group${['leadership', 'staff'].includes(group.type!) ? ' staffgroup' : ''}`}>
|
||||||
{groupSymbol}
|
{groupSymbol}
|
||||||
</em>
|
</em>
|
||||||
{group.type === 'leadership' ?
|
{group.type === 'leadership' ? (
|
||||||
<strong><em style={{color}}>{name.substr(1)}</em></strong>
|
<strong><em style={{ color }}>{name.substr(1)}</em></strong>
|
||||||
: group.type === 'staff' ?
|
) : group.type === 'staff' ? (
|
||||||
<strong style={{color}}>{name.substr(1)}</strong>
|
<strong style={{ color }}>{name.substr(1)}</strong>
|
||||||
:
|
) : (
|
||||||
<span style={{color}}>{name.substr(1)}</span>
|
<span style={{ color }}>{name.substr(1)}</span>
|
||||||
}
|
)}
|
||||||
</button></li>;
|
</button></li>;
|
||||||
})}
|
})}
|
||||||
</ul>;
|
</ul>;
|
||||||
|
|
@ -505,7 +509,7 @@ export class ChatUserList extends preact.Component<{room: ChatRoom, left?: numbe
|
||||||
|
|
||||||
export class ChatLog extends preact.Component<{
|
export class ChatLog extends preact.Component<{
|
||||||
class: string, room: ChatRoom, onClick?: (e: Event) => void, children?: preact.ComponentChildren,
|
class: string, room: ChatRoom, onClick?: (e: Event) => void, children?: preact.ComponentChildren,
|
||||||
left?: number, top?: number, noSubscription?: boolean;
|
left?: number, top?: number, noSubscription?: boolean,
|
||||||
}> {
|
}> {
|
||||||
log: BattleLog | null = null;
|
log: BattleLog | null = null;
|
||||||
subscription: PSSubscription | null = null;
|
subscription: PSSubscription | null = null;
|
||||||
|
|
@ -575,9 +579,10 @@ export class ChatLog extends preact.Component<{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
return <div class={this.props.class} role="log" onClick={this.props.onClick} style={{
|
return <div
|
||||||
left: this.props.left || 0, top: this.props.top || 0,
|
class={this.props.class} role="log" onClick={this.props.onClick}
|
||||||
}}></div>;
|
style={{ left: this.props.left || 0, top: this.props.top || 0 }}
|
||||||
|
></div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,14 @@
|
||||||
* @license AGPLv3
|
* @license AGPLv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PS, PSRoom, type RoomOptions} from "./client-main";
|
import { PS, PSRoom, type RoomOptions } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
|
|
||||||
// Example room with panel
|
// Example room with panel
|
||||||
|
|
||||||
class ExampleRoom extends PSRoom {
|
class ExampleRoom extends PSRoom {
|
||||||
override readonly classType: string = 'example';
|
override readonly classType: string = 'example';
|
||||||
|
// eslint-disable-next-line no-useless-constructor
|
||||||
constructor(options: RoomOptions) {
|
constructor(options: RoomOptions) {
|
||||||
super(options);
|
super(options);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import preact from "../js/lib/preact";
|
import preact from "../js/lib/preact";
|
||||||
import {PS, PSRoom} from "./client-main";
|
import { PS, PSRoom } from "./client-main";
|
||||||
import {Net} from "./client-connection";
|
import { Net } from "./client-connection";
|
||||||
import {PSPanelWrapper, PSRoomPanel, SanitizedHTML} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel, SanitizedHTML } from "./panels";
|
||||||
import {BattleLog} from "./battle-log";
|
import { BattleLog } from "./battle-log";
|
||||||
import {toID} from "./battle-dex";
|
import { toID } from "./battle-dex";
|
||||||
|
|
||||||
export class LadderRoom extends PSRoom {
|
export class LadderRoom extends PSRoom {
|
||||||
override readonly classType: string = 'ladder';
|
override readonly classType: string = 'ladder';
|
||||||
readonly format?: string = this.id.split('-')[1];
|
readonly format?: string = this.id.split('-')[1];
|
||||||
notice?: string;
|
notice?: string;
|
||||||
searchValue: string = '';
|
searchValue = '';
|
||||||
lastSearch: string = '';
|
lastSearch = '';
|
||||||
loading: boolean = false;
|
loading = false;
|
||||||
error?: string;
|
error?: string;
|
||||||
ladderData?: string;
|
ladderData?: string;
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ export class LadderRoom extends PSRoom {
|
||||||
requestLadderData = (searchValue?: string) => {
|
requestLadderData = (searchValue?: string) => {
|
||||||
const { teams } = PS;
|
const { teams } = PS;
|
||||||
if (teams.usesLocalLadder) {
|
if (teams.usesLocalLadder) {
|
||||||
this.send(`/cmd laddertop ${this.format} ${toID(this.searchValue)}`);
|
this.send(`/cmd laddertop ${this.format!} ${toID(this.searchValue)}`);
|
||||||
} else if (this.format !== undefined) {
|
} else if (this.format !== undefined) {
|
||||||
Net('/ladder.php')
|
Net('/ladder.php')
|
||||||
.get({
|
.get({
|
||||||
|
|
@ -71,8 +71,8 @@ export class LadderRoom extends PSRoom {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function LadderFormat(props: {room: LadderRoom}) {
|
function LadderFormat(props: { room: LadderRoom }) {
|
||||||
const {room} = props;
|
const { room } = props;
|
||||||
const {
|
const {
|
||||||
format, searchValue, lastSearch, loading, error, ladderData,
|
format, searchValue, lastSearch, loading, error, ladderData,
|
||||||
setSearchValue, setLastSearch, requestLadderData,
|
setSearchValue, setLastSearch, requestLadderData,
|
||||||
|
|
@ -124,18 +124,18 @@ function LadderFormat(props: {room: LadderRoom}) {
|
||||||
}
|
}
|
||||||
return <>
|
return <>
|
||||||
<p>
|
<p>
|
||||||
<button class="button" data-href="ladder" data-target="replace" >
|
<button class="button" data-href="ladder" data-target="replace">
|
||||||
<i class="fa fa-refresh"></i> Refresh
|
<i class="fa fa-refresh"></i> Refresh
|
||||||
</button>
|
</button>
|
||||||
<RenderSearch/>
|
<RenderSearch />
|
||||||
</p>
|
</p>
|
||||||
<RenderHeader/>
|
<RenderHeader />
|
||||||
<SanitizedHTML>{ladderData}</SanitizedHTML>
|
<SanitizedHTML>{ladderData}</SanitizedHTML>
|
||||||
</>;
|
</>;
|
||||||
};
|
};
|
||||||
return <div class="ladder pad">
|
return <div class="ladder pad">
|
||||||
<p>
|
<p>
|
||||||
<button class="button" data-href="ladder" data-target="replace">
|
<button class="button" data-href="ladder" data-target="replace">
|
||||||
<i class="fa fa-chevron-left"></i> Format List
|
<i class="fa fa-chevron-left"></i> Format List
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -145,7 +145,7 @@ function LadderFormat(props: {room: LadderRoom}) {
|
||||||
|
|
||||||
class LadderPanel extends PSRoomPanel<LadderRoom> {
|
class LadderPanel extends PSRoomPanel<LadderRoom> {
|
||||||
override componentDidMount() {
|
override componentDidMount() {
|
||||||
const {room} = this.props;
|
const { room } = this.props;
|
||||||
// Request ladder data either on mount or after BattleFormats are loaded
|
// Request ladder data either on mount or after BattleFormats are loaded
|
||||||
if (BattleFormats && room.format !== undefined) room.requestLadderData();
|
if (BattleFormats && room.format !== undefined) room.requestLadderData();
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
|
|
@ -170,8 +170,8 @@ class LadderPanel extends PSRoomPanel<LadderRoom> {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
static Notice = (props: {notice: string | undefined}) => {
|
static Notice = (props: { notice: string | undefined }) => {
|
||||||
const {notice} = props;
|
const { notice } = props;
|
||||||
if (notice) {
|
if (notice) {
|
||||||
return (
|
return (
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -185,7 +185,7 @@ class LadderPanel extends PSRoomPanel<LadderRoom> {
|
||||||
if (!BattleFormats) {
|
if (!BattleFormats) {
|
||||||
return <p>Loading...</p>;
|
return <p>Loading...</p>;
|
||||||
}
|
}
|
||||||
let currentSection: string = "";
|
let currentSection = "";
|
||||||
let sections: JSX.Element[] = [];
|
let sections: JSX.Element[] = [];
|
||||||
let formats: JSX.Element[] = [];
|
let formats: JSX.Element[] = [];
|
||||||
for (const [key, format] of Object.entries(BattleFormats)) {
|
for (const [key, format] of Object.entries(BattleFormats)) {
|
||||||
|
|
@ -217,8 +217,8 @@ class LadderPanel extends PSRoomPanel<LadderRoom> {
|
||||||
}
|
}
|
||||||
return <>{sections}</>;
|
return <>{sections}</>;
|
||||||
};
|
};
|
||||||
static ShowFormatList = (props: {room: LadderRoom}) => {
|
static ShowFormatList = (props: { room: LadderRoom }) => {
|
||||||
const {room} = props;
|
const { room } = props;
|
||||||
return <>
|
return <>
|
||||||
<p>
|
<p>
|
||||||
<a class="button" href={`/${Config.routes.users}/`} target="_blank">
|
<a class="button" href={`/${Config.routes.users}/`} target="_blank">
|
||||||
|
|
@ -235,7 +235,7 @@ class LadderPanel extends PSRoomPanel<LadderRoom> {
|
||||||
</>;
|
</>;
|
||||||
};
|
};
|
||||||
override render() {
|
override render() {
|
||||||
const {room} = this.props;
|
const { room } = this.props;
|
||||||
return <PSPanelWrapper room={room} scrollable>
|
return <PSPanelWrapper room={room} scrollable>
|
||||||
<div class="ladder pad">
|
<div class="ladder pad">
|
||||||
{room.format === undefined && (
|
{room.format === undefined && (
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import preact from "../js/lib/preact";
|
import preact from "../js/lib/preact";
|
||||||
import {PSLoginServer} from "./client-connection";
|
import { PSLoginServer } from "./client-connection";
|
||||||
import {PS, PSRoom, type RoomID, type Team} from "./client-main";
|
import { PS, PSRoom, type RoomID, type Team } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
import type {BattlesRoom} from "./panel-battle";
|
import type { BattlesRoom } from "./panel-battle";
|
||||||
import type {ChatRoom} from "./panel-chat";
|
import type { ChatRoom } from "./panel-chat";
|
||||||
import type {LadderRoom} from "./panel-ladder";
|
import type { LadderRoom } from "./panel-ladder";
|
||||||
import type {RoomsRoom} from "./panel-rooms";
|
import type { RoomsRoom } from "./panel-rooms";
|
||||||
import {TeamBox} from "./panel-teamdropdown";
|
import { TeamBox } from "./panel-teamdropdown";
|
||||||
import type {UserRoom} from "./panel-topbar";
|
import type { UserRoom } from "./panel-topbar";
|
||||||
import {Dex, toID, type ID} from "./battle-dex";
|
import { Dex, toID, type ID } from "./battle-dex";
|
||||||
import type {Args} from "./battle-text-parser";
|
import type { Args } from "./battle-text-parser";
|
||||||
|
|
||||||
export type RoomInfo = {
|
export type RoomInfo = {
|
||||||
title: string, desc?: string, userCount?: number, section?: string, spotlight?: string, subRooms?: string[],
|
title: string, desc?: string, userCount?: number, section?: string, spotlight?: string, subRooms?: string[],
|
||||||
|
|
@ -24,14 +24,16 @@ export type RoomInfo = {
|
||||||
|
|
||||||
export class MainMenuRoom extends PSRoom {
|
export class MainMenuRoom extends PSRoom {
|
||||||
override readonly classType: string = 'mainmenu';
|
override readonly classType: string = 'mainmenu';
|
||||||
userdetailsCache: {[userid: string]: {
|
userdetailsCache: {
|
||||||
userid: ID,
|
[userid: string]: {
|
||||||
avatar?: string | number,
|
userid: ID,
|
||||||
status?: string,
|
avatar?: string | number,
|
||||||
group?: string,
|
status?: string,
|
||||||
customgroup?: string,
|
group?: string,
|
||||||
rooms?: {[roomid: string]: {isPrivate?: true, p1?: string, p2?: string}},
|
customgroup?: string,
|
||||||
}} = {};
|
rooms?: { [roomid: string]: { isPrivate?: true, p1?: string, p2?: string } },
|
||||||
|
},
|
||||||
|
} = {};
|
||||||
roomsCache: {
|
roomsCache: {
|
||||||
battleCount?: number,
|
battleCount?: number,
|
||||||
userCount?: number,
|
userCount?: number,
|
||||||
|
|
@ -75,7 +77,8 @@ export class MainMenuRoom extends PSRoom {
|
||||||
const [, message] = args;
|
const [, message] = args;
|
||||||
alert(message.replace(/\|\|/g, '\n'));
|
alert(message.replace(/\|\|/g, '\n'));
|
||||||
return;
|
return;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
const lobby = PS.rooms['lobby'];
|
const lobby = PS.rooms['lobby'];
|
||||||
if (lobby) lobby.receiveLine(args);
|
if (lobby) lobby.receiveLine(args);
|
||||||
}
|
}
|
||||||
|
|
@ -109,9 +112,9 @@ export class MainMenuRoom extends PSRoom {
|
||||||
|
|
||||||
let column = 0;
|
let column = 0;
|
||||||
|
|
||||||
window.NonBattleGames = {rps: 'Rock Paper Scissors'};
|
window.NonBattleGames = { rps: 'Rock Paper Scissors' };
|
||||||
for (let i = 3; i <= 9; i = i + 2) {
|
for (let i = 3; i <= 9; i += 2) {
|
||||||
window.NonBattleGames['bestof' + i] = 'Best-of-' + i;
|
window.NonBattleGames[`bestof${i}`] = `Best-of-${i}`;
|
||||||
}
|
}
|
||||||
window.BattleFormats = {};
|
window.BattleFormats = {};
|
||||||
for (let j = 1; j < formatsList.length; j++) {
|
for (let j = 1; j < formatsList.length; j++) {
|
||||||
|
|
@ -121,7 +124,7 @@ export class MainMenuRoom extends PSRoom {
|
||||||
isSection = false;
|
isSection = false;
|
||||||
} else if (entry === ',LL') {
|
} else if (entry === ',LL') {
|
||||||
PS.teams.usesLocalLadder = true;
|
PS.teams.usesLocalLadder = true;
|
||||||
} else if (entry === '' || (entry.charAt(0) === ',' && !isNaN(Number(entry.slice(1))))) {
|
} else if (entry === '' || (entry.startsWith(',') && !isNaN(Number(entry.slice(1))))) {
|
||||||
isSection = true;
|
isSection = true;
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
|
|
@ -158,16 +161,16 @@ export class MainMenuRoom extends PSRoom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let id = toID(name);
|
let id = toID(name);
|
||||||
let isTeambuilderFormat = !team && name.slice(-11) !== 'Custom Game';
|
let isTeambuilderFormat = !team && !name.endsWith('Custom Game');
|
||||||
let teambuilderFormat = '' as ID;
|
let teambuilderFormat = '' as ID;
|
||||||
let teambuilderFormatName = '';
|
let teambuilderFormatName = '';
|
||||||
if (isTeambuilderFormat) {
|
if (isTeambuilderFormat) {
|
||||||
teambuilderFormatName = name;
|
teambuilderFormatName = name;
|
||||||
if (id.slice(0, 3) !== 'gen') {
|
if (!id.startsWith('gen')) {
|
||||||
teambuilderFormatName = '[Gen 6] ' + name;
|
teambuilderFormatName = '[Gen 6] ' + name;
|
||||||
}
|
}
|
||||||
let parenPos = teambuilderFormatName.indexOf('(');
|
let parenPos = teambuilderFormatName.indexOf('(');
|
||||||
if (parenPos > 0 && name.slice(-1) === ')') {
|
if (parenPos > 0 && name.endsWith(')')) {
|
||||||
// variation of existing tier
|
// variation of existing tier
|
||||||
teambuilderFormatName = teambuilderFormatName.slice(0, parenPos).trim();
|
teambuilderFormatName = teambuilderFormatName.slice(0, parenPos).trim();
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +217,7 @@ export class MainMenuRoom extends PSRoom {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match base formats to their variants, if they are unavailable in the server.
|
// Match base formats to their variants, if they are unavailable in the server.
|
||||||
let multivariantFormats: {[id: string]: 1} = {};
|
let multivariantFormats: { [id: string]: 1 } = {};
|
||||||
for (let id in BattleFormats) {
|
for (let id in BattleFormats) {
|
||||||
let teambuilderFormat = BattleFormats[BattleFormats[id].teambuilderFormat!];
|
let teambuilderFormat = BattleFormats[BattleFormats[id].teambuilderFormat!];
|
||||||
if (!teambuilderFormat || multivariantFormats[teambuilderFormat.id]) continue;
|
if (!teambuilderFormat || multivariantFormats[teambuilderFormat.id]) continue;
|
||||||
|
|
@ -300,21 +303,21 @@ export class MainMenuRoom extends PSRoom {
|
||||||
class NewsPanel extends PSRoomPanel {
|
class NewsPanel extends PSRoomPanel {
|
||||||
override render() {
|
override render() {
|
||||||
return <PSPanelWrapper room={this.props.room} scrollable>
|
return <PSPanelWrapper room={this.props.room} scrollable>
|
||||||
<div class="mini-window-body" dangerouslySetInnerHTML={{__html: PS.newsHTML}}></div>
|
<div class="mini-window-body" dangerouslySetInnerHTML={{ __html: PS.newsHTML }}></div>
|
||||||
</PSPanelWrapper>;
|
</PSPanelWrapper>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
||||||
override focus() {
|
override focus() {
|
||||||
(this.base!.querySelector('button.big') as HTMLButtonElement).focus();
|
this.base!.querySelector<HTMLButtonElement>('button.big')!.focus();
|
||||||
}
|
}
|
||||||
submit = (e: Event) => {
|
submit = (e: Event) => {
|
||||||
alert('todo: implement');
|
alert('todo: implement');
|
||||||
};
|
};
|
||||||
handleDragStart = (e: DragEvent) => {
|
handleDragStart = (e: DragEvent) => {
|
||||||
const roomid = (e.currentTarget as HTMLElement).getAttribute('data-roomid') as RoomID;
|
const roomid = (e.currentTarget as HTMLElement).getAttribute('data-roomid') as RoomID;
|
||||||
PS.dragging = {type: 'room', roomid};
|
PS.dragging = { type: 'room', roomid };
|
||||||
};
|
};
|
||||||
renderMiniRoom(room: PSRoom) {
|
renderMiniRoom(room: PSRoom) {
|
||||||
const roomType = PS.roomTypes[room.type];
|
const roomType = PS.roomTypes[room.type];
|
||||||
|
|
@ -327,7 +330,9 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
||||||
return <div class="pmbox">
|
return <div class="pmbox">
|
||||||
<div class="mini-window">
|
<div class="mini-window">
|
||||||
<h3 draggable onDragStart={this.handleDragStart} data-roomid={roomid}>
|
<h3 draggable onDragStart={this.handleDragStart} data-roomid={roomid}>
|
||||||
<button class="closebutton" name="closeRoom" value={roomid} aria-label="Close" tabIndex={-1}><i class="fa fa-times-circle"></i></button>
|
<button class="closebutton" name="closeRoom" value={roomid} aria-label="Close" tabIndex={-1}>
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
</button>
|
||||||
<button class="minimizebutton" tabIndex={-1}><i class="fa fa-minus-circle"></i></button>
|
<button class="minimizebutton" tabIndex={-1}><i class="fa fa-minus-circle"></i></button>
|
||||||
{room.title}
|
{room.title}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
@ -339,13 +344,13 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
||||||
renderSearchButton() {
|
renderSearchButton() {
|
||||||
if (PS.down) {
|
if (PS.down) {
|
||||||
return <div class="menugroup" style="background: rgba(10,10,10,.6)">
|
return <div class="menugroup" style="background: rgba(10,10,10,.6)">
|
||||||
{PS.down === 'ddos' ?
|
{PS.down === 'ddos' ? (
|
||||||
<p class="error"><strong>Pokémon Showdown is offline due to a DDoS attack!</strong></p>
|
<p class="error"><strong>Pokémon Showdown is offline due to a DDoS attack!</strong></p>
|
||||||
:
|
) : (
|
||||||
<p class="error"><strong>Pokémon Showdown is offline due to technical difficulties!</strong></p>
|
<p class="error"><strong>Pokémon Showdown is offline due to technical difficulties!</strong></p>
|
||||||
}
|
)}
|
||||||
<p>
|
<p>
|
||||||
<div style={{textAlign: 'center'}}>
|
<div style={{ textAlign: 'center' }}>
|
||||||
<img width="96" height="96" src={`//${Config.routes.client}/sprites/gen5/teddiursa.png`} alt="" />
|
<img width="96" height="96" src={`//${Config.routes.client}/sprites/gen5/teddiursa.png`} alt="" />
|
||||||
</div>
|
</div>
|
||||||
Bear with us as we freak out.
|
Bear with us as we freak out.
|
||||||
|
|
@ -391,13 +396,13 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="rightmenu" style={{display: PS.leftRoomWidth ? 'none' : 'block'}}>
|
<div class="rightmenu" style={{ display: PS.leftRoomWidth ? 'none' : 'block' }}>
|
||||||
<div class="menugroup">
|
<div class="menugroup">
|
||||||
{PS.server.id === 'showdown' ?
|
{PS.server.id === 'showdown' ? (
|
||||||
<p><button class={"mainmenu1" + onlineButton} name="joinRoom" value="rooms">Join chat</button></p>
|
<p><button class={"mainmenu1" + onlineButton} name="joinRoom" value="rooms">Join chat</button></p>
|
||||||
:
|
) : (
|
||||||
<p><button class={"mainmenu1" + onlineButton} name="joinRoom" value="lobby">Join lobby chat</button></p>
|
<p><button class={"mainmenu1" + onlineButton} name="joinRoom" value="lobby">Join lobby chat</button></p>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mainmenufooter">
|
<div class="mainmenufooter">
|
||||||
|
|
@ -415,7 +420,7 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FormatDropdown extends preact.Component<{format?: string, onChange?: JSX.EventHandler<Event>}> {
|
export class FormatDropdown extends preact.Component<{ format?: string, onChange?: JSX.EventHandler<Event> }> {
|
||||||
declare base?: HTMLButtonElement;
|
declare base?: HTMLButtonElement;
|
||||||
format = '[Gen 7] Random Battle';
|
format = '[Gen 7] Random Battle';
|
||||||
change = (e: Event) => {
|
change = (e: Event) => {
|
||||||
|
|
@ -427,7 +432,7 @@ export class FormatDropdown extends preact.Component<{format?: string, onChange?
|
||||||
render() {
|
render() {
|
||||||
if (this.props.format) {
|
if (this.props.format) {
|
||||||
return <button
|
return <button
|
||||||
name="format" value={this.props.format} class="select formatselect preselected" disabled
|
name="format" value={this.props.format} class="select formatselect preselected" disabled
|
||||||
>{this.props.format}</button>;
|
>{this.props.format}</button>;
|
||||||
}
|
}
|
||||||
return <button
|
return <button
|
||||||
|
|
@ -439,7 +444,7 @@ export class FormatDropdown extends preact.Component<{format?: string, onChange?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TeamDropdown extends preact.Component<{format: string}> {
|
class TeamDropdown extends preact.Component<{ format: string }> {
|
||||||
teamFormat = '';
|
teamFormat = '';
|
||||||
teamKey = '';
|
teamKey = '';
|
||||||
change = () => {
|
change = () => {
|
||||||
|
|
@ -456,7 +461,7 @@ class TeamDropdown extends preact.Component<{format: string}> {
|
||||||
render() {
|
render() {
|
||||||
const teamFormat = PS.teams.teambuilderFormat(this.props.format);
|
const teamFormat = PS.teams.teambuilderFormat(this.props.format);
|
||||||
const formatData = window.BattleFormats?.[teamFormat];
|
const formatData = window.BattleFormats?.[teamFormat];
|
||||||
if (formatData && formatData.team) {
|
if (formatData?.team) {
|
||||||
return <button class="select teamselect preselected" name="team" value="random" disabled>
|
return <button class="select teamselect preselected" name="team" value="random" disabled>
|
||||||
<div class="team">
|
<div class="team">
|
||||||
<strong>Random team</strong>
|
<strong>Random team</strong>
|
||||||
|
|
@ -489,14 +494,14 @@ export class TeamForm extends preact.Component<{
|
||||||
children: preact.ComponentChildren, class?: string, format?: string,
|
children: preact.ComponentChildren, class?: string, format?: string,
|
||||||
onSubmit: null | ((e: Event, format: string, team?: Team) => void),
|
onSubmit: null | ((e: Event, format: string, team?: Team) => void),
|
||||||
}> {
|
}> {
|
||||||
override state = {format: '[Gen 7] Random Battle'};
|
override state = { format: '[Gen 7] Random Battle' };
|
||||||
changeFormat = (e: Event) => {
|
changeFormat = (e: Event) => {
|
||||||
this.setState({format: (e.target as HTMLButtonElement).value});
|
this.setState({ format: (e.target as HTMLButtonElement).value });
|
||||||
};
|
};
|
||||||
submit = (e: Event) => {
|
submit = (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const format = (this.base!.querySelector('button[name=format]') as HTMLButtonElement).value;
|
const format = this.base!.querySelector<HTMLButtonElement>('button[name=format]')!.value;
|
||||||
const teamKey = (this.base!.querySelector('button[name=team]') as HTMLButtonElement).value;
|
const teamKey = this.base!.querySelector<HTMLButtonElement>('button[name=team]')!.value;
|
||||||
const team = teamKey ? PS.teams.byKey[teamKey] : undefined;
|
const team = teamKey ? PS.teams.byKey[teamKey] : undefined;
|
||||||
if (this.props.onSubmit) this.props.onSubmit(e, format, team);
|
if (this.props.onSubmit) this.props.onSubmit(e, format, team);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,17 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PS, PSRoom, type RoomOptions} from "./client-main";
|
import { PS, PSRoom, type RoomOptions } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel, SanitizedHTML} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel, SanitizedHTML } from "./panels";
|
||||||
import {BattleLog} from "./battle-log";
|
import { BattleLog } from "./battle-log";
|
||||||
import type {Args} from "./battle-text-parser";
|
import type { Args } from "./battle-text-parser";
|
||||||
|
|
||||||
class PageRoom extends PSRoom {
|
class PageRoom extends PSRoom {
|
||||||
override readonly classType: string = 'html';
|
override readonly classType: string = 'html';
|
||||||
readonly page?: string = this.id.split("-")[1];
|
readonly page?: string = this.id.split("-")[1];
|
||||||
override readonly canConnect = true;
|
override readonly canConnect = true;
|
||||||
|
|
||||||
loading: boolean = true;
|
loading = true;
|
||||||
htmlData?: string;
|
htmlData?: string;
|
||||||
|
|
||||||
setHTMLData = (htmlData?: string) => {
|
setHTMLData = (htmlData?: string) => {
|
||||||
|
|
@ -39,8 +39,7 @@ class PageRoom extends PSRoom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function PageLadderHelp(props: {room: PageRoom}) {
|
function PageLadderHelp(props: { room: PageRoom }) {
|
||||||
const {room} = props;
|
|
||||||
return <div class="ladder pad">
|
return <div class="ladder pad">
|
||||||
<p>
|
<p>
|
||||||
<button name="selectFormat" data-href="ladder" data-target="replace">
|
<button name="selectFormat" data-href="ladder" data-target="replace">
|
||||||
|
|
@ -74,13 +73,13 @@ function PageLadderHelp(props: {room: PageRoom}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PagePanel extends PSRoomPanel<PageRoom> {
|
class PagePanel extends PSRoomPanel<PageRoom> {
|
||||||
clientRooms: {[key: string]: JSX.Element} = {'ladderhelp': <PageLadderHelp room={this.props.room}/>};
|
clientRooms: { [key: string]: JSX.Element } = { 'ladderhelp': <PageLadderHelp room={this.props.room} /> };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true to prevent line from being sent to server
|
* @return true to prevent line from being sent to server
|
||||||
*/
|
*/
|
||||||
override receiveLine(args: Args) {
|
override receiveLine(args: Args) {
|
||||||
const {room} = this.props;
|
const { room } = this.props;
|
||||||
switch (args[0]) {
|
switch (args[0]) {
|
||||||
case 'title':
|
case 'title':
|
||||||
room.title = args[1];
|
room.title = args[1];
|
||||||
|
|
@ -88,7 +87,7 @@ class PagePanel extends PSRoomPanel<PageRoom> {
|
||||||
return true;
|
return true;
|
||||||
case 'tempnotify': {
|
case 'tempnotify': {
|
||||||
const [, id, title, body] = args;
|
const [, id, title, body] = args;
|
||||||
room.notify({title, body, id});
|
room.notify({ title, body, id });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 'tempnotifyoff': {
|
case 'tempnotifyoff': {
|
||||||
|
|
@ -114,7 +113,7 @@ class PagePanel extends PSRoomPanel<PageRoom> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override render() {
|
override render() {
|
||||||
const {room} = this.props;
|
const { room } = this.props;
|
||||||
let renderPage;
|
let renderPage;
|
||||||
if (room.page !== undefined && this.clientRooms[room.page]) {
|
if (room.page !== undefined && this.clientRooms[room.page]) {
|
||||||
renderPage = this.clientRooms[room.page];
|
renderPage = this.clientRooms[room.page];
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
* @license AGPLv3
|
* @license AGPLv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PS, PSRoom, type RoomID, type RoomOptions} from "./client-main";
|
import { PS, PSRoom, type RoomID, type RoomOptions } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
import type {RoomInfo} from "./panel-mainmenu";
|
import type { RoomInfo } from "./panel-mainmenu";
|
||||||
import {toID} from "./battle-dex";
|
import { toID } from "./battle-dex";
|
||||||
|
|
||||||
export class RoomsRoom extends PSRoom {
|
export class RoomsRoom extends PSRoom {
|
||||||
override readonly classType: string = 'rooms';
|
override readonly classType: string = 'rooms';
|
||||||
|
|
@ -36,7 +36,7 @@ class RoomsPanel extends PSRoomPanel {
|
||||||
PS.update();
|
PS.update();
|
||||||
};
|
};
|
||||||
changeSearch = (e: Event) => {
|
changeSearch = (e: Event) => {
|
||||||
const target = (e.currentTarget as HTMLInputElement);
|
const target = e.currentTarget as HTMLInputElement;
|
||||||
if (target.selectionStart !== target.selectionEnd) return;
|
if (target.selectionStart !== target.selectionEnd) return;
|
||||||
this.search = target.value;
|
this.search = target.value;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
|
|
@ -44,7 +44,7 @@ class RoomsPanel extends PSRoomPanel {
|
||||||
keyDownSearch = (e: KeyboardEvent) => {
|
keyDownSearch = (e: KeyboardEvent) => {
|
||||||
this.lastKeyCode = e.keyCode;
|
this.lastKeyCode = e.keyCode;
|
||||||
if (e.keyCode === 13) {
|
if (e.keyCode === 13) {
|
||||||
const target = (e.currentTarget as HTMLInputElement);
|
const target = e.currentTarget as HTMLInputElement;
|
||||||
let value = target.value;
|
let value = target.value;
|
||||||
const arrowIndex = value.indexOf(' \u21d2 ');
|
const arrowIndex = value.indexOf(' \u21d2 ');
|
||||||
if (arrowIndex >= 0) value = value.slice(arrowIndex + 3);
|
if (arrowIndex >= 0) value = value.slice(arrowIndex + 3);
|
||||||
|
|
@ -86,7 +86,7 @@ class RoomsPanel extends PSRoomPanel {
|
||||||
room.title.replace(/[^A-Z0-9]+/g, '').toLowerCase().startsWith(searchid)
|
room.title.replace(/[^A-Z0-9]+/g, '').toLowerCase().startsWith(searchid)
|
||||||
);
|
);
|
||||||
|
|
||||||
const hidden = !exactMatch ? [{title: this.search, desc: "(Private room?)"}] : [];
|
const hidden = !exactMatch ? [{ title: this.search, desc: "(Private room?)" }] : [];
|
||||||
|
|
||||||
const autoFill = this.lastKeyCode !== 127 && this.lastKeyCode >= 32;
|
const autoFill = this.lastKeyCode !== 127 && this.lastKeyCode >= 32;
|
||||||
if (autoFill) {
|
if (autoFill) {
|
||||||
|
|
@ -103,15 +103,15 @@ class RoomsPanel extends PSRoomPanel {
|
||||||
autoFillValue = ' \u21d2 ' + firstTitle;
|
autoFillValue = ' \u21d2 ' + firstTitle;
|
||||||
}
|
}
|
||||||
const oldSearch = this.search;
|
const oldSearch = this.search;
|
||||||
const searchElem = this.base!.querySelector('input[type=search]') as HTMLInputElement;
|
const searchElem = this.base!.querySelector<HTMLInputElement>('input[type=search]')!;
|
||||||
searchElem.value = oldSearch + autoFillValue;
|
searchElem.value = oldSearch + autoFillValue;
|
||||||
searchElem.setSelectionRange(oldSearch.length, oldSearch.length + autoFillValue.length);
|
searchElem.setSelectionRange(oldSearch.length, oldSearch.length + autoFillValue.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {start, abbr, hidden};
|
return { start, abbr, hidden };
|
||||||
}
|
}
|
||||||
override focus() {
|
override focus() {
|
||||||
(this.base!.querySelector('input[type=search]') as HTMLInputElement).focus();
|
this.base!.querySelector<HTMLInputElement>('input[type=search]')!.focus();
|
||||||
}
|
}
|
||||||
override render() {
|
override render() {
|
||||||
if (this.hidden && PS.isVisible(this.props.room)) this.hidden = false;
|
if (this.hidden && PS.isVisible(this.props.room)) this.hidden = false;
|
||||||
|
|
@ -166,7 +166,7 @@ class RoomsPanel extends PSRoomPanel {
|
||||||
</div></PSPanelWrapper>;
|
</div></PSPanelWrapper>;
|
||||||
}
|
}
|
||||||
renderRoomList(title: string, rooms?: RoomInfo[]) {
|
renderRoomList(title: string, rooms?: RoomInfo[]) {
|
||||||
if (!rooms || !rooms.length) return null;
|
if (!rooms?.length) return null;
|
||||||
// Descending order
|
// Descending order
|
||||||
const sortedRooms = rooms.sort((a, b) => (b.userCount || 0) - (a.userCount || 0));
|
const sortedRooms = rooms.sort((a, b) => (b.userCount || 0) - (a.userCount || 0));
|
||||||
return <div class="roomlist">
|
return <div class="roomlist">
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import preact from "../js/lib/preact";
|
import preact from "../js/lib/preact";
|
||||||
import {PS, PSRoom, type Team} from "./client-main";
|
import { PS, PSRoom, type Team } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
import {PSTeambuilder} from "./panel-teamdropdown";
|
import { PSTeambuilder } from "./panel-teamdropdown";
|
||||||
import {Dex, toID, type ID} from "./battle-dex";
|
import { Dex, toID, type ID } from "./battle-dex";
|
||||||
import {DexSearch} from "./battle-dex-search";
|
import { DexSearch } from "./battle-dex-search";
|
||||||
import {PSSearchResults} from "./battle-searchresults";
|
import { PSSearchResults } from "./battle-searchresults";
|
||||||
|
|
||||||
class TeamRoom extends PSRoom {
|
class TeamRoom extends PSRoom {
|
||||||
team: Team | null = null;
|
team: Team | null = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class TeamTextbox extends preact.Component<{team: Team}> {
|
class TeamTextbox extends preact.Component<{ team: Team }> {
|
||||||
setInfo: {
|
setInfo: {
|
||||||
species: string,
|
species: string,
|
||||||
bottomY: number,
|
bottomY: number,
|
||||||
|
|
@ -158,7 +158,9 @@ class TeamTextbox extends preact.Component<{team: Team}> {
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
return <div class="teameditor">
|
return <div class="teameditor">
|
||||||
<textarea class="textbox teamtextbox" onInput={this.input} onSelect={this.select} onClick={this.select} onKeyUp={this.select} />
|
<textarea
|
||||||
|
class="textbox teamtextbox" onInput={this.input} onSelect={this.select} onClick={this.select} onKeyUp={this.select}
|
||||||
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
class="textbox teamtextbox heighttester" style="visibility:hidden" tabIndex={-1} aria-hidden={true}
|
class="textbox teamtextbox heighttester" style="visibility:hidden" tabIndex={-1} aria-hidden={true}
|
||||||
/>
|
/>
|
||||||
|
|
@ -177,18 +179,22 @@ class TeamTextbox extends preact.Component<{team: Team}> {
|
||||||
const left = (num % 12) * 40;
|
const left = (num % 12) * 40;
|
||||||
const iconStyle = `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-sheet.png) no-repeat scroll -${left}px -${top}px`;
|
const iconStyle = `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-sheet.png) no-repeat scroll -${left}px -${top}px`;
|
||||||
|
|
||||||
return <span class="picon" style={
|
return <span
|
||||||
`top:${prevOffset + 1}px;left:50px;position:absolute;${iconStyle}`
|
class="picon" style={`top:${prevOffset + 1}px;left:50px;position:absolute;${iconStyle}`}
|
||||||
}></span>;
|
></span>;
|
||||||
})}
|
})}
|
||||||
{this.activeOffsetY >= 0 &&
|
{this.activeOffsetY >= 0 && (
|
||||||
<div class="teaminnertextbox" style={{top: this.activeOffsetY - 1}}></div>
|
<div class="teaminnertextbox" style={{ top: this.activeOffsetY - 1 }}></div>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{this.activeType && <div class="searchresults" style={{top: this.activeSetIndex >= 0 ? this.setInfo[this.activeSetIndex].bottomY - 12 : 0}}>
|
{this.activeType && (
|
||||||
<button class="button closesearch" onClick={this.closeMenu}><i class="fa fa-times"></i> Close</button>
|
<div
|
||||||
<PSSearchResults search={this.search} />
|
class="searchresults" style={{ top: this.activeSetIndex >= 0 ? this.setInfo[this.activeSetIndex].bottomY - 12 : 0 }}
|
||||||
</div>}
|
>
|
||||||
|
<button class="button closesearch" onClick={this.closeMenu}><i class="fa fa-times"></i> Close</button>
|
||||||
|
<PSSearchResults search={this.search} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -223,7 +229,9 @@ class TeamPanel extends PSRoomPanel<TeamRoom> {
|
||||||
</button>
|
</button>
|
||||||
<label class="label teamname">
|
<label class="label teamname">
|
||||||
Team name:
|
Team name:
|
||||||
<input class="textbox" type="text" value={team.name} onInput={this.rename} onChange={this.rename} onKeyUp={this.rename} />
|
<input
|
||||||
|
class="textbox" type="text" value={team.name} onInput={this.rename} onChange={this.rename} onKeyUp={this.rename}
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
<TeamTextbox team={team} />
|
<TeamTextbox team={team} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
* @license AGPLv3
|
* @license AGPLv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PS, PSRoom, type Team} from "./client-main";
|
import { PS, PSRoom, type Team } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
import {TeamBox, TeamFolder} from "./panel-teamdropdown";
|
import { TeamBox, TeamFolder } from "./panel-teamdropdown";
|
||||||
import {PSUtils, type ID} from "./battle-dex";
|
import { PSUtils, type ID } from "./battle-dex";
|
||||||
|
|
||||||
class TeambuilderRoom extends PSRoom {
|
class TeambuilderRoom extends PSRoom {
|
||||||
readonly DEFAULT_FORMAT = 'gen8' as ID;
|
readonly DEFAULT_FORMAT = 'gen8' as ID;
|
||||||
|
|
@ -49,7 +49,8 @@ class TeambuilderRoom extends PSRoom {
|
||||||
PS.teams.undelete();
|
PS.teams.undelete();
|
||||||
this.update(null);
|
this.update(null);
|
||||||
return true;
|
return true;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// unrecognized command
|
// unrecognized command
|
||||||
alert(`Unrecognized command: ${line}`);
|
alert(`Unrecognized command: ${line}`);
|
||||||
|
|
@ -110,7 +111,7 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
|
||||||
// (This is why folders you create will automatically disappear
|
// (This is why folders you create will automatically disappear
|
||||||
// if you leave them without adding anything to them.)
|
// if you leave them without adding anything to them.)
|
||||||
|
|
||||||
const folderTable: {[folder: string]: 1 | undefined} = {'': 1};
|
const folderTable: { [folder: string]: 1 | undefined } = { '': 1 };
|
||||||
const folders: string[] = [];
|
const folders: string[] = [];
|
||||||
for (const team of PS.teams.list) {
|
for (const team of PS.teams.list) {
|
||||||
const folder = team.folder;
|
const folder = team.folder;
|
||||||
|
|
@ -169,9 +170,9 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
|
||||||
const folderOpenIcon = room.curFolder === format ? 'fa-folder-open' : 'fa-folder';
|
const folderOpenIcon = room.curFolder === format ? 'fa-folder-open' : 'fa-folder';
|
||||||
if (gen === 0) {
|
if (gen === 0) {
|
||||||
renderedFolders.push(<TeamFolder cur={room.curFolder === format} value={format}>
|
renderedFolders.push(<TeamFolder cur={room.curFolder === format} value={format}>
|
||||||
<i class={
|
<i
|
||||||
`fa ${folderOpenIcon}${format === '/' ? '-o' : ''}`
|
class={`fa ${folderOpenIcon}${format === '/' ? '-o' : ''}`}
|
||||||
}></i>
|
></i>
|
||||||
{format.slice(0, -1) || '(uncategorized)'}
|
{format.slice(0, -1) || '(uncategorized)'}
|
||||||
</TeamFolder>);
|
</TeamFolder>);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -212,7 +213,7 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
|
||||||
let filterFolder: string | null = null;
|
let filterFolder: string | null = null;
|
||||||
let filterFormat: string | null = null;
|
let filterFormat: string | null = null;
|
||||||
if (room.curFolder) {
|
if (room.curFolder) {
|
||||||
if (room.curFolder.slice(-1) === '/') {
|
if (room.curFolder.endsWith('/')) {
|
||||||
filterFolder = room.curFolder.slice(0, -1);
|
filterFolder = room.curFolder.slice(0, -1);
|
||||||
teams = teams.filter(team => !team || team.folder === filterFolder);
|
teams = teams.filter(team => !team || team.folder === filterFolder);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -226,7 +227,7 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
|
||||||
{this.renderFolderList()}
|
{this.renderFolderList()}
|
||||||
</div>
|
</div>
|
||||||
<div class="teampane">
|
<div class="teampane">
|
||||||
{filterFolder ?
|
{filterFolder ? (
|
||||||
<h2>
|
<h2>
|
||||||
<i class="fa fa-folder-open"></i> {filterFolder} {}
|
<i class="fa fa-folder-open"></i> {filterFolder} {}
|
||||||
<button class="button small" style="margin-left:5px" name="renameFolder">
|
<button class="button small" style="margin-left:5px" name="renameFolder">
|
||||||
|
|
@ -236,13 +237,13 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
|
||||||
<i class="fa fa-times"></i> Remove
|
<i class="fa fa-times"></i> Remove
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
: filterFolder === '' ?
|
) : filterFolder === '' ? (
|
||||||
<h2><i class="fa fa-folder-open-o"></i> Teams not in any folders</h2>
|
<h2><i class="fa fa-folder-open-o"></i> Teams not in any folders</h2>
|
||||||
: filterFormat ?
|
) : filterFormat ? (
|
||||||
<h2><i class="fa fa-folder-open-o"></i> {filterFormat} <small>({teams.length})</small></h2>
|
<h2><i class="fa fa-folder-open-o"></i> {filterFormat} <small>({teams.length})</small></h2>
|
||||||
:
|
) : (
|
||||||
<h2>All Teams <small>({teams.length})</small></h2>
|
<h2>All Teams <small>({teams.length})</small></h2>
|
||||||
}
|
)}
|
||||||
<p>
|
<p>
|
||||||
<button name="cmd" value="/newteam" class="button big"><i class="fa fa-plus-circle"></i> New Team</button>
|
<button name="cmd" value="/newteam" class="button big"><i class="fa fa-plus-circle"></i> New Team</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -254,7 +255,7 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
|
||||||
</li>
|
</li>
|
||||||
) : (
|
) : (
|
||||||
<li key="undelete">
|
<li key="undelete">
|
||||||
<button name="cmd" value={`/undeleteteam`}><i class="fa fa-undo"></i> Undo delete</button>
|
<button name="cmd" value="/undeleteteam"><i class="fa fa-undo"></i> Undo delete</button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
* @license AGPLv3
|
* @license AGPLv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PS, type Team} from "./client-main";
|
import { PS, type Team } from "./client-main";
|
||||||
import {PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
import {Dex, toID, type ID} from "./battle-dex";
|
import { Dex, toID, type ID } from "./battle-dex";
|
||||||
import { BattleStatIDs, BattleStatNames } from "./battle-dex-data";
|
import { BattleStatIDs, BattleStatNames } from "./battle-dex-data";
|
||||||
|
|
||||||
export class PSTeambuilder {
|
export class PSTeambuilder {
|
||||||
|
|
@ -24,14 +24,14 @@ export class PSTeambuilder {
|
||||||
|
|
||||||
// species
|
// species
|
||||||
let id = toID(set.species);
|
let id = toID(set.species);
|
||||||
buf += '|' + (toID(set.name || set.species) === id ? '' : id);
|
buf += `|${toID(set.name || set.species) === id ? '' : id}`;
|
||||||
|
|
||||||
// item
|
// item
|
||||||
buf += '|' + toID(set.item);
|
buf += `|${toID(set.item)}`;
|
||||||
|
|
||||||
// ability
|
// ability
|
||||||
id = toID(set.ability);
|
id = toID(set.ability);
|
||||||
buf += '|' + (id || '-');
|
buf += `|${id || '-'}`;
|
||||||
|
|
||||||
// moves
|
// moves
|
||||||
buf += '|';
|
buf += '|';
|
||||||
|
|
@ -39,7 +39,7 @@ export class PSTeambuilder {
|
||||||
for (let j = 0; j < set.moves.length; j++) {
|
for (let j = 0; j < set.moves.length; j++) {
|
||||||
let moveid = toID(set.moves[j]);
|
let moveid = toID(set.moves[j]);
|
||||||
if (j && !moveid) continue;
|
if (j && !moveid) continue;
|
||||||
buf += (j ? ',' : '') + moveid;
|
buf += `${j ? ',' : ''}${moveid}`;
|
||||||
if (moveid.substr(0, 11) === 'hiddenpower' && moveid.length > 11) {
|
if (moveid.substr(0, 11) === 'hiddenpower' && moveid.length > 11) {
|
||||||
hasHP = moveid.slice(11);
|
hasHP = moveid.slice(11);
|
||||||
}
|
}
|
||||||
|
|
@ -47,35 +47,24 @@ export class PSTeambuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// nature
|
// nature
|
||||||
buf += '|' + (set.nature || '');
|
buf += `|${set.nature || ''}`;
|
||||||
|
|
||||||
// evs
|
// evs
|
||||||
if (set.evs) {
|
if (set.evs) {
|
||||||
buf += '|' + (set.evs['hp'] || '') + ',' +
|
buf += `|${set.evs['hp'] || ''},${set.evs['atk'] || ''},${set.evs['def'] || ''},` +
|
||||||
(set.evs['atk'] || '') + ',' +
|
`${set.evs['spa'] || ''},${set.evs['spd'] || ''},${set.evs['spe'] || ''}`;
|
||||||
(set.evs['def'] || '') + ',' +
|
|
||||||
(set.evs['spa'] || '') + ',' +
|
|
||||||
(set.evs['spd'] || '') + ',' +
|
|
||||||
(set.evs['spe'] || '');
|
|
||||||
} else {
|
} else {
|
||||||
buf += '|';
|
buf += '|';
|
||||||
}
|
}
|
||||||
|
|
||||||
// gender
|
// gender
|
||||||
if (set.gender) {
|
buf += `|${set.gender || ''}`;
|
||||||
buf += '|' + set.gender;
|
|
||||||
} else {
|
|
||||||
buf += '|';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ivs
|
// ivs
|
||||||
if (set.ivs) {
|
if (set.ivs) {
|
||||||
buf += '|' + (set.ivs['hp'] === 31 ? '' : set.ivs['hp']) + ',' +
|
buf += `|${set.ivs['hp'] === 31 ? '' : set.ivs['hp']},${set.ivs['atk'] === 31 ? '' : set.ivs['atk']},` +
|
||||||
(set.ivs['atk'] === 31 ? '' : set.ivs['atk']) + ',' +
|
`${set.ivs['def'] === 31 ? '' : set.ivs['def']},${set.ivs['spa'] === 31 ? '' : set.ivs['spa']},` +
|
||||||
(set.ivs['def'] === 31 ? '' : set.ivs['def']) + ',' +
|
`${set.ivs['spd'] === 31 ? '' : set.ivs['spd']},${set.ivs['spe'] === 31 ? '' : set.ivs['spe']}`;
|
||||||
(set.ivs['spa'] === 31 ? '' : set.ivs['spa']) + ',' +
|
|
||||||
(set.ivs['spd'] === 31 ? '' : set.ivs['spd']) + ',' +
|
|
||||||
(set.ivs['spe'] === 31 ? '' : set.ivs['spe']);
|
|
||||||
} else {
|
} else {
|
||||||
buf += '|';
|
buf += '|';
|
||||||
}
|
}
|
||||||
|
|
@ -89,14 +78,14 @@ export class PSTeambuilder {
|
||||||
|
|
||||||
// level
|
// level
|
||||||
if (set.level) {
|
if (set.level) {
|
||||||
buf += '|' + set.level;
|
buf += `|${set.level}`;
|
||||||
} else {
|
} else {
|
||||||
buf += '|';
|
buf += '|';
|
||||||
}
|
}
|
||||||
|
|
||||||
// happiness
|
// happiness
|
||||||
if (set.happiness !== undefined) {
|
if (set.happiness !== undefined) {
|
||||||
buf += '|' + set.happiness;
|
buf += `|${set.happiness}`;
|
||||||
} else {
|
} else {
|
||||||
buf += '|';
|
buf += '|';
|
||||||
}
|
}
|
||||||
|
|
@ -105,10 +94,10 @@ export class PSTeambuilder {
|
||||||
set.pokeball || (set.hpType && toID(set.hpType) !== hasHP) || set.gigantamax ||
|
set.pokeball || (set.hpType && toID(set.hpType) !== hasHP) || set.gigantamax ||
|
||||||
(set.dynamaxLevel !== undefined && set.dynamaxLevel !== 10)
|
(set.dynamaxLevel !== undefined && set.dynamaxLevel !== 10)
|
||||||
) {
|
) {
|
||||||
buf += ',' + (set.hpType || '');
|
buf += `,${set.hpType || ''}`;
|
||||||
buf += ',' + toID(set.pokeball);
|
buf += `,${toID(set.pokeball)}`;
|
||||||
buf += ',' + (set.gigantamax ? 'G' : '');
|
buf += `,${set.gigantamax ? 'G' : ''}`;
|
||||||
buf += ',' + (set.dynamaxLevel !== undefined && set.dynamaxLevel !== 10 ? set.dynamaxLevel : '');
|
buf += `,${set.dynamaxLevel !== undefined && set.dynamaxLevel !== 10 ? set.dynamaxLevel : ''}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,7 +112,7 @@ export class PSTeambuilder {
|
||||||
for (const setBuf of buf.split(`]`)) {
|
for (const setBuf of buf.split(`]`)) {
|
||||||
const parts = setBuf.split(`|`);
|
const parts = setBuf.split(`|`);
|
||||||
if (parts.length < 11) continue;
|
if (parts.length < 11) continue;
|
||||||
let set: Dex.PokemonSet = {species: '', moves: []};
|
let set: Dex.PokemonSet = { species: '', moves: [] };
|
||||||
team.push(set);
|
team.push(set);
|
||||||
|
|
||||||
// name
|
// name
|
||||||
|
|
@ -137,13 +126,12 @@ export class PSTeambuilder {
|
||||||
|
|
||||||
// ability
|
// ability
|
||||||
const species = Dex.species.get(set.species);
|
const species = Dex.species.get(set.species);
|
||||||
set.ability = parts[3] === '-' ?
|
set.ability =
|
||||||
'' :
|
parts[3] === '-' ? '' :
|
||||||
(species.baseSpecies === 'Zygarde' && parts[3] === 'H') ?
|
(species.baseSpecies === 'Zygarde' && parts[3] === 'H') ? 'Power Construct' :
|
||||||
'Power Construct' :
|
|
||||||
['', '0', '1', 'H', 'S'].includes(parts[3]) ?
|
['', '0', '1', 'H', 'S'].includes(parts[3]) ?
|
||||||
species.abilities[parts[3] as '0' || '0'] || (parts[3] === '' ? '' : '!!!ERROR!!!') :
|
species.abilities[parts[3] as '0' || '0'] || (parts[3] === '' ? '' : '!!!ERROR!!!') :
|
||||||
Dex.abilities.get(parts[3]).name;
|
Dex.abilities.get(parts[3]).name;
|
||||||
|
|
||||||
// moves
|
// moves
|
||||||
set.moves = parts[4].split(',').map(moveid =>
|
set.moves = parts[4].split(',').map(moveid =>
|
||||||
|
|
@ -167,7 +155,7 @@ export class PSTeambuilder {
|
||||||
spe: Number(evs[5]) || 0,
|
spe: Number(evs[5]) || 0,
|
||||||
};
|
};
|
||||||
} else if (parts[6] === '0') {
|
} else if (parts[6] === '0') {
|
||||||
set.evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
set.evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,7 +321,7 @@ export class PSTeambuilder {
|
||||||
line = line.slice(0, -4);
|
line = line.slice(0, -4);
|
||||||
}
|
}
|
||||||
let parenIndex = line.lastIndexOf(' (');
|
let parenIndex = line.lastIndexOf(' (');
|
||||||
if (line.charAt(line.length - 1) === ')' && parenIndex !== -1) {
|
if (line.endsWith(')') && parenIndex !== -1) {
|
||||||
set.species = Dex.species.get(line.slice(parenIndex + 2, -1)).name;
|
set.species = Dex.species.get(line.slice(parenIndex + 2, -1)).name;
|
||||||
set.name = line.slice(0, parenIndex);
|
set.name = line.slice(0, parenIndex);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -368,7 +356,7 @@ export class PSTeambuilder {
|
||||||
} else if (line.startsWith('EVs: ')) {
|
} else if (line.startsWith('EVs: ')) {
|
||||||
line = line.slice(5);
|
line = line.slice(5);
|
||||||
let evLines = line.split('/');
|
let evLines = line.split('/');
|
||||||
set.evs = {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0};
|
set.evs = { hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0 };
|
||||||
for (let evLine of evLines) {
|
for (let evLine of evLines) {
|
||||||
evLine = evLine.trim();
|
evLine = evLine.trim();
|
||||||
let spaceIndex = evLine.indexOf(' ');
|
let spaceIndex = evLine.indexOf(' ');
|
||||||
|
|
@ -381,7 +369,7 @@ export class PSTeambuilder {
|
||||||
} else if (line.startsWith('IVs: ')) {
|
} else if (line.startsWith('IVs: ')) {
|
||||||
line = line.slice(5);
|
line = line.slice(5);
|
||||||
let ivLines = line.split(' / ');
|
let ivLines = line.split(' / ');
|
||||||
set.ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31};
|
set.ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 };
|
||||||
for (let ivLine of ivLines) {
|
for (let ivLine of ivLines) {
|
||||||
ivLine = ivLine.trim();
|
ivLine = ivLine.trim();
|
||||||
let spaceIndex = ivLine.indexOf(' ');
|
let spaceIndex = ivLine.indexOf(' ');
|
||||||
|
|
@ -392,19 +380,19 @@ export class PSTeambuilder {
|
||||||
if (isNaN(statval)) statval = 31;
|
if (isNaN(statval)) statval = 31;
|
||||||
set.ivs[statid] = statval;
|
set.ivs[statid] = statval;
|
||||||
}
|
}
|
||||||
} else if (line.match(/^[A-Za-z]+ (N|n)ature/)) {
|
} else if (/^[A-Za-z]+ (N|n)ature/.exec(line)) {
|
||||||
let natureIndex = line.indexOf(' Nature');
|
let natureIndex = line.indexOf(' Nature');
|
||||||
if (natureIndex === -1) natureIndex = line.indexOf(' nature');
|
if (natureIndex === -1) natureIndex = line.indexOf(' nature');
|
||||||
if (natureIndex === -1) return;
|
if (natureIndex === -1) return;
|
||||||
line = line.substr(0, natureIndex);
|
line = line.substr(0, natureIndex);
|
||||||
if (line !== 'undefined') set.nature = line as Dex.NatureName;
|
if (line !== 'undefined') set.nature = line as Dex.NatureName;
|
||||||
} else if (line.charAt(0) === '-' || line.charAt(0) === '~') {
|
} else if (line.startsWith('-') || line.startsWith('~')) {
|
||||||
line = line.slice(line.charAt(1) === ' ' ? 2 : 1);
|
line = line.slice(line.charAt(1) === ' ' ? 2 : 1);
|
||||||
if (line.startsWith('Hidden Power [')) {
|
if (line.startsWith('Hidden Power [')) {
|
||||||
const hpType = line.slice(14, -1) as Dex.TypeName;
|
const hpType = line.slice(14, -1) as Dex.TypeName;
|
||||||
line = 'Hidden Power ' + hpType;
|
line = 'Hidden Power ' + hpType;
|
||||||
if (!set.ivs && Dex.types.isName(hpType)) {
|
if (!set.ivs && Dex.types.isName(hpType)) {
|
||||||
set.ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31};
|
set.ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 };
|
||||||
const hpIVs = Dex.types.get(hpType).HPivs || {};
|
const hpIVs = Dex.types.get(hpType).HPivs || {};
|
||||||
for (let stat in hpIVs) {
|
for (let stat in hpIVs) {
|
||||||
set.ivs[stat as Dex.StatName] = hpIVs[stat as Dex.StatName]!;
|
set.ivs[stat as Dex.StatName] = hpIVs[stat as Dex.StatName]!;
|
||||||
|
|
@ -546,7 +534,7 @@ export class PSTeambuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TeamFolder(props: {cur?: boolean, value: string, children: preact.ComponentChildren}) {
|
export function TeamFolder(props: { cur?: boolean, value: string, children: preact.ComponentChildren }) {
|
||||||
// folders are <div>s rather than <button>s because in theory it has
|
// folders are <div>s rather than <button>s because in theory it has
|
||||||
// less weird interactions with HTML5 drag-and-drop
|
// less weird interactions with HTML5 drag-and-drop
|
||||||
if (props.cur) {
|
if (props.cur) {
|
||||||
|
|
@ -560,7 +548,7 @@ export function TeamFolder(props: {cur?: boolean, value: string, children: preac
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TeamBox(props: {team: Team | null, noLink?: boolean, button?: boolean}) {
|
export function TeamBox(props: { team: Team | null, noLink?: boolean, button?: boolean }) {
|
||||||
const team = props.team;
|
const team = props.team;
|
||||||
let contents;
|
let contents;
|
||||||
if (team) {
|
if (team) {
|
||||||
|
|
@ -659,7 +647,7 @@ class TeamDropdownPanel extends PSRoomPanel {
|
||||||
if (availableWidth > 636) width = 613;
|
if (availableWidth > 636) width = 613;
|
||||||
if (availableWidth > 945) width = 919;
|
if (availableWidth > 945) width = 919;
|
||||||
|
|
||||||
let teamBuckets: {[folder: string]: Team[]} = {};
|
let teamBuckets: { [folder: string]: Team[] } = {};
|
||||||
for (const team of teams) {
|
for (const team of teams) {
|
||||||
const list = teamBuckets[team.folder] || (teamBuckets[team.folder] = []);
|
const list = teamBuckets[team.folder] || (teamBuckets[team.folder] = []);
|
||||||
list.push(team);
|
list.push(team);
|
||||||
|
|
@ -676,13 +664,27 @@ class TeamDropdownPanel extends PSRoomPanel {
|
||||||
const hasOtherGens = genList.length > 1 || genList[0] !== baseGen;
|
const hasOtherGens = genList.length > 1 || genList[0] !== baseGen;
|
||||||
|
|
||||||
teamList.push(<p>
|
teamList.push(<p>
|
||||||
{baseFormat.length > 4 && <button class={'button' + (baseFormat === this.format ? ' disabled' : '')} onClick={this.setFormat} name="format" value={baseFormat}>
|
{baseFormat.length > 4 && (
|
||||||
<i class="fa fa-folder-o"></i> [{baseFormat.slice(0, 4)}] {baseFormat.slice(4)}
|
<button
|
||||||
</button>} <button class={'button' + (baseGen === this.format ? ' disabled' : '')} onClick={this.setFormat} name="format" value={baseGen}>
|
class={'button' + (baseFormat === this.format ? ' disabled' : '')}
|
||||||
|
onClick={this.setFormat} name="format" value={baseFormat}
|
||||||
|
>
|
||||||
|
<i class="fa fa-folder-o"></i> [{baseFormat.slice(0, 4)}] {baseFormat.slice(4)}
|
||||||
|
</button>
|
||||||
|
)} {}
|
||||||
|
<button
|
||||||
|
class={'button' + (baseGen === this.format ? ' disabled' : '')} onClick={this.setFormat} name="format" value={baseGen}
|
||||||
|
>
|
||||||
<i class="fa fa-folder-o"></i> [{baseGen}] <em>(uncategorized)</em>
|
<i class="fa fa-folder-o"></i> [{baseGen}] <em>(uncategorized)</em>
|
||||||
</button> <button class={'button' + (baseGen === this.gen ? ' disabled' : '')} onClick={this.setFormat} name="gen" value={baseGen}>
|
</button> {}
|
||||||
|
<button
|
||||||
|
class={'button' + (baseGen === this.gen ? ' disabled' : '')} onClick={this.setFormat} name="gen" value={baseGen}
|
||||||
|
>
|
||||||
<i class="fa fa-folder-o"></i> [{baseGen}] <em>(all)</em>
|
<i class="fa fa-folder-o"></i> [{baseGen}] <em>(all)</em>
|
||||||
</button> {hasOtherGens && !this.gen && <button class="button" onClick={this.setFormat} name="gen" value={baseGen}>Other gens</button>}
|
</button> {}
|
||||||
|
{hasOtherGens && !this.gen && (
|
||||||
|
<button class="button" onClick={this.setFormat} name="gen" value={baseGen}>Other gens</button>
|
||||||
|
)}
|
||||||
</p>);
|
</p>);
|
||||||
|
|
||||||
if (hasOtherGens && this.gen) {
|
if (hasOtherGens && this.gen) {
|
||||||
|
|
@ -716,7 +718,7 @@ class TeamDropdownPanel extends PSRoomPanel {
|
||||||
</h2>);
|
</h2>);
|
||||||
}
|
}
|
||||||
teamList.push(<ul class="teamdropdown" onClick={this.click}>
|
teamList.push(<ul class="teamdropdown" onClick={this.click}>
|
||||||
{teamBuckets[folder].map(team => <li key={team.key} style={"display:inline-block"}>
|
{teamBuckets[folder].map(team => <li key={team.key} style={{ display: 'inline-block' }}>
|
||||||
<TeamBox team={team} button />
|
<TeamBox team={team} button />
|
||||||
</li>)}
|
</li>)}
|
||||||
</ul>);
|
</ul>);
|
||||||
|
|
@ -747,9 +749,7 @@ export interface FormatData {
|
||||||
effectType: 'Format';
|
effectType: 'Format';
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const BattleFormats: {[id: string]: FormatData};
|
declare const BattleFormats: { [id: string]: FormatData };
|
||||||
/** id:name */
|
|
||||||
declare const NonBattleGames: {[id: string]: string};
|
|
||||||
|
|
||||||
class FormatDropdownPanel extends PSRoomPanel {
|
class FormatDropdownPanel extends PSRoomPanel {
|
||||||
gen = '';
|
gen = '';
|
||||||
|
|
@ -778,6 +778,7 @@ class FormatDropdownPanel extends PSRoomPanel {
|
||||||
let formatsLoaded = !!window.BattleFormats;
|
let formatsLoaded = !!window.BattleFormats;
|
||||||
if (formatsLoaded) {
|
if (formatsLoaded) {
|
||||||
formatsLoaded = false;
|
formatsLoaded = false;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
for (let i in window.BattleFormats) {
|
for (let i in window.BattleFormats) {
|
||||||
formatsLoaded = true;
|
formatsLoaded = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -806,7 +807,7 @@ class FormatDropdownPanel extends PSRoomPanel {
|
||||||
|
|
||||||
let curSection = '';
|
let curSection = '';
|
||||||
let curColumnNum = 0;
|
let curColumnNum = 0;
|
||||||
let curColumn: (FormatData | {id: null, section: string})[] = [];
|
let curColumn: (FormatData | { id: null, section: string })[] = [];
|
||||||
const columns = [curColumn];
|
const columns = [curColumn];
|
||||||
for (const format of formats) {
|
for (const format of formats) {
|
||||||
if (format.column !== curColumnNum) {
|
if (format.column !== curColumnNum) {
|
||||||
|
|
@ -819,7 +820,7 @@ class FormatDropdownPanel extends PSRoomPanel {
|
||||||
if (format.section !== curSection) {
|
if (format.section !== curSection) {
|
||||||
curSection = format.section;
|
curSection = format.section;
|
||||||
if (curSection) {
|
if (curSection) {
|
||||||
curColumn.push({id: null, section: curSection});
|
curColumn.push({ id: null, section: curSection });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
curColumn.push(format);
|
curColumn.push(format);
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import preact from "../js/lib/preact";
|
import preact from "../js/lib/preact";
|
||||||
import {PS, PSRoom, type RoomOptions, type RoomID} from "./client-main";
|
import { PS, PSRoom, type RoomOptions, type RoomID } from "./client-main";
|
||||||
import {PSMain, PSPanelWrapper, PSRoomPanel} from "./panels";
|
import { PSMain, PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||||
import type {Battle} from "./battle";
|
import type { Battle } from "./battle";
|
||||||
import {Dex, toRoomid, toUserid, type ID} from "./battle-dex";
|
import { Dex, toRoomid, toUserid, type ID } from "./battle-dex";
|
||||||
import {BattleLog} from "./battle-log";
|
import { BattleLog } from "./battle-log";
|
||||||
|
|
||||||
window.addEventListener('drop', e => {
|
window.addEventListener('drop', e => {
|
||||||
console.log('drop ' + e.dataTransfer!.dropEffect);
|
console.log('drop ' + e.dataTransfer!.dropEffect);
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (/^text/.test((target as HTMLInputElement).type)) {
|
if ((target as HTMLInputElement).type.startsWith("text")) {
|
||||||
PS.dragging = null;
|
PS.dragging = null;
|
||||||
return; // Ignore text fields
|
return; // Ignore text fields
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ window.addEventListener('dragover', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
export class PSHeader extends preact.Component<{style: {}}> {
|
export class PSHeader extends preact.Component<{ style: object }> {
|
||||||
handleDragEnter = (e: DragEvent) => {
|
handleDragEnter = (e: DragEvent) => {
|
||||||
console.log('dragenter ' + e.dataTransfer!.dropEffect);
|
console.log('dragenter ' + e.dataTransfer!.dropEffect);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -63,6 +63,7 @@ export class PSHeader extends preact.Component<{style: {}}> {
|
||||||
if (rightIndex >= 0) {
|
if (rightIndex >= 0) {
|
||||||
this.dragOnto(draggingRoom, 'rightRoomList', rightIndex);
|
this.dragOnto(draggingRoom, 'rightRoomList', rightIndex);
|
||||||
} else {
|
} else {
|
||||||
|
// eslint-disable-next-line no-useless-return
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +76,7 @@ export class PSHeader extends preact.Component<{style: {}}> {
|
||||||
const roomid = PS.router.extractRoomID((e.currentTarget as HTMLAnchorElement).href);
|
const roomid = PS.router.extractRoomID((e.currentTarget as HTMLAnchorElement).href);
|
||||||
if (!roomid) return; // should never happen
|
if (!roomid) return; // should never happen
|
||||||
|
|
||||||
PS.dragging = {type: 'room', roomid};
|
PS.dragging = { type: 'room', roomid };
|
||||||
};
|
};
|
||||||
dragOnto(fromRoom: RoomID, toRoomList: 'leftRoomList' | 'rightRoomList' | 'miniRoomList', toIndex: number) {
|
dragOnto(fromRoom: RoomID, toRoomList: 'leftRoomList' | 'rightRoomList' | 'miniRoomList', toIndex: number) {
|
||||||
// one day you will be able to rearrange mainmenu and rooms, but not today
|
// one day you will be able to rearrange mainmenu and rooms, but not today
|
||||||
|
|
@ -186,7 +187,7 @@ export class PSHeader extends preact.Component<{style: {}}> {
|
||||||
break;
|
break;
|
||||||
case 'html':
|
case 'html':
|
||||||
default:
|
default:
|
||||||
if (title.charAt(0) === '[') {
|
if (title.startsWith('[')) {
|
||||||
let closeBracketIndex = title.indexOf(']');
|
let closeBracketIndex = title.indexOf(']');
|
||||||
if (closeBracketIndex > 0) {
|
if (closeBracketIndex > 0) {
|
||||||
icon = <i class="text">{title.slice(1, closeBracketIndex)}</i>;
|
icon = <i class="text">{title.slice(1, closeBracketIndex)}</i>;
|
||||||
|
|
@ -222,7 +223,7 @@ export class PSHeader extends preact.Component<{style: {}}> {
|
||||||
if (!PS.user.named) {
|
if (!PS.user.named) {
|
||||||
return <a class="button" href="login">Choose name</a>;
|
return <a class="button" href="login">Choose name</a>;
|
||||||
}
|
}
|
||||||
const userColor = window.BattleLog && {color: BattleLog.usernameColor(PS.user.userid)};
|
const userColor = window.BattleLog && { color: BattleLog.usernameColor(PS.user.userid) };
|
||||||
return <span class="username" data-name={PS.user.name} style={userColor}>
|
return <span class="username" data-name={PS.user.name} style={userColor}>
|
||||||
<i class="fa fa-user" style="color:#779EC5"></i> <span class="usernametext">{PS.user.name}</span>
|
<i class="fa fa-user" style="color:#779EC5"></i> <span class="usernametext">{PS.user.name}</span>
|
||||||
</span>;
|
</span>;
|
||||||
|
|
@ -244,7 +245,7 @@ export class PSHeader extends preact.Component<{style: {}}> {
|
||||||
<ul>
|
<ul>
|
||||||
{PS.leftRoomList.slice(1).map(roomid => this.renderRoomTab(roomid))}
|
{PS.leftRoomList.slice(1).map(roomid => this.renderRoomTab(roomid))}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="siderooms" style={{float: 'none', marginLeft: PS.leftRoomWidth - 144}}>
|
<ul class="siderooms" style={{ float: 'none', marginLeft: PS.leftRoomWidth - 144 }}>
|
||||||
{PS.rightRoomList.map(roomid => this.renderRoomTab(roomid))}
|
{PS.rightRoomList.map(roomid => this.renderRoomTab(roomid))}
|
||||||
</ul>
|
</ul>
|
||||||
</div></div>
|
</div></div>
|
||||||
|
|
@ -285,19 +286,19 @@ export class UserRoom extends PSRoom {
|
||||||
class UserPanel extends PSRoomPanel<UserRoom> {
|
class UserPanel extends PSRoomPanel<UserRoom> {
|
||||||
override render() {
|
override render() {
|
||||||
const room = this.props.room;
|
const room = this.props.room;
|
||||||
const user = PS.mainmenu.userdetailsCache[room.userid] || {userid: room.userid, avatar: '[loading]'};
|
const user = PS.mainmenu.userdetailsCache[room.userid] || { userid: room.userid, avatar: '[loading]' };
|
||||||
const name = room.name.slice(1);
|
const name = room.name.slice(1);
|
||||||
|
|
||||||
const group = PS.server.getGroup(room.name);
|
const group = PS.server.getGroup(room.name);
|
||||||
let groupName: preact.ComponentChild = group.name || null;
|
let groupName: preact.ComponentChild = group.name || null;
|
||||||
if (group.type === 'punishment') {
|
if (group.type === 'punishment') {
|
||||||
groupName = <span style='color:#777777'>{groupName}</span>;
|
groupName = <span style="color:#777777">{groupName}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalGroup = PS.server.getGroup(user.group);
|
const globalGroup = PS.server.getGroup(user.group);
|
||||||
let globalGroupName: preact.ComponentChild = globalGroup.name && `Global ${globalGroup.name}` || null;
|
let globalGroupName: preact.ComponentChild = globalGroup.name && `Global ${globalGroup.name}` || null;
|
||||||
if (globalGroup.type === 'punishment') {
|
if (globalGroup.type === 'punishment') {
|
||||||
globalGroupName = <span style='color:#777777'>{globalGroupName}</span>;
|
globalGroupName = <span style="color:#777777">{globalGroupName}</span>;
|
||||||
}
|
}
|
||||||
if (globalGroup.name === group.name) groupName = null;
|
if (globalGroup.name === group.name) groupName = null;
|
||||||
|
|
||||||
|
|
@ -319,7 +320,8 @@ class UserPanel extends PSRoomPanel<UserRoom> {
|
||||||
const p1 = curRoom.p1!.substr(1);
|
const p1 = curRoom.p1!.substr(1);
|
||||||
const p2 = curRoom.p2!.substr(1);
|
const p2 = curRoom.p2!.substr(1);
|
||||||
const ownBattle = (PS.user.userid === toUserid(p1) || PS.user.userid === toUserid(p2));
|
const ownBattle = (PS.user.userid === toUserid(p1) || PS.user.userid === toUserid(p2));
|
||||||
const roomLink = <a href={`/${roomid}`} class={'ilink' + (ownBattle || roomid in PS.rooms ? ' yours' : '')}
|
const roomLink = <a
|
||||||
|
href={`/${roomid}`} class={'ilink' + (ownBattle || roomid in PS.rooms ? ' yours' : '')}
|
||||||
title={`${p1 || '?'} v. ${p2 || '?'}`}
|
title={`${p1 || '?'} v. ${p2 || '?'}`}
|
||||||
>{roomrank}{roomid.substr(7)}</a>;
|
>{roomrank}{roomid.substr(7)}</a>;
|
||||||
if (curRoom.isPrivate) {
|
if (curRoom.isPrivate) {
|
||||||
|
|
@ -365,28 +367,31 @@ class UserPanel extends PSRoomPanel<UserRoom> {
|
||||||
{user.avatar !== '[loading]' &&
|
{user.avatar !== '[loading]' &&
|
||||||
<img
|
<img
|
||||||
class={'trainersprite' + (room.isSelf ? ' yours' : '')}
|
class={'trainersprite' + (room.isSelf ? ' yours' : '')}
|
||||||
src={Dex.resolveAvatar('' + (user.avatar || 'unknown'))}
|
src={Dex.resolveAvatar(`${user.avatar || 'unknown'}`)}
|
||||||
/>
|
/>}
|
||||||
}
|
<strong><a
|
||||||
<strong><a href={`//${Config.routes.users}/${user.userid}`} target="_blank" style={away ? {color: '#888888'} : null}>{name}</a></strong><br />
|
href={`//${Config.routes.users}/${user.userid}`} target="_blank" style={away ? { color: '#888888' } : null}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</a></strong><br />
|
||||||
{status && <div class="userstatus">{status}</div>}
|
{status && <div class="userstatus">{status}</div>}
|
||||||
{groupName && <div class="usergroup roomgroup">{groupName}</div>}
|
{groupName && <div class="usergroup roomgroup">{groupName}</div>}
|
||||||
{globalGroupName && <div class="usergroup globalgroup">{globalGroupName}</div>}
|
{globalGroupName && <div class="usergroup globalgroup">{globalGroupName}</div>}
|
||||||
{user.customgroup && <div class="usergroup globalgroup">{user.customgroup}</div>}
|
{user.customgroup && <div class="usergroup globalgroup">{user.customgroup}</div>}
|
||||||
{roomsList}
|
{roomsList}
|
||||||
</div>
|
</div>
|
||||||
{isSelf || !PS.user.named ?
|
{isSelf || !PS.user.named ? (
|
||||||
<p class="buttonbar">
|
<p class="buttonbar">
|
||||||
<button class="button" disabled>Challenge</button> {}
|
<button class="button" disabled>Challenge</button> {}
|
||||||
<button class="button" disabled>Chat</button>
|
<button class="button" disabled>Chat</button>
|
||||||
</p>
|
</p>
|
||||||
:
|
) : (
|
||||||
<p class="buttonbar">
|
<p class="buttonbar">
|
||||||
<button class="button" data-href={`/challenge-${user.userid}`}>Challenge</button> {}
|
<button class="button" data-href={`/challenge-${user.userid}`}>Challenge</button> {}
|
||||||
<button class="button" data-href={`/pm-${user.userid}`}>Chat</button> {}
|
<button class="button" data-href={`/pm-${user.userid}`}>Chat</button> {}
|
||||||
<button class="button disabled" name="userOptions">{'\u2026'}</button>
|
<button class="button disabled" name="userOptions">{'\u2026'}</button>
|
||||||
</p>
|
</p>
|
||||||
}
|
)}
|
||||||
{isSelf && <hr />}
|
{isSelf && <hr />}
|
||||||
{isSelf && <p class="buttonbar" style="text-align: right">
|
{isSelf && <p class="buttonbar" style="text-align: right">
|
||||||
<button class="button disabled" name="login"><i class="fa fa-pencil"></i> Change name</button> {}
|
<button class="button disabled" name="login"><i class="fa fa-pencil"></i> Change name</button> {}
|
||||||
|
|
@ -423,7 +428,9 @@ class VolumePanel extends PSRoomPanel {
|
||||||
return <PSPanelWrapper room={room}>
|
return <PSPanelWrapper room={room}>
|
||||||
<h3>Volume</h3>
|
<h3>Volume</h3>
|
||||||
<p class="volume">
|
<p class="volume">
|
||||||
<label class="optlabel">Effects: <span class="value">{!PS.prefs.mute && PS.prefs.effectvolume ? `${PS.prefs.effectvolume}%` : `muted`}</span></label>
|
<label class="optlabel">
|
||||||
|
Effects: <span class="value">{!PS.prefs.mute && PS.prefs.effectvolume ? `${PS.prefs.effectvolume}%` : `muted`}</span>
|
||||||
|
</label>
|
||||||
{PS.prefs.mute ?
|
{PS.prefs.mute ?
|
||||||
<em>(muted)</em> :
|
<em>(muted)</em> :
|
||||||
<input
|
<input
|
||||||
|
|
@ -432,7 +439,9 @@ class VolumePanel extends PSRoomPanel {
|
||||||
/>}
|
/>}
|
||||||
</p>
|
</p>
|
||||||
<p class="volume">
|
<p class="volume">
|
||||||
<label class="optlabel">Music: <span class="value">{!PS.prefs.mute && PS.prefs.musicvolume ? `${PS.prefs.musicvolume}%` : `muted`}</span></label>
|
<label class="optlabel">
|
||||||
|
Music: <span class="value">{!PS.prefs.mute && PS.prefs.musicvolume ? `${PS.prefs.musicvolume}%` : `muted`}</span>
|
||||||
|
</label>
|
||||||
{PS.prefs.mute ?
|
{PS.prefs.mute ?
|
||||||
<em>(muted)</em> :
|
<em>(muted)</em> :
|
||||||
<input
|
<input
|
||||||
|
|
@ -441,7 +450,10 @@ class VolumePanel extends PSRoomPanel {
|
||||||
/>}
|
/>}
|
||||||
</p>
|
</p>
|
||||||
<p class="volume">
|
<p class="volume">
|
||||||
<label class="optlabel">Notifications: <span class="value">{!PS.prefs.mute && PS.prefs.notifvolume ? `${PS.prefs.notifvolume}%` : `muted`}</span></label>
|
<label class="optlabel">
|
||||||
|
Notifications: {}
|
||||||
|
<span class="value">{!PS.prefs.mute && PS.prefs.notifvolume ? `${PS.prefs.notifvolume}%` : `muted`}</span>
|
||||||
|
</label>
|
||||||
{PS.prefs.mute ?
|
{PS.prefs.mute ?
|
||||||
<em>(muted)</em> :
|
<em>(muted)</em> :
|
||||||
<input
|
<input
|
||||||
|
|
@ -450,7 +462,9 @@ class VolumePanel extends PSRoomPanel {
|
||||||
/>}
|
/>}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label class="checkbox"><input type="checkbox" name="mute" checked={PS.prefs.mute} onChange={this.setMute} /> Mute all</label>
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" name="mute" checked={PS.prefs.mute} onChange={this.setMute} /> Mute all
|
||||||
|
</label>
|
||||||
</p>
|
</p>
|
||||||
</PSPanelWrapper>;
|
</PSPanelWrapper>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import preact from "../js/lib/preact";
|
import preact from "../js/lib/preact";
|
||||||
import {toID} from "./battle-dex";
|
import { toID } from "./battle-dex";
|
||||||
import {BattleLog} from "./battle-log";
|
import { BattleLog } from "./battle-log";
|
||||||
import type { Args } from "./battle-text-parser";
|
import type { Args } from "./battle-text-parser";
|
||||||
import {BattleTooltips} from "./battle-tooltips";
|
import { BattleTooltips } from "./battle-tooltips";
|
||||||
import type {PSSubscription} from "./client-core";
|
import type { PSSubscription } from "./client-core";
|
||||||
import {PS, PSRoom, type RoomID} from "./client-main";
|
import { PS, type PSRoom, type RoomID } from "./client-main";
|
||||||
import {PSHeader} from "./panel-topbar";
|
import { PSHeader } from "./panel-topbar";
|
||||||
|
|
||||||
export class PSRouter {
|
export class PSRouter {
|
||||||
roomid = '' as RoomID;
|
roomid = '' as RoomID;
|
||||||
|
|
@ -127,7 +127,7 @@ export class PSRouter {
|
||||||
}
|
}
|
||||||
PS.router = new PSRouter();
|
PS.router = new PSRouter();
|
||||||
|
|
||||||
export class PSRoomPanel<T extends PSRoom = PSRoom> extends preact.Component<{room: T}> {
|
export class PSRoomPanel<T extends PSRoom = PSRoom> extends preact.Component<{ room: T }> {
|
||||||
subscriptions: PSSubscription[] = [];
|
subscriptions: PSSubscription[] = [];
|
||||||
override componentDidMount() {
|
override componentDidMount() {
|
||||||
if (PS.room === this.props.room) this.focus();
|
if (PS.room === this.props.room) this.focus();
|
||||||
|
|
@ -277,6 +277,7 @@ export class PSMain extends preact.Component {
|
||||||
}
|
}
|
||||||
if (PS.room !== clickedRoom) {
|
if (PS.room !== clickedRoom) {
|
||||||
if (clickedRoom) PS.room = clickedRoom;
|
if (clickedRoom) PS.room = clickedRoom;
|
||||||
|
// eslint-disable-next-line no-unmodified-loop-condition
|
||||||
while (PS.popups.length && (!clickedRoom || clickedRoom.id !== PS.popups[PS.popups.length - 1])) {
|
while (PS.popups.length && (!clickedRoom || clickedRoom.id !== PS.popups[PS.popups.length - 1])) {
|
||||||
PS.closePopup();
|
PS.closePopup();
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +319,7 @@ export class PSMain extends preact.Component {
|
||||||
|
|
||||||
const colorSchemeQuery = window.matchMedia?.('(prefers-color-scheme: dark)');
|
const colorSchemeQuery = window.matchMedia?.('(prefers-color-scheme: dark)');
|
||||||
if (colorSchemeQuery?.media !== 'not all') {
|
if (colorSchemeQuery?.media !== 'not all') {
|
||||||
colorSchemeQuery.addEventListener('change', function (cs) {
|
colorSchemeQuery.addEventListener('change', cs => {
|
||||||
if (PS.prefs.theme === 'system') document.body.className = cs.matches ? 'dark' : '';
|
if (PS.prefs.theme === 'system') document.body.className = cs.matches ? 'dark' : '';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -326,7 +327,7 @@ export class PSMain extends preact.Component {
|
||||||
PS.prefs.subscribeAndRun(key => {
|
PS.prefs.subscribeAndRun(key => {
|
||||||
if (!key || key === 'theme') {
|
if (!key || key === 'theme') {
|
||||||
const dark = PS.prefs.theme === 'dark' ||
|
const dark = PS.prefs.theme === 'dark' ||
|
||||||
(PS.prefs.theme === 'system' && colorSchemeQuery && colorSchemeQuery.matches);
|
(PS.prefs.theme === 'system' && colorSchemeQuery?.matches);
|
||||||
document.body.className = dark ? 'dark' : '';
|
document.body.className = dark ? 'dark' : '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -374,21 +375,21 @@ export class PSMain extends preact.Component {
|
||||||
try {
|
try {
|
||||||
const selection = window.getSelection()!;
|
const selection = window.getSelection()!;
|
||||||
if (selection.type === 'Range') return false;
|
if (selection.type === 'Range') return false;
|
||||||
} catch (err) {}
|
} catch {}
|
||||||
BattleTooltips.hideTooltip();
|
BattleTooltips.hideTooltip();
|
||||||
}
|
}
|
||||||
static posStyle(room: PSRoom) {
|
static posStyle(room: PSRoom) {
|
||||||
let pos: PanelPosition | null = null;
|
let pos: PanelPosition | null = null;
|
||||||
if (PS.leftRoomWidth === 0) {
|
if (PS.leftRoomWidth === 0) {
|
||||||
// one panel visible
|
// one panel visible
|
||||||
if (room === PS.activePanel) pos = {top: 56};
|
if (room === PS.activePanel) pos = { top: 56 };
|
||||||
} else {
|
} else {
|
||||||
// both panels visible
|
// both panels visible
|
||||||
if (room === PS.leftRoom) pos = {top: 56, right: PS.leftRoomWidth};
|
if (room === PS.leftRoom) pos = { top: 56, right: PS.leftRoomWidth };
|
||||||
if (room === PS.rightRoom) pos = {top: 56, left: PS.leftRoomWidth};
|
if (room === PS.rightRoom) pos = { top: 56, left: PS.leftRoomWidth };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pos) return {display: 'none'};
|
if (!pos) return { display: 'none' };
|
||||||
|
|
||||||
let top: number | null = (pos.top || 0);
|
let top: number | null = (pos.top || 0);
|
||||||
let height: number | null = null;
|
let height: number | null = null;
|
||||||
|
|
@ -422,7 +423,7 @@ export class PSMain extends preact.Component {
|
||||||
}
|
}
|
||||||
static getPopupStyle(room: PSRoom, width?: number | 'auto'): any {
|
static getPopupStyle(room: PSRoom, width?: number | 'auto'): any {
|
||||||
if (room.location === 'modal-popup' || !room.parentElem) {
|
if (room.location === 'modal-popup' || !room.parentElem) {
|
||||||
return {width: width || 480};
|
return { width: width || 480 };
|
||||||
}
|
}
|
||||||
if (!room.width || !room.height) {
|
if (!room.width || !room.height) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -513,15 +514,15 @@ export class PSMain extends preact.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return <div class="ps-frame">
|
return <div class="ps-frame">
|
||||||
<PSHeader style={{top: 0, left: 0, right: 0, height: '50px'}} />
|
<PSHeader style={{ top: 0, left: 0, right: 0, height: '50px' }} />
|
||||||
{rooms}
|
{rooms}
|
||||||
{PS.popups.map(roomid => this.renderPopup(PS.rooms[roomid]!))}
|
{PS.popups.map(roomid => this.renderPopup(PS.rooms[roomid]!))}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelPosition = {top?: number, bottom?: number, left?: number, right?: number} | null;
|
type PanelPosition = { top?: number, bottom?: number, left?: number, right?: number } | null;
|
||||||
|
|
||||||
export function SanitizedHTML(props: {children: string}) {
|
export function SanitizedHTML(props: { children: string }) {
|
||||||
return <div dangerouslySetInnerHTML={{__html: BattleLog.sanitizeHTML(props.children)}}/>;
|
return <div dangerouslySetInnerHTML={{ __html: BattleLog.sanitizeHTML(props.children) }} />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,9 +93,9 @@
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
<script nomodule src="/js/lib/ps-polyfill.js"></script>
|
||||||
<script src="../config/testclient-key.js"></script>
|
<script src="../config/testclient-key.js"></script>
|
||||||
<script src="js/client-core.js"></script>
|
<script src="js/client-core.js"></script>
|
||||||
<script nomodule src="/js/lib/ps-polyfill.js"></script>
|
|
||||||
|
|
||||||
<script src="js/battle-dex.js"></script>
|
<script src="js/battle-dex.js"></script>
|
||||||
<script src="js/battle-text-parser.js"></script>
|
<script src="js/battle-text-parser.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ var ReplaySidebarPanel = Panels.StaticPanel.extend({
|
||||||
if (!page) page = 2;
|
if (!page) page = 2;
|
||||||
var user = elem.dataset.user;
|
var user = elem.dataset.user;
|
||||||
var format = elem.dataset.format;
|
var format = elem.dataset.format;
|
||||||
var private = !!elem.dataset.private;
|
var priv = !!elem.dataset.private;
|
||||||
var self = this;
|
var self = this;
|
||||||
elem.innerHTML = 'Loading...<br /><i class="fa fa-caret-down"></i>'
|
elem.innerHTML = 'Loading...<br /><i class="fa fa-caret-down"></i>'
|
||||||
$.get('/search', Object.assign({
|
$.get('/search', Object.assign({
|
||||||
|
|
@ -35,7 +35,7 @@ var ReplaySidebarPanel = Panels.StaticPanel.extend({
|
||||||
format: format,
|
format: format,
|
||||||
page: page,
|
page: page,
|
||||||
output: 'html'
|
output: 'html'
|
||||||
}, private ? {private: 1} : {}), function (data) {
|
}, priv ? {private: 1} : {}), function (data) {
|
||||||
self.$('ul.linklist').append(data);
|
self.$('ul.linklist').append(data);
|
||||||
// var $nextOffset = self.$('input.offset');
|
// var $nextOffset = self.$('input.offset');
|
||||||
// var val = $nextOffset.val();
|
// var val = $nextOffset.val();
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,42 @@
|
||||||
/** @jsx preact.h */
|
/** @jsx preact.h */
|
||||||
import preact from 'preact';
|
import preact from '../../play.pokemonshowdown.com/js/lib/preact';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import {Net} from './utils';
|
import { Net } from './utils';
|
||||||
import {PSRouter, PSReplays} from './replays';
|
import { PSRouter, PSReplays } from './replays';
|
||||||
import {Battle} from '../../play.pokemonshowdown.com/src/battle';
|
import { Battle } from '../../play.pokemonshowdown.com/src/battle';
|
||||||
import {BattleLog} from '../../play.pokemonshowdown.com/src/battle-log';
|
import { BattleLog } from '../../play.pokemonshowdown.com/src/battle-log';
|
||||||
import {BattleSound} from '../../play.pokemonshowdown.com/src/battle-sound';
|
import { BattleSound } from '../../play.pokemonshowdown.com/src/battle-sound';
|
||||||
|
import type { ID } from '../../play.pokemonshowdown.com/src/battle-dex';
|
||||||
declare function toID(input: string): string;
|
declare function toID(input: string): string;
|
||||||
|
|
||||||
function showAd(id: string) {
|
function showAd(id: string) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error no clue how to declare this one
|
||||||
window.top.__vm_add = window.top.__vm_add || [];
|
window.top.__vm_add = window.top.__vm_add || [];
|
||||||
|
|
||||||
//this is a x-browser way to make sure content has loaded.
|
// this is a x-browser way to make sure content has loaded.
|
||||||
|
|
||||||
(function (success) {
|
(success => {
|
||||||
if (window.document.readyState !== "loading") {
|
if (window.document.readyState !== "loading") {
|
||||||
success();
|
success();
|
||||||
} else {
|
} else {
|
||||||
window.document.addEventListener("DOMContentLoaded", function () {
|
window.document.addEventListener("DOMContentLoaded", () => {
|
||||||
success();
|
success();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})(function () {
|
})(() => {
|
||||||
var placement = document.createElement("div");
|
const placement = document.createElement("div");
|
||||||
placement.setAttribute("class", "vm-placement");
|
placement.setAttribute("class", "vm-placement");
|
||||||
if (window.innerWidth > 1000) {
|
if (window.innerWidth > 1000) {
|
||||||
//load desktop placement
|
// load desktop placement
|
||||||
placement.setAttribute("data-id", "6452680c0b35755a3f09b59b");
|
placement.setAttribute("data-id", "6452680c0b35755a3f09b59b");
|
||||||
} else {
|
} else {
|
||||||
//load mobile placement
|
// load mobile placement
|
||||||
placement.setAttribute("data-id", "645268557bc7b571c2f06f62");
|
placement.setAttribute("data-id", "645268557bc7b571c2f06f62");
|
||||||
}
|
}
|
||||||
document.querySelector("#" + id)!.appendChild(placement);
|
document.querySelector("#" + id)!.appendChild(placement);
|
||||||
// @ts-expect-error
|
// @ts-expect-error no clue how to declare this one
|
||||||
window.top.__vm_add.push(placement);
|
window.top.__vm_add.push(placement);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BattleDiv extends preact.Component {
|
export class BattleDiv extends preact.Component {
|
||||||
|
|
@ -43,7 +44,7 @@ export class BattleDiv extends preact.Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
override render() {
|
override render() {
|
||||||
return <div class="battle" style={{position: 'relative'}}></div>;
|
return <div class="battle" style={{ position: 'relative' }}></div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class BattleLogDiv extends preact.Component {
|
class BattleLogDiv extends preact.Component {
|
||||||
|
|
@ -55,466 +56,479 @@ class BattleLogDiv extends preact.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BattlePanel extends preact.Component<{id: string}> {
|
export class BattlePanel extends preact.Component<{ id: string }> {
|
||||||
result: {
|
result: {
|
||||||
uploadtime: number;
|
uploadtime: number,
|
||||||
id: string;
|
id: string,
|
||||||
format: string;
|
format: string,
|
||||||
players: string[];
|
players: string[],
|
||||||
log: string;
|
log: string,
|
||||||
views: number;
|
views: number,
|
||||||
rating: number;
|
rating: number,
|
||||||
private: number;
|
private: number,
|
||||||
password: string;
|
password: string,
|
||||||
} | null | undefined = undefined;
|
} | null | undefined = undefined;
|
||||||
resultError = '';
|
resultError = '';
|
||||||
battle: Battle | null;
|
battle!: Battle | null;
|
||||||
/** debug purposes */
|
/** debug purposes */
|
||||||
lastUsedKeyCode = '0';
|
lastUsedKeyCode = '0';
|
||||||
turnView: boolean | string = false;
|
turnView: boolean | string = false;
|
||||||
autofocusTurnView: 'select' | 'end' | null = null;
|
autofocusTurnView: 'select' | 'end' | null = null;
|
||||||
override componentDidMount() {
|
override componentDidMount() {
|
||||||
this.loadBattle(this.props.id);
|
this.loadBattle(this.props.id);
|
||||||
showAd('LeaderboardBTF');
|
showAd('LeaderboardBTF');
|
||||||
window.onkeydown = this.keyPressed;
|
window.onkeydown = this.keyPressed;
|
||||||
}
|
}
|
||||||
override componentWillReceiveProps(nextProps) {
|
override componentWillReceiveProps(nextProps: this['props']) {
|
||||||
if (this.stripQuery(this.props.id) !== this.stripQuery(nextProps.id)) {
|
if (this.stripQuery(this.props.id) !== this.stripQuery(nextProps.id)) {
|
||||||
this.loadBattle(nextProps.id);
|
this.loadBattle(nextProps.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stripQuery(id: string) {
|
stripQuery(id: string) {
|
||||||
return id.includes('?') ? id.slice(0, id.indexOf('?')) : id;
|
return id.includes('?') ? id.slice(0, id.indexOf('?')) : id;
|
||||||
}
|
}
|
||||||
loadBattle(id: string) {
|
loadBattle(id: string) {
|
||||||
if (this.battle) this.battle.destroy();
|
if (this.battle) this.battle.destroy();
|
||||||
this.battle = null;
|
this.battle = null;
|
||||||
this.result = undefined;
|
this.result = undefined;
|
||||||
this.resultError = '';
|
this.resultError = '';
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
|
|
||||||
const elem = document.getElementById(`replaydata-${id}`);
|
const elem = document.getElementById(`replaydata-${id}`);
|
||||||
const logElem = document.getElementById(`replaylog-${id}`);
|
const logElem = document.getElementById(`replaylog-${id}`);
|
||||||
if (elem) {
|
if (elem) {
|
||||||
// we actually do need to wait for that update to finish so
|
// we actually do need to wait for that update to finish so
|
||||||
// loadResult definitely has access to $frame and $logFrame
|
// loadResult definitely has access to $frame and $logFrame
|
||||||
setTimeout(() => this.loadResult(elem.innerText, id, logElem?.innerText.replace(/<\\\//g, '</')), 1);
|
setTimeout(() => this.loadResult(elem.innerText, id, logElem?.innerText.replace(/<\\\//g, '</')), 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Net(`/${this.stripQuery(id)}.json`).get().then(result => {
|
Net(`/${this.stripQuery(id)}.json`).get().then(result => {
|
||||||
this.loadResult(result, id);
|
this.loadResult(result, id);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
this.loadResult(err.statusCode === 404 ? '' : String(err?.body || ''), id);
|
this.loadResult(err.statusCode === 404 ? '' : String(err?.body || ''), id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
loadResult(result: string, id: string, log = '') {
|
loadResult(result: string, id: string, log = '') {
|
||||||
try {
|
try {
|
||||||
const replay: NonNullable<BattlePanel['result']> = JSON.parse(result);
|
const replay: NonNullable<BattlePanel['result']> = JSON.parse(result);
|
||||||
replay.log ||= log;
|
replay.log ||= log;
|
||||||
this.result = replay;
|
this.result = replay;
|
||||||
const $base = $(this.base!);
|
const $base = $(this.base!);
|
||||||
this.battle = new Battle({
|
this.battle = new Battle({
|
||||||
id: replay.id,
|
id: replay.id as ID,
|
||||||
$frame: $base.find('.battle'),
|
$frame: $base.find('.battle'),
|
||||||
$logFrame: $base.find('.battle-log'),
|
$logFrame: $base.find('.battle-log'),
|
||||||
log: replay.log.split('\n'),
|
log: replay.log.split('\n'),
|
||||||
isReplay: true,
|
isReplay: true,
|
||||||
paused: true,
|
paused: true,
|
||||||
autoresize: true,
|
autoresize: true,
|
||||||
});
|
});
|
||||||
// for ease of debugging
|
// for ease of debugging
|
||||||
(window as any).battle = this.battle;
|
(window as any).battle = this.battle;
|
||||||
this.battle.subscribe(_ => {
|
this.battle.subscribe(_ => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
});
|
});
|
||||||
const query = Net.decodeQuery(id);
|
const query = Net.decodeQuery(id);
|
||||||
if ('p2' in query) {
|
if ('p2' in query) {
|
||||||
this.battle.switchViewpoint();
|
this.battle.switchViewpoint();
|
||||||
}
|
}
|
||||||
if (query.turn || query.t) {
|
if (query.turn || query.t) {
|
||||||
this.battle.seekTurn(parseInt(query.turn || query.t, 10));
|
this.battle.seekTurn(parseInt(query.turn || query.t, 10));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this.result = null;
|
this.result = null;
|
||||||
this.resultError = result.startsWith('{') ? err.toString() : result;
|
this.resultError = result.startsWith('{') ? err.toString() : result;
|
||||||
}
|
}
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
override componentWillUnmount(): void {
|
override componentWillUnmount(): void {
|
||||||
this.battle?.destroy();
|
this.battle?.destroy();
|
||||||
(window as any).battle = null;
|
(window as any).battle = null;
|
||||||
window.onkeydown = null;
|
window.onkeydown = null;
|
||||||
}
|
}
|
||||||
override componentDidUpdate(): void {
|
override componentDidUpdate(): void {
|
||||||
if (this.autofocusTurnView === 'select') {
|
if (this.autofocusTurnView === 'select') {
|
||||||
this.base?.querySelector<HTMLInputElement>('input[name=turn]')?.select();
|
this.base?.querySelector<HTMLInputElement>('input[name=turn]')?.select();
|
||||||
this.autofocusTurnView = null;
|
this.autofocusTurnView = null;
|
||||||
}
|
}
|
||||||
if (this.autofocusTurnView === 'end') {
|
if (this.autofocusTurnView === 'end') {
|
||||||
const turnbox = this.base?.querySelector<HTMLInputElement>('input[name=turn]');
|
const turnbox = this.base?.querySelector<HTMLInputElement>('input[name=turn]');
|
||||||
turnbox?.setSelectionRange(2, 2);
|
turnbox?.setSelectionRange(2, 2);
|
||||||
turnbox?.focus();
|
turnbox?.focus();
|
||||||
this.autofocusTurnView = null;
|
this.autofocusTurnView = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keyPressed = (e: KeyboardEvent) => {
|
keyPressed = (e: KeyboardEvent) => {
|
||||||
// @ts-ignore
|
this.lastUsedKeyCode = `${e.keyCode}`;
|
||||||
this.lastUsedKeyCode = `${e.keyCode}`;
|
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
||||||
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
if (e.keyCode === 27 && this.turnView) { // Esc
|
||||||
if (e.keyCode === 27 && this.turnView) { // Esc
|
this.closeTurn();
|
||||||
this.closeTurn();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
// @ts-expect-error really wish they let me assert that the target is an HTMLElement
|
||||||
// @ts-ignore
|
if (e.target?.tagName === 'INPUT' || e.target?.tagName === 'SELECT') return;
|
||||||
if (e.target?.tagName === 'INPUT' || e.target?.tagName === 'SELECT') return;
|
switch (e.keyCode) {
|
||||||
switch (e.keyCode) {
|
case 75: // k
|
||||||
case 75: // k
|
if (this.battle?.atQueueEnd) {
|
||||||
if (this.battle?.atQueueEnd) {
|
this.replay();
|
||||||
this.replay();
|
} else if (this.battle?.paused) {
|
||||||
} else if (this.battle?.paused) {
|
this.play();
|
||||||
this.play();
|
} else {
|
||||||
} else {
|
this.pause();
|
||||||
this.pause();
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case 74: // j
|
||||||
case 74: // j
|
if (e.shiftKey) this.firstTurn();
|
||||||
if (e.shiftKey) this.firstTurn();
|
else this.prevTurn();
|
||||||
else this.prevTurn();
|
break;
|
||||||
break;
|
case 76: // l
|
||||||
case 76: // l
|
if (e.shiftKey) this.lastTurn();
|
||||||
if (e.shiftKey) this.lastTurn();
|
else this.nextTurn();
|
||||||
else this.nextTurn();
|
break;
|
||||||
break;
|
case 188: // , (<)
|
||||||
case 188: // , (<)
|
if (e.shiftKey) this.stepSpeed(-1);
|
||||||
if (e.shiftKey) this.stepSpeed(-1);
|
break;
|
||||||
break;
|
case 190: // . (>)
|
||||||
case 190: // . (>)
|
if (e.shiftKey) this.stepSpeed(1);
|
||||||
if (e.shiftKey) this.stepSpeed(1);
|
break;
|
||||||
break;
|
case 191: // / (?)
|
||||||
case 191: // / (?)
|
if (e.shiftKey) {
|
||||||
if (e.shiftKey) {
|
alert(
|
||||||
alert(
|
'k = play/pause\n' +
|
||||||
'k = play/pause\n' +
|
'j = previous turn\n' +
|
||||||
'j = previous turn\n' +
|
'l = next turn\n' +
|
||||||
'l = next turn\n' +
|
'J = first turn\n' +
|
||||||
'J = first turn\n' +
|
'L = last turn\n' +
|
||||||
'L = last turn\n' +
|
'm = mute\n' +
|
||||||
'm = mute\n' +
|
'< = slower\n' +
|
||||||
'< = slower\n' +
|
'> = faster\n' +
|
||||||
'> = faster\n' +
|
'1-9 = skip to turn\n' +
|
||||||
'1-9 = skip to turn\n' +
|
'? = keyboard shortcuts (this)\n'
|
||||||
'? = keyboard shortcuts (this)\n'
|
);
|
||||||
);
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 0-9
|
||||||
case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 0-9
|
case 96: case 97: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: // numpad 0-9
|
||||||
case 96: case 97: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: // numpad 0-9
|
this.turnView = String.fromCharCode(e.keyCode - (e.keyCode >= 96 ? 48 : 0));
|
||||||
this.turnView = String.fromCharCode(e.keyCode - (e.keyCode >= 96 ? 48 : 0));
|
if (this.turnView === '0') this.turnView = '10';
|
||||||
if (this.turnView === '0') this.turnView = '10';
|
this.autofocusTurnView = 'end';
|
||||||
this.autofocusTurnView = 'end';
|
e.preventDefault();
|
||||||
e.preventDefault();
|
this.forceUpdate();
|
||||||
this.forceUpdate();
|
break;
|
||||||
break;
|
case 77: // m
|
||||||
case 77: // m
|
this.toggleMute();
|
||||||
this.toggleMute();
|
break;
|
||||||
break;
|
}
|
||||||
}
|
this.forceUpdate();
|
||||||
this.forceUpdate();
|
};
|
||||||
};
|
play = () => {
|
||||||
play = () => {
|
this.battle?.play();
|
||||||
this.battle?.play();
|
};
|
||||||
};
|
replay = () => {
|
||||||
replay = () => {
|
this.battle?.reset();
|
||||||
this.battle?.reset();
|
this.battle?.play();
|
||||||
this.battle?.play();
|
this.forceUpdate();
|
||||||
this.forceUpdate();
|
};
|
||||||
};
|
pause = () => {
|
||||||
pause = () => {
|
this.battle?.pause();
|
||||||
this.battle?.pause();
|
};
|
||||||
};
|
nextTurn = () => {
|
||||||
nextTurn = () => {
|
this.battle?.seekBy(1);
|
||||||
this.battle?.seekBy(1);
|
};
|
||||||
};
|
prevTurn = () => {
|
||||||
prevTurn = () => {
|
this.battle?.seekBy(-1);
|
||||||
this.battle?.seekBy(-1);
|
};
|
||||||
};
|
firstTurn = () => {
|
||||||
firstTurn = () => {
|
this.battle?.seekTurn(0);
|
||||||
this.battle?.seekTurn(0);
|
};
|
||||||
};
|
lastTurn = () => {
|
||||||
lastTurn = () => {
|
this.battle?.seekTurn(Infinity);
|
||||||
this.battle?.seekTurn(Infinity);
|
};
|
||||||
};
|
goToTurn = (e: Event) => {
|
||||||
goToTurn = (e) => {
|
|
||||||
const turn = this.base?.querySelector<HTMLInputElement>('input[name=turn]')?.value;
|
const turn = this.base?.querySelector<HTMLInputElement>('input[name=turn]')?.value;
|
||||||
if (!turn?.trim()) return this.closeTurn(e);
|
if (!turn?.trim()) return this.closeTurn(e);
|
||||||
let turnNum = Number(turn);
|
let turnNum = Number(turn);
|
||||||
if (turn === 'e' || turn === 'end' || turn === 'f' || turn === 'finish') turnNum = Infinity;
|
if (turn === 'e' || turn === 'end' || turn === 'f' || turn === 'finish') turnNum = Infinity;
|
||||||
if (isNaN(turnNum) || turnNum < 0) alert("Invalid turn");
|
if (isNaN(turnNum) || turnNum < 0) alert("Invalid turn");
|
||||||
this.battle?.seekTurn(turnNum);
|
this.battle?.seekTurn(turnNum);
|
||||||
this.closeTurn(e);
|
this.closeTurn(e);
|
||||||
};
|
};
|
||||||
switchViewpoint = () => {
|
switchViewpoint = () => {
|
||||||
this.battle?.switchViewpoint();
|
this.battle?.switchViewpoint();
|
||||||
if (this.battle?.viewpointSwitched) {
|
if (this.battle?.viewpointSwitched) {
|
||||||
PSRouter.replace(this.stripQuery(this.props.id) + '?p2');
|
PSRouter.replace(this.stripQuery(this.props.id) + '?p2');
|
||||||
} else {
|
} else {
|
||||||
PSRouter.replace(this.stripQuery(this.props.id));
|
PSRouter.replace(this.stripQuery(this.props.id));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
clickDownload = (e: MouseEvent) => {
|
clickDownload = (e: MouseEvent) => {
|
||||||
if (!this.battle) {
|
if (!this.battle) {
|
||||||
// should never happen
|
// should never happen
|
||||||
alert("Wait for the battle to finish loading before downloading.");
|
alert("Wait for the battle to finish loading before downloading.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let filename = (this.battle.tier || 'Battle').replace(/[^A-Za-z0-9]/g, '');
|
let filename = (this.battle.tier || 'Battle').replace(/[^A-Za-z0-9]/g, '');
|
||||||
|
|
||||||
// ladies and gentlemen, JavaScript dates
|
// ladies and gentlemen, JavaScript dates
|
||||||
const timestamp = (this.result?.uploadtime || 0) * 1000;
|
const timestamp = (this.result?.uploadtime || 0) * 1000;
|
||||||
const date = new Date(timestamp);
|
const date = new Date(timestamp);
|
||||||
filename += '-' + date.getFullYear();
|
filename += `-${date.getFullYear()}`;
|
||||||
filename += (date.getMonth() >= 9 ? '-' : '-0') + (date.getMonth() + 1);
|
filename += `${date.getMonth() >= 9 ? '-' : '-0'}${date.getMonth() + 1}`;
|
||||||
filename += (date.getDate() >= 10 ? '-' : '-0') + date.getDate();
|
filename += `${date.getDate() >= 10 ? '-' : '-0'}${date.getDate()}`;
|
||||||
|
|
||||||
filename += '-' + toID(this.battle.p1.name);
|
filename += '-' + toID(this.battle.p1.name);
|
||||||
filename += '-' + toID(this.battle.p2.name);
|
filename += '-' + toID(this.battle.p2.name);
|
||||||
|
|
||||||
const a = e.currentTarget as HTMLAnchorElement;
|
const a = e.currentTarget as HTMLAnchorElement;
|
||||||
a.href = BattleLog.createReplayFileHref({battle: this.battle});
|
a.href = BattleLog.createReplayFileHref({ battle: this.battle });
|
||||||
a.download = filename + '.html';
|
a.download = filename + '.html';
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
getSpeed() {
|
getSpeed() {
|
||||||
if (!this.battle) return 'normal';
|
if (!this.battle) return 'normal';
|
||||||
if (this.battle.messageFadeTime <= 40) {
|
if (this.battle.messageFadeTime <= 40) {
|
||||||
return 'hyperfast';
|
return 'hyperfast';
|
||||||
} else if (this.battle.messageFadeTime <= 50) {
|
} else if (this.battle.messageFadeTime <= 50) {
|
||||||
return 'fast';
|
return 'fast';
|
||||||
} else if (this.battle.messageFadeTime >= 500) {
|
} else if (this.battle.messageFadeTime >= 500) {
|
||||||
return 'slow';
|
return 'slow';
|
||||||
} else if (this.battle.messageFadeTime >= 1000) {
|
} else if (this.battle.messageFadeTime >= 1000) {
|
||||||
return 'reallyslow';
|
return 'reallyslow';
|
||||||
}
|
}
|
||||||
return 'normal';
|
return 'normal';
|
||||||
}
|
}
|
||||||
changeSpeed = (e: Event | {target: HTMLSelectElement}) => {
|
changeSpeed = (e: Event | { target: HTMLSelectElement }) => {
|
||||||
const speed = (e.target as HTMLSelectElement).value;
|
const speed = (e.target as HTMLSelectElement).value;
|
||||||
const fadeTable = {
|
const fadeTable = {
|
||||||
hyperfast: 40,
|
hyperfast: 40,
|
||||||
fast: 50,
|
fast: 50,
|
||||||
normal: 300,
|
normal: 300,
|
||||||
slow: 500,
|
slow: 500,
|
||||||
reallyslow: 1000
|
reallyslow: 1000,
|
||||||
};
|
};
|
||||||
const delayTable = {
|
const delayTable = {
|
||||||
hyperfast: 1,
|
hyperfast: 1,
|
||||||
fast: 1,
|
fast: 1,
|
||||||
normal: 1,
|
normal: 1,
|
||||||
slow: 1000,
|
slow: 1000,
|
||||||
reallyslow: 3000
|
reallyslow: 3000,
|
||||||
};
|
};
|
||||||
if (!this.battle) return;
|
if (!this.battle) return;
|
||||||
this.battle.messageShownTime = delayTable[speed];
|
this.battle.messageShownTime = delayTable[speed as 'fast'];
|
||||||
this.battle.messageFadeTime = fadeTable[speed];
|
this.battle.messageFadeTime = fadeTable[speed as 'fast'];
|
||||||
this.battle.scene.updateAcceleration();
|
this.battle.scene.updateAcceleration();
|
||||||
};
|
};
|
||||||
stepSpeed(delta: number) {
|
stepSpeed(delta: number) {
|
||||||
const target = this.base?.querySelector<HTMLSelectElement>('select[name=speed]');
|
const target = this.base?.querySelector<HTMLSelectElement>('select[name=speed]');
|
||||||
if (!target) return; // should never happen
|
if (!target) return; // should never happen
|
||||||
const values = ['reallyslow', 'slow', 'normal', 'fast', 'hyperfast'];
|
const values = ['reallyslow', 'slow', 'normal', 'fast', 'hyperfast'];
|
||||||
const newValue = values[values.indexOf(target.value) + delta];
|
const newValue = values[values.indexOf(target.value) + delta];
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
target.value = newValue;
|
target.value = newValue;
|
||||||
this.changeSpeed({target});
|
this.changeSpeed({ target });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toggleMute() {
|
toggleMute() {
|
||||||
this.battle?.setMute(!BattleSound.muted);
|
this.battle?.setMute(!BattleSound.muted);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
changeSound = (e: Event) => {
|
changeSound = (e: Event) => {
|
||||||
const muted = (e.target as HTMLSelectElement).value;
|
const muted = (e.target as HTMLSelectElement).value;
|
||||||
this.battle?.setMute(muted === 'off');
|
this.battle?.setMute(muted === 'off');
|
||||||
// Wolfram Alpha says that default volume is 100 e^(-(2 log^2(2))/log(10)) which is around 65.881
|
// Wolfram Alpha says that default volume is 100 e^(-(2 log^2(2))/log(10)) which is around 65.881
|
||||||
BattleSound.setBgmVolume(muted === 'musicoff' ? 0 : 65.881258001265573);
|
BattleSound.setBgmVolume(muted === 'musicoff' ? 0 : 65.88125800126558);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
changeDarkMode = (e: Event) => {
|
changeDarkMode = (e: Event) => {
|
||||||
const darkmode = (e.target as HTMLSelectElement).value as 'dark';
|
const darkmode = (e.target as HTMLSelectElement).value as 'dark';
|
||||||
PSReplays.darkMode = darkmode;
|
PSReplays.darkMode = darkmode;
|
||||||
PSReplays.updateDarkMode();
|
PSReplays.updateDarkMode();
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
openTurn = (e: Event) => {
|
openTurn = (e: Event) => {
|
||||||
this.turnView = `${this.battle?.turn}` || true;
|
this.turnView = `${this.battle?.turn || ''}` || true;
|
||||||
this.autofocusTurnView = 'select';
|
this.autofocusTurnView = 'select';
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
closeTurn = (e?: Event) => {
|
closeTurn = (e?: Event) => {
|
||||||
this.turnView = false;
|
this.turnView = false;
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
renderError(position: any) {
|
renderError(position: any) {
|
||||||
if (this.resultError) {
|
if (this.resultError) {
|
||||||
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}><section class="section">
|
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}>
|
||||||
<h1>Error</h1>
|
<section class="section">
|
||||||
<p>
|
<h1>Error</h1>
|
||||||
{this.resultError}
|
<p>
|
||||||
</p>
|
{this.resultError}
|
||||||
</section></div>;
|
</p>
|
||||||
}
|
</section>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
// In theory, this should almost never happen, because Replays will
|
// In theory, this should almost never happen, because Replays will
|
||||||
// never link to a nonexistent replay, but this might happen if e.g.
|
// never link to a nonexistent replay, but this might happen if e.g.
|
||||||
// a replay gets deleted or made private after you searched for it
|
// a replay gets deleted or made private after you searched for it
|
||||||
// but before you clicked it.
|
// but before you clicked it.
|
||||||
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}><section class="section" style={{maxWidth: '200px'}}>
|
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}>
|
||||||
<div style={{textAlign: 'center'}}>
|
<section class="section" style={{ maxWidth: '200px' }}>
|
||||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-n.gif" alt="" style={{imageRendering: 'pixelated'}} />
|
<div style={{ textAlign: 'center' }}>
|
||||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-o.gif" alt="" style={{imageRendering: 'pixelated'}} />
|
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-n.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-t.gif" alt="" style={{imageRendering: 'pixelated'}} />
|
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-o.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||||
</div>
|
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-t.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||||
<div style={{textAlign: 'center'}}>
|
</div>
|
||||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-f.gif" alt="" style={{imageRendering: 'pixelated'}} />
|
<div style={{ textAlign: 'center' }}>
|
||||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-o.gif" alt="" style={{imageRendering: 'pixelated'}} />
|
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-f.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-u.gif" alt="" style={{imageRendering: 'pixelated'}} />
|
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-o.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-n.gif" alt="" style={{imageRendering: 'pixelated'}} />
|
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-u.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-d.gif" alt="" style={{imageRendering: 'pixelated'}} />
|
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-n.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||||
</div>
|
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-d.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||||
</section><section class="section">
|
</div>
|
||||||
<h1>Not Found</h1>
|
</section><section class="section">
|
||||||
|
<h1>Not Found</h1>
|
||||||
|
<p>
|
||||||
|
The battle you're looking for has expired. Battles expire after 15 minutes of inactivity unless they're saved.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In the future, remember to click <strong>Upload and share replay</strong> to save a replay permanently.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
renderControls() {
|
||||||
|
const atEnd = !this.battle || this.battle.atQueueEnd;
|
||||||
|
const atStart = !this.battle?.started;
|
||||||
|
|
||||||
|
if (this.turnView) {
|
||||||
|
const value = this.turnView === true ? undefined : this.turnView;
|
||||||
|
this.turnView = true;
|
||||||
|
return <div class="replay-controls"><section class="section">
|
||||||
|
<form onSubmit={this.goToTurn}>
|
||||||
|
Turn? <input name="turn" autofocus value={value} inputMode="numeric" class="textbox" size={5} /> {}
|
||||||
|
<button type="submit" class="button"><strong>Go</strong></button> {}
|
||||||
|
<button type="button" class="button" onClick={this.closeTurn}>Cancel</button>
|
||||||
|
</form>
|
||||||
|
<p>
|
||||||
|
<em>Pro tip:</em> You don't need to click "Skip to turn" if you have a keyboard, just start typing
|
||||||
|
the turn number and press <kbd>Enter</kbd>. For more shortcuts, press <kbd>Shift</kbd>+<kbd>/</kbd>
|
||||||
|
when a text box isn't focused.
|
||||||
|
</p>
|
||||||
|
</section></div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div class="replay-controls">
|
||||||
<p>
|
<p>
|
||||||
The battle you're looking for has expired. Battles expire after 15 minutes of inactivity unless they're saved.
|
{atEnd && this.battle ? (
|
||||||
|
<button onClick={this.replay} class="button" style={{ width: '5em', marginRight: '3px' }}>
|
||||||
|
<i class="fa fa-undo"></i><br />Replay
|
||||||
|
</button>
|
||||||
|
) : !this.battle || this.battle.paused ? (
|
||||||
|
<button onClick={this.play} class="button" disabled={!this.battle} style={{ width: '5em', marginRight: '3px' }}>
|
||||||
|
<i class="fa fa-play"></i><br /><strong>Play</strong>
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button onClick={this.pause} class="button" style={{ width: '5em', marginRight: '3px' }}>
|
||||||
|
<i class="fa fa-pause"></i><br /><strong>Pause</strong>
|
||||||
|
</button>
|
||||||
|
)} {}
|
||||||
|
<button class="button button-first" disabled={atStart} onClick={this.firstTurn}>
|
||||||
|
<i class="fa fa-fast-backward"></i><br />First turn
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button-first" disabled={atStart} style={{ marginLeft: '1px', position: 'relative', zIndex: '1' }}
|
||||||
|
onClick={this.prevTurn}
|
||||||
|
>
|
||||||
|
<i class="fa fa-step-backward"></i><br />Prev turn
|
||||||
|
</button>
|
||||||
|
<button class="button button-last" disabled={atEnd} style={{ marginRight: '2px' }} onClick={this.nextTurn}>
|
||||||
|
<i class="fa fa-step-forward"></i><br />Skip turn
|
||||||
|
</button>
|
||||||
|
<button class="button button-last" disabled={atEnd} onClick={this.lastTurn}>
|
||||||
|
<i class="fa fa-fast-forward"></i><br />Skip to end
|
||||||
|
</button> {}
|
||||||
|
<button class="button" onClick={this.openTurn}>
|
||||||
|
<i class="fa fa-repeat"></i> Go to turn...
|
||||||
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In the future, remember to click <strong>Upload and share replay</strong> to save a replay permanently.
|
<label class="optgroup">
|
||||||
|
Speed:<br />
|
||||||
|
<select name="speed" class="button" onChange={this.changeSpeed} value={this.getSpeed()}>
|
||||||
|
<option value="hyperfast">Hyperfast</option>
|
||||||
|
<option value="fast">Fast</option>
|
||||||
|
<option value="normal">Normal</option>
|
||||||
|
<option value="slow">Slow</option>
|
||||||
|
<option value="reallyslow">Really slow</option>
|
||||||
|
</select>
|
||||||
|
</label> {}
|
||||||
|
<label class="optgroup">
|
||||||
|
Sound:<br />
|
||||||
|
<select
|
||||||
|
name="sound" class="button" onChange={this.changeSound}
|
||||||
|
value={BattleSound.muted ? 'off' : BattleSound.bgmVolume ? 'on' : 'musicoff'}
|
||||||
|
>
|
||||||
|
<option value="on">On</option>
|
||||||
|
<option value="musicoff">Music Off</option>
|
||||||
|
<option value="off">Muted</option>
|
||||||
|
</select>
|
||||||
|
</label> {}
|
||||||
|
<label class="optgroup">
|
||||||
|
Dark mode:<br />
|
||||||
|
<select name="darkmode" class="button" onChange={this.changeDarkMode} value={PSReplays.darkMode}>
|
||||||
|
<option value="auto">Automatic</option>
|
||||||
|
<option value="dark">Dark</option>
|
||||||
|
<option value="light">Light</option>
|
||||||
|
</select>
|
||||||
|
</label> {}
|
||||||
|
<label class="optgroup">
|
||||||
|
Viewpoint:<br />
|
||||||
|
<button onClick={this.switchViewpoint} name="viewpoint" class={this.battle ? 'button' : 'button disabled'}>
|
||||||
|
{(this.battle?.viewpointSwitched ? this.result?.players[1] : this.result?.players[0] || "Player")} {}
|
||||||
|
<i class="fa fa-random" aria-label="Switch viewpoint"></i>
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
</p>
|
</p>
|
||||||
</section></div>;
|
{this.result ? <h1>
|
||||||
}
|
<strong>{this.result.format}</strong>: {this.result.players.join(' vs. ')}
|
||||||
renderControls() {
|
</h1> : <h1>
|
||||||
const atEnd = !this.battle || this.battle.atQueueEnd;
|
<em>Loading...</em>
|
||||||
const atStart = !this.battle?.started;
|
</h1>}
|
||||||
|
{this.result ? <p>
|
||||||
|
<a class="button" href="#" onClick={this.clickDownload} style={{ float: 'right' }}>
|
||||||
|
<i class="fa fa-download" aria-hidden></i> Download
|
||||||
|
</a>
|
||||||
|
{this.result.uploadtime ? new Date(this.result.uploadtime * 1000).toDateString() : "Unknown upload date"}
|
||||||
|
{this.result.rating ? [` | `, <em>Rating:</em>, ` ${this.result.rating}`] : ''}
|
||||||
|
{/* {} <code>{this.keyCode}</code> */}
|
||||||
|
</p> : <p> </p>}
|
||||||
|
{!PSRouter.showingLeft() && <p>
|
||||||
|
<a href={PSRouter.leftLoc || '.'} class="button"><i class="fa fa-caret-left"></i> More replays</a>
|
||||||
|
</p>}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
override render() {
|
||||||
|
let position: any = {};
|
||||||
|
if (PSRouter.showingLeft()) {
|
||||||
|
if (PSRouter.stickyRight) {
|
||||||
|
position = { position: 'sticky', top: '0' };
|
||||||
|
} else {
|
||||||
|
position = { position: 'sticky', bottom: '0' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.turnView) {
|
if (this.result === null) return this.renderError(position);
|
||||||
const value = this.turnView === true ? undefined : this.turnView;
|
|
||||||
this.turnView = true;
|
|
||||||
return <div class="replay-controls"><section class="section">
|
|
||||||
<form onSubmit={this.goToTurn}>
|
|
||||||
Turn? <input name="turn" autofocus value={value} inputMode="numeric" class="textbox" size={5} /> {}
|
|
||||||
<button type="submit" class="button"><strong>Go</strong></button> {}
|
|
||||||
<button type="button" class="button" onClick={this.closeTurn}>Cancel</button>
|
|
||||||
</form>
|
|
||||||
<p>
|
|
||||||
<em>Pro tip:</em> You don't need to click "Skip to turn" if you have a keyboard, just start typing the turn number and press <kbd>Enter</kbd>. For more shortcuts, press <kbd>Shift</kbd>+<kbd>/</kbd> when a text box isn't focused.
|
|
||||||
</p>
|
|
||||||
</section></div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div class="replay-controls">
|
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}>
|
||||||
<p>
|
<div style={{ position: 'relative' }}>
|
||||||
{atEnd && this.battle ?
|
<BattleDiv />
|
||||||
<button onClick={this.replay} class="button" style={{width: '5em', marginRight: '3px'}}>
|
<BattleLogDiv />
|
||||||
<i class="fa fa-undo"></i><br />Replay
|
{this.renderControls()}
|
||||||
</button>
|
<div id="LeaderboardBTF"></div>
|
||||||
: (!this.battle || this.battle.paused) ?
|
</div>
|
||||||
<button onClick={this.play} class="button" disabled={!this.battle} style={{width: '5em', marginRight: '3px'}}>
|
</div>;
|
||||||
<i class="fa fa-play"></i><br /><strong>Play</strong>
|
}
|
||||||
</button>
|
|
||||||
:
|
|
||||||
<button onClick={this.pause} class="button" style={{width: '5em', marginRight: '3px'}}>
|
|
||||||
<i class="fa fa-pause"></i><br /><strong>Pause</strong>
|
|
||||||
</button>
|
|
||||||
} {}
|
|
||||||
<button class="button button-first" disabled={atStart} onClick={this.firstTurn}>
|
|
||||||
<i class="fa fa-fast-backward"></i><br />First turn
|
|
||||||
</button>
|
|
||||||
<button class="button button-first" disabled={atStart} style={{marginLeft:'1px',position:'relative',zIndex:'1'}} onClick={this.prevTurn}>
|
|
||||||
<i class="fa fa-step-backward"></i><br />Prev turn
|
|
||||||
</button>
|
|
||||||
<button class="button button-last" disabled={atEnd} style={{marginRight:'2px'}} onClick={this.nextTurn}>
|
|
||||||
<i class="fa fa-step-forward"></i><br />Skip turn
|
|
||||||
</button>
|
|
||||||
<button class="button button-last" disabled={atEnd} onClick={this.lastTurn}>
|
|
||||||
<i class="fa fa-fast-forward"></i><br />Skip to end
|
|
||||||
</button> {}
|
|
||||||
<button class="button" onClick={this.openTurn}>
|
|
||||||
<i class="fa fa-repeat"></i> Go to turn...
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label class="optgroup">
|
|
||||||
Speed:<br />
|
|
||||||
<select name="speed" class="button" onChange={this.changeSpeed} value={this.getSpeed()}>
|
|
||||||
<option value="hyperfast">Hyperfast</option>
|
|
||||||
<option value="fast">Fast</option>
|
|
||||||
<option value="normal">Normal</option>
|
|
||||||
<option value="slow">Slow</option>
|
|
||||||
<option value="reallyslow">Really slow</option>
|
|
||||||
</select>
|
|
||||||
</label> {}
|
|
||||||
<label class="optgroup">
|
|
||||||
Sound:<br />
|
|
||||||
<select name="sound" class="button" onChange={this.changeSound} value={BattleSound.muted ? 'off' : BattleSound.bgmVolume ? 'on' : 'musicoff'}>
|
|
||||||
<option value="on">On</option>
|
|
||||||
<option value="musicoff">Music Off</option>
|
|
||||||
<option value="off">Muted</option>
|
|
||||||
</select>
|
|
||||||
</label> {}
|
|
||||||
<label class="optgroup">
|
|
||||||
Dark mode:<br />
|
|
||||||
<select name="darkmode" class="button" onChange={this.changeDarkMode} value={PSReplays.darkMode}>
|
|
||||||
<option value="auto">Automatic</option>
|
|
||||||
<option value="dark">Dark</option>
|
|
||||||
<option value="light">Light</option>
|
|
||||||
</select>
|
|
||||||
</label> {}
|
|
||||||
<label class="optgroup">
|
|
||||||
Viewpoint:<br />
|
|
||||||
<button onClick={this.switchViewpoint} name="viewpoint" class={this.battle ? 'button' : 'button disabled'}>
|
|
||||||
{(this.battle?.viewpointSwitched ? this.result?.players[1] : this.result?.players[0] || "Player")} {}
|
|
||||||
<i class="fa fa-random" aria-label="Switch viewpoint"></i>
|
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
{this.result ? <h1>
|
|
||||||
<strong>{this.result.format}</strong>: {this.result.players.join(' vs. ')}
|
|
||||||
</h1> : <h1>
|
|
||||||
<em>Loading...</em>
|
|
||||||
</h1>}
|
|
||||||
{this.result ? <p>
|
|
||||||
<a class="button" href="#" onClick={this.clickDownload} style={{float: 'right'}}>
|
|
||||||
<i class="fa fa-download" aria-hidden></i> Download
|
|
||||||
</a>
|
|
||||||
{this.result.uploadtime ? new Date(this.result.uploadtime * 1000).toDateString() : "Unknown upload date"}
|
|
||||||
{this.result.rating ? [` | `, <em>Rating:</em>, ` ${this.result.rating}`] : ''}
|
|
||||||
{/* {} <code>{this.keyCode}</code> */}
|
|
||||||
</p> : <p> </p>}
|
|
||||||
{!PSRouter.showingLeft() && <p>
|
|
||||||
<a href={PSRouter.leftLoc || '.'} class="button"><i class="fa fa-caret-left"></i> More replays</a>
|
|
||||||
</p>}
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
override render() {
|
|
||||||
let position: any = {};
|
|
||||||
if (PSRouter.showingLeft()) {
|
|
||||||
if (PSRouter.stickyRight) {
|
|
||||||
position = {position: 'sticky', top: '0'};
|
|
||||||
} else {
|
|
||||||
position = {position: 'sticky', bottom: '0'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.result === null) return this.renderError(position);
|
|
||||||
|
|
||||||
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}><div style={{position: 'relative'}}>
|
|
||||||
<BattleDiv />
|
|
||||||
<BattleLogDiv />
|
|
||||||
{this.renderControls()}
|
|
||||||
<div id="LeaderboardBTF"></div>
|
|
||||||
</div></div>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,484 +1,513 @@
|
||||||
/** @jsx preact.h */
|
/** @jsx preact.h */
|
||||||
import preact from 'preact';
|
import preact from '../../play.pokemonshowdown.com/js/lib/preact';
|
||||||
import {Net, PSModel} from './utils';
|
import { Net, PSModel } from './utils';
|
||||||
import {BattlePanel} from './replays-battle';
|
import { BattlePanel } from './replays-battle';
|
||||||
declare function toID(input: string): string;
|
declare function toID(input: string): string;
|
||||||
declare const Config: any;
|
declare const Config: any;
|
||||||
|
|
||||||
interface ReplayResult {
|
interface ReplayResult {
|
||||||
uploadtime: number;
|
uploadtime: number;
|
||||||
id: string;
|
id: string;
|
||||||
format: string;
|
format: string;
|
||||||
players: string[];
|
players: string[];
|
||||||
password?: string;
|
password?: string;
|
||||||
private?: number;
|
private?: number;
|
||||||
rating?: number;
|
rating?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchPanel extends preact.Component<{id: string}> {
|
class SearchPanel extends preact.Component<{ id: string }> {
|
||||||
results: ReplayResult[] | null = null;
|
results: ReplayResult[] | null = null;
|
||||||
resultError: string | null = null;
|
resultError: string | null = null;
|
||||||
format = '';
|
format = '';
|
||||||
user = '';
|
user = '';
|
||||||
isPrivate = false;
|
isPrivate = false;
|
||||||
byRating = false;
|
byRating = false;
|
||||||
page = 1;
|
page = 1;
|
||||||
loggedInUser: string | null = null;
|
loggedInUser: string | null = null;
|
||||||
loggedInUserIsSysop = false;
|
loggedInUserIsSysop = false;
|
||||||
sort = 'date';
|
sort = 'date';
|
||||||
override componentDidMount() {
|
override componentDidMount() {
|
||||||
Net('/check-login.php').get().then(result => {
|
Net('/check-login.php').get().then(result => {
|
||||||
if (result.charAt(0) !== ']') return;
|
if (!result.startsWith(']')) return;
|
||||||
const [userid, sysop] = result.slice(1).split(',');
|
const [userid, sysop] = result.slice(1).split(',');
|
||||||
this.loggedInUser = userid;
|
this.loggedInUser = userid;
|
||||||
this.loggedInUserIsSysop = !!sysop;
|
this.loggedInUserIsSysop = !!sysop;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
});
|
});
|
||||||
this.updateSearch(Net.decodeQuery(this.props.id));
|
this.updateSearch(Net.decodeQuery(this.props.id));
|
||||||
}
|
}
|
||||||
override componentDidUpdate(previousProps: this['props']) {
|
override componentDidUpdate(previousProps: this['props']) {
|
||||||
if (this.props.id === previousProps.id) return;
|
if (this.props.id === previousProps.id) return;
|
||||||
const query = Net.decodeQuery(this.props.id);
|
const query = Net.decodeQuery(this.props.id);
|
||||||
const page = parseInt(query.page || '1');
|
const page = parseInt(query.page || '1');
|
||||||
const byRating = (query.sort === 'rating');
|
const byRating = (query.sort === 'rating');
|
||||||
if (page !== this.page || byRating !== this.byRating) this.updateSearch(query);
|
if (page !== this.page || byRating !== this.byRating) this.updateSearch(query);
|
||||||
}
|
}
|
||||||
updateSearch(query: {[k: string]: string}) {
|
updateSearch(query: { [k: string]: string }) {
|
||||||
const user = query.user || '';
|
const user = query.user || '';
|
||||||
const format = query.format || '';
|
const format = query.format || '';
|
||||||
const page = parseInt(query.page || '1');
|
const page = parseInt(query.page || '1');
|
||||||
const isPrivate = !!query.private;
|
const isPrivate = !!query.private;
|
||||||
this.byRating = (query.sort === 'rating');
|
this.byRating = (query.sort === 'rating');
|
||||||
this.search(user, format, isPrivate, page);
|
this.search(user, format, isPrivate, page);
|
||||||
}
|
}
|
||||||
parseResponse(response: string, isPrivate?: boolean) {
|
parseResponse(response: string, isPrivate?: boolean) {
|
||||||
this.results = null;
|
this.results = null;
|
||||||
this.resultError = null;
|
this.resultError = null;
|
||||||
|
|
||||||
if (isPrivate) {
|
if (isPrivate) {
|
||||||
if (response.charAt(0) !== ']') {
|
if (!response.startsWith(']')) {
|
||||||
this.resultError = `Unrecognized response: ${response}`;
|
this.resultError = `Unrecognized response: ${response}`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
response = response.slice(1);
|
response = response.slice(1);
|
||||||
}
|
}
|
||||||
const results = JSON.parse(response);
|
const results = JSON.parse(response);
|
||||||
if (!Array.isArray(results)) {
|
if (!Array.isArray(results)) {
|
||||||
this.resultError = results.actionerror || `Unrecognized response: ${response}`;
|
this.resultError = results.actionerror || `Unrecognized response: ${response}`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.results = results;
|
this.results = results;
|
||||||
}
|
}
|
||||||
search(user: string, format: string, isPrivate?: boolean, page = 1) {
|
search(user: string, format: string, isPrivate?: boolean, page = 1) {
|
||||||
this.base!.querySelector<HTMLInputElement>('input[name=user]')!.value = user;
|
this.base!.querySelector<HTMLInputElement>('input[name=user]')!.value = user;
|
||||||
this.base!.querySelector<HTMLInputElement>('input[name=format]')!.value = format;
|
this.base!.querySelector<HTMLInputElement>('input[name=format]')!.value = format;
|
||||||
this.base!.querySelectorAll<HTMLInputElement>('input[name=private]')[isPrivate ? 1 : 0]!.checked = true;
|
this.base!.querySelectorAll<HTMLInputElement>('input[name=private]')[isPrivate ? 1 : 0].checked = true;
|
||||||
|
|
||||||
if (!format && !user) return this.recent();
|
if (!format && !user) return this.recent();
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.isPrivate = !!isPrivate;
|
this.isPrivate = !!isPrivate;
|
||||||
this.page = page;
|
this.page = page;
|
||||||
this.results = null;
|
this.results = null;
|
||||||
this.resultError = null;
|
this.resultError = null;
|
||||||
if (user || !format) this.byRating = false;
|
if (user || !format) this.byRating = false;
|
||||||
|
|
||||||
if (!format && !user) {
|
if (!format && !user) {
|
||||||
PSRouter.replace('')
|
PSRouter.replace('');
|
||||||
} else {
|
} else {
|
||||||
PSRouter.replace('?' + Net.encodeQuery({
|
PSRouter.replace('?' + Net.encodeQuery({
|
||||||
user: user || undefined,
|
user: user || undefined,
|
||||||
format: format || undefined,
|
format: format || undefined,
|
||||||
private: isPrivate ? '1' : undefined,
|
private: isPrivate ? '1' : undefined,
|
||||||
page: page === 1 ? undefined : page,
|
page: page === 1 ? undefined : page,
|
||||||
sort: this.byRating ? 'rating' : undefined,
|
sort: this.byRating ? 'rating' : undefined,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
Net(`/api/replays/${isPrivate ? 'searchprivate' : 'search'}`).get({
|
Net(`/api/replays/${isPrivate ? 'searchprivate' : 'search'}`).get({
|
||||||
query: {
|
query: {
|
||||||
username: this.user,
|
username: this.user,
|
||||||
format: this.format,
|
format: this.format,
|
||||||
page,
|
page,
|
||||||
sort: this.byRating ? 'rating' : undefined,
|
sort: this.byRating ? 'rating' : undefined,
|
||||||
},
|
},
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
if (this.format !== format || this.user !== user) return;
|
if (this.format !== format || this.user !== user) return;
|
||||||
this.parseResponse(response, true);
|
this.parseResponse(response, true);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (this.format !== '' || this.user !== '') return;
|
if (this.format !== '' || this.user !== '') return;
|
||||||
this.resultError = '' + error;
|
this.resultError = '' + error;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
modLink(overrides: {page?: number, sort?: string}) {
|
modLink(overrides: { page?: number, sort?: string }) {
|
||||||
const newPage = (overrides.page !== undefined ? this.page + overrides.page : 1);
|
const newPage = (overrides.page !== undefined ? this.page + overrides.page : 1);
|
||||||
return './?' + Net.encodeQuery({
|
return './?' + Net.encodeQuery({
|
||||||
user: this.user || undefined,
|
user: this.user || undefined,
|
||||||
format: this.format || undefined,
|
format: this.format || undefined,
|
||||||
private: this.isPrivate ? '1' : undefined,
|
private: this.isPrivate ? '1' : undefined,
|
||||||
page: newPage === 1 ? undefined : newPage,
|
page: newPage === 1 ? undefined : newPage,
|
||||||
sort: (overrides.sort ? overrides.sort === 'rating' : this.byRating) ? 'rating' : undefined,
|
sort: (overrides.sort ? overrides.sort === 'rating' : this.byRating) ? 'rating' : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
recent() {
|
recent() {
|
||||||
this.format = '';
|
this.format = '';
|
||||||
this.user = '';
|
this.user = '';
|
||||||
this.results = null;
|
this.results = null;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
Net('/api/replays/recent').get().then(response => {
|
Net('/api/replays/recent').get().then(response => {
|
||||||
if (this.format !== '' || this.user !== '') return;
|
if (this.format !== '' || this.user !== '') return;
|
||||||
this.parseResponse(response, true);
|
this.parseResponse(response, true);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (this.format !== '' || this.user !== '') return;
|
if (this.format !== '' || this.user !== '') return;
|
||||||
this.resultError = '' + error;
|
this.resultError = '' + error;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
submitForm = (e: Event) => {
|
submitForm = (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const format = this.base!.querySelector<HTMLInputElement>('input[name=format]')?.value || '';
|
const format = this.base!.querySelector<HTMLInputElement>('input[name=format]')?.value || '';
|
||||||
const user = this.base!.querySelector<HTMLInputElement>('input[name=user]')?.value || '';
|
const user = this.base!.querySelector<HTMLInputElement>('input[name=user]')?.value || '';
|
||||||
const isPrivate = !this.base!.querySelector<HTMLInputElement>('input[name=private]')?.checked;
|
const isPrivate = !this.base!.querySelector<HTMLInputElement>('input[name=private]')?.checked;
|
||||||
this.search(user, format, isPrivate);
|
this.search(user, format, isPrivate);
|
||||||
};
|
};
|
||||||
cancelForm = (e: Event) => {
|
cancelForm = (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.search('', '');
|
this.search('', '');
|
||||||
};
|
};
|
||||||
searchLoggedIn = (e: Event) => {
|
searchLoggedIn = (e: Event) => {
|
||||||
if (!this.loggedInUser) return; // shouldn't happen
|
if (!this.loggedInUser) return; // shouldn't happen
|
||||||
(this.base!.querySelector('input[name=user]') as HTMLInputElement).value = this.loggedInUser;
|
this.base!.querySelector<HTMLInputElement>('input[name=user]')!.value = this.loggedInUser;
|
||||||
this.submitForm(e);
|
this.submitForm(e);
|
||||||
};
|
};
|
||||||
url(replay: ReplayResult) {
|
url(replay: ReplayResult) {
|
||||||
const viewpointSwitched = (toID(replay.players[1]) === toID(this.user));
|
const viewpointSwitched = (toID(replay.players[1]) === toID(this.user));
|
||||||
return replay.id + (replay.password ? `-${replay.password}pw` : '') + (viewpointSwitched ? '?p2' : '');
|
return replay.id + (replay.password ? `-${replay.password}pw` : '') + (viewpointSwitched ? '?p2' : '');
|
||||||
}
|
}
|
||||||
formatid(replay: ReplayResult) {
|
formatid(replay: ReplayResult) {
|
||||||
let formatid = replay.format;
|
let formatid = replay.format;
|
||||||
if (!formatid.startsWith('gen') || !/[0-9]/.test(formatid.charAt(3))) {
|
if (!formatid.startsWith('gen') || !/[0-9]/.test(formatid.charAt(3))) {
|
||||||
// 2013 Oct 14, two days after X and Y were released; good enough
|
// 2013 Oct 14, two days after X and Y were released; good enough
|
||||||
// estimate for when we renamed `ou` to `gen5ou`.
|
// estimate for when we renamed `ou` to `gen5ou`.
|
||||||
formatid = (replay.uploadtime > 1381734000 ? 'gen6' : 'gen5') + formatid;
|
formatid = (replay.uploadtime > 1381734000 ? 'gen6' : 'gen5') + formatid;
|
||||||
}
|
}
|
||||||
if (!/^gen[0-9]+-/.test(formatid)) {
|
if (!/^gen[0-9]+-/.test(formatid)) {
|
||||||
formatid = formatid.slice(0, 4) + '-' + formatid.slice(4);
|
formatid = formatid.slice(0, 4) + '-' + formatid.slice(4);
|
||||||
}
|
}
|
||||||
return formatid;
|
return formatid;
|
||||||
}
|
}
|
||||||
override render() {
|
override render() {
|
||||||
const activelySearching = !!(this.format || this.user);
|
const activelySearching = !!(this.format || this.user);
|
||||||
const hasNextPageLink = (this.results?.length || 0) > 50;
|
const hasNextPageLink = (this.results?.length || 0) > 50;
|
||||||
const results = hasNextPageLink ? this.results!.slice(0, 50) : this.results;
|
const results = hasNextPageLink ? this.results!.slice(0, 50) : this.results;
|
||||||
const searchResults = <ul class="linklist">
|
const searchResults = <ul class="linklist">
|
||||||
{(this.resultError && <li>
|
{(this.resultError && <li>
|
||||||
<strong class="message-error">{this.resultError}</strong>
|
<strong class="message-error">{this.resultError}</strong>
|
||||||
</li>) ||
|
</li>) ||
|
||||||
(!results && <li>
|
(!results && <li>
|
||||||
<em>Loading...</em>
|
<em>Loading...</em>
|
||||||
</li>) ||
|
</li>) ||
|
||||||
(results?.map(result => <li>
|
(results?.map(result => <li>
|
||||||
<a href={this.url(result)} class="blocklink">
|
<a href={this.url(result)} class="blocklink">
|
||||||
<small>{result.format}{result.rating ? ` (Rating: ${result.rating})` : ''}<br /></small>
|
<small>{result.format}{result.rating ? ` (Rating: ${result.rating})` : ''}<br /></small>
|
||||||
{!!result.private && <i class="fa fa-lock"></i>} {}
|
{!!result.private && <i class="fa fa-lock"></i>} {}
|
||||||
<strong>{result.players[0]}</strong> vs. <strong>{result.players[1]}</strong>
|
<strong>{result.players[0]}</strong> vs. <strong>{result.players[1]}</strong>
|
||||||
</a>
|
</a>
|
||||||
</li>))}
|
</li>))}
|
||||||
</ul>;
|
</ul>;
|
||||||
return <div class={PSRouter.showingRight() ? 'sidebar' : ''}>
|
return <div class={PSRouter.showingRight() ? 'sidebar' : ''}>
|
||||||
<section class="section first-section">
|
<section class="section first-section">
|
||||||
<h1>Search replays</h1>
|
<h1>Search replays</h1>
|
||||||
<form onSubmit={this.submitForm}>
|
<form onSubmit={this.submitForm}>
|
||||||
<p>
|
<p>
|
||||||
<label>
|
<label>
|
||||||
Username:<br />
|
Username:<br />
|
||||||
<input type="search" class="textbox" name="user" placeholder="(blank = any user)" size={20} /> {}
|
<input type="search" class="textbox" name="user" placeholder="(blank = any user)" size={20} /> {}
|
||||||
{this.loggedInUser && <button type="button" class="button" onClick={this.searchLoggedIn}>{this.loggedInUser}'s replays</button>}
|
{this.loggedInUser &&
|
||||||
</label>
|
<button type="button" class="button" onClick={this.searchLoggedIn}>{this.loggedInUser}'s replays</button>}
|
||||||
</p>
|
</label>
|
||||||
<p>
|
</p>
|
||||||
<label>Format:<br />
|
<p>
|
||||||
<input type="search" class="textbox" name="format" placeholder="(blank = any format)" size={30} /></label>
|
<label>Format:<br />
|
||||||
</p>
|
<input type="search" class="textbox" name="format" placeholder="(blank = any format)" size={30} /></label>
|
||||||
<p>
|
</p>
|
||||||
<label class="checkbox inline"><input type="radio" name="private" value="" /> Public</label> {}
|
<p>
|
||||||
<label class="checkbox inline"><input type="radio" name="private" value="1" /> Private (your own replays only)</label>
|
<label class="checkbox inline"><input type="radio" name="private" value="" /> Public</label> {}
|
||||||
</p>
|
<label class="checkbox inline"><input type="radio" name="private" value="1" /> Private (your own replays only)</label>
|
||||||
<p>
|
</p>
|
||||||
<button type="submit" class="button"><i class="fa fa-search" aria-hidden></i> <strong>Search</strong></button> {}
|
<p>
|
||||||
{activelySearching && <button class="button" onClick={this.cancelForm}>Cancel</button>}
|
<button type="submit" class="button"><i class="fa fa-search" aria-hidden></i> <strong>Search</strong></button> {}
|
||||||
</p>
|
{activelySearching && <button class="button" onClick={this.cancelForm}>Cancel</button>}
|
||||||
{activelySearching && <h1 aria-label="Results"></h1>}
|
</p>
|
||||||
{activelySearching && this.format && !this.user && <p>
|
{activelySearching && <h1 aria-label="Results"></h1>}
|
||||||
Sort by: {}
|
{activelySearching && this.format && !this.user && <p>
|
||||||
<a href={this.modLink({sort: 'date'})} class={`button button-first${this.byRating ? '' : ' disabled'}`}>
|
Sort by: {}
|
||||||
Date
|
<a href={this.modLink({ sort: 'date' })} class={`button button-first${this.byRating ? '' : ' disabled'}`}>
|
||||||
</a>
|
Date
|
||||||
<a href={this.modLink({sort: 'rating'})} class={`button button-last${this.byRating ? ' disabled' : ''}`}>
|
</a>
|
||||||
Rating
|
<a href={this.modLink({ sort: 'rating' })} class={`button button-last${this.byRating ? ' disabled' : ''}`}>
|
||||||
</a>
|
Rating
|
||||||
</p>}
|
</a>
|
||||||
{activelySearching && this.page > 1 && <p class="pagelink">
|
</p>}
|
||||||
<a href={this.modLink({page: -1})} class="button"><i class="fa fa-caret-up"></i><br />Page {this.page - 1}</a>
|
{activelySearching && this.page > 1 && <p class="pagelink">
|
||||||
</p>}
|
<a href={this.modLink({ page: -1 })} class="button"><i class="fa fa-caret-up"></i><br />Page {this.page - 1}</a>
|
||||||
{activelySearching && searchResults}
|
</p>}
|
||||||
{activelySearching && (this.results?.length || 0) > 50 && <p class="pagelink">
|
{activelySearching && searchResults}
|
||||||
<a href={this.modLink({page: 1})} class="button">Page {this.page + 1}<br /><i class="fa fa-caret-down"></i></a>
|
{activelySearching && (this.results?.length || 0) > 50 && <p class="pagelink">
|
||||||
</p>}
|
<a href={this.modLink({ page: 1 })} class="button">Page {this.page + 1}<br /><i class="fa fa-caret-down"></i></a>
|
||||||
</form>
|
</p>}
|
||||||
</section>
|
</form>
|
||||||
{!activelySearching && <FeaturedReplays />}
|
</section>
|
||||||
{!activelySearching && <section class="section">
|
{!activelySearching && <FeaturedReplays />}
|
||||||
<h1>Recent replays</h1>
|
{!activelySearching && <section class="section">
|
||||||
<ul class="linklist">
|
<h1>Recent replays</h1>
|
||||||
{searchResults}
|
<ul class="linklist">
|
||||||
</ul>
|
{searchResults}
|
||||||
</section>}
|
</ul>
|
||||||
</div>;
|
</section>}
|
||||||
}
|
</div>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FeaturedReplays extends preact.Component {
|
class FeaturedReplays extends preact.Component {
|
||||||
moreFun = false;
|
moreFun = false;
|
||||||
moreCompetitive = false;
|
moreCompetitive = false;
|
||||||
showMoreFun = (e: Event) => {
|
showMoreFun = (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.moreFun = true;
|
this.moreFun = true;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
showMoreCompetitive = (e: Event) => {
|
showMoreCompetitive = (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.moreCompetitive = true;
|
this.moreCompetitive = true;
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
override render() {
|
override render() {
|
||||||
return <section class="section">
|
return <section class="section">
|
||||||
<h1>Featured replays</h1>
|
<h1>Featured replays</h1>
|
||||||
<ul class="linklist">
|
<ul class="linklist">
|
||||||
<li><h2>Fun</h2></li>
|
<li><h2>Fun</h2></li>
|
||||||
<li><a href="oumonotype-82345404" class="blocklink">
|
<li><a href="oumonotype-82345404" class="blocklink">
|
||||||
<small>[gen6-oumonotype]<br /></small>
|
<small>[gen6-oumonotype]<br /></small>
|
||||||
<strong>kdarewolf</strong> vs. <strong>Onox</strong>
|
<strong>kdarewolf</strong> vs. <strong>Onox</strong>
|
||||||
<small><br />Protean + prediction</small>
|
<small><br />Protean + prediction</small>
|
||||||
</a></li>
|
</a></li>
|
||||||
<li><a href="anythinggoes-218380995?p2" class="blocklink">
|
<li><a href="anythinggoes-218380995?p2" class="blocklink">
|
||||||
<small>[gen6-anythinggoes]<br /></small>
|
<small>[gen6-anythinggoes]<br /></small>
|
||||||
<strong>Anta2</strong> vs. <strong>dscottnew</strong>
|
<strong>Anta2</strong> vs. <strong>dscottnew</strong>
|
||||||
<small><br />Cheek Pouch</small>
|
<small><br />Cheek Pouch</small>
|
||||||
</a></li>
|
</a></li>
|
||||||
<li><a href="uberssuspecttest-147833524" class="blocklink">
|
<li><a href="uberssuspecttest-147833524" class="blocklink">
|
||||||
<small>[gen6-ubers]<br /></small>
|
<small>[gen6-ubers]<br /></small>
|
||||||
<strong>Metal Brellow</strong> vs. <strong>zig100</strong>
|
<strong>Metal Brellow</strong> vs. <strong>zig100</strong>
|
||||||
<small><br />Topsy-Turvy</small>
|
<small><br />Topsy-Turvy</small>
|
||||||
</a></li>
|
</a></li>
|
||||||
{!this.moreFun && <li style={{paddingLeft: '8px'}}>
|
{!this.moreFun && <li style={{ paddingLeft: '8px' }}>
|
||||||
<button class="button" onClick={this.showMoreFun}>More <i class="fa fa-caret-right" aria-hidden></i></button>
|
<button class="button" onClick={this.showMoreFun}>More <i class="fa fa-caret-right" aria-hidden></i></button>
|
||||||
</li>}
|
</li>}
|
||||||
{this.moreFun && <li><a href="smogondoubles-75588440?p2" class="blocklink">
|
{this.moreFun && <li><a href="smogondoubles-75588440?p2" class="blocklink">
|
||||||
<small>[gen6-smogondoubles]<br /></small>
|
<small>[gen6-smogondoubles]<br /></small>
|
||||||
<strong>jamace6</strong> vs. <strong>DubsWelder</strong>
|
<strong>jamace6</strong> vs. <strong>DubsWelder</strong>
|
||||||
<small><br />Garchomp sweeps 11 pokemon</small>
|
<small><br />Garchomp sweeps 11 pokemon</small>
|
||||||
</a></li>}
|
</a></li>}
|
||||||
{this.moreFun && <li><a href="ou-20651579?p2" class="blocklink">
|
{this.moreFun && <li><a href="ou-20651579?p2" class="blocklink">
|
||||||
<small>[gen5-ou]<br /></small>
|
<small>[gen5-ou]<br /></small>
|
||||||
<strong>RainSeven07</strong> vs. <strong>my body is regi</strong>
|
<strong>RainSeven07</strong> vs. <strong>my body is regi</strong>
|
||||||
<small><br />An entire team based on Assist V-create</small>
|
<small><br />An entire team based on Assist V-create</small>
|
||||||
</a></li>}
|
</a></li>}
|
||||||
{this.moreFun && <li><a href="balancedhackmons7322360?p2" class="blocklink">
|
{this.moreFun && <li><a href="balancedhackmons7322360?p2" class="blocklink">
|
||||||
<small>[gen5-balancedhackmons]<br /></small>
|
<small>[gen5-balancedhackmons]<br /></small>
|
||||||
<strong>a ver</strong> vs. <strong>Shuckie</strong>
|
<strong>a ver</strong> vs. <strong>Shuckie</strong>
|
||||||
<small><br />To a ver's frustration, PP stall is viable in Balanced Hackmons</small>
|
<small><br />To a ver's frustration, PP stall is viable in Balanced Hackmons</small>
|
||||||
</a></li>}
|
</a></li>}
|
||||||
<h2>Competitive</h2>
|
<h2>Competitive</h2>
|
||||||
<li><a href="doublesou-232753081" class="blocklink">
|
<li><a href="doublesou-232753081" class="blocklink">
|
||||||
<small>[gen6-doublesou]<br /></small>
|
<small>[gen6-doublesou]<br /></small>
|
||||||
<strong>Electrolyte</strong> vs. <strong>finally</strong>
|
<strong>Electrolyte</strong> vs. <strong>finally</strong>
|
||||||
<small><br />finally steals Electrolyte's spot in the finals of the Doubles Winter Seasonal by outplaying Toxic Aegislash.</small>
|
<small><br />
|
||||||
</a></li>
|
finally steals Electrolyte's spot in the finals of the Doubles Winter Seasonal by outplaying Toxic Aegislash.
|
||||||
<li><a href="smogtours-gen5ou-59402" class="blocklink">
|
</small>
|
||||||
<small>[gen5-ou]<br /></small>
|
</a></li>
|
||||||
<strong>Reymedy</strong> vs. <strong>Leftiez</strong>
|
<li><a href="smogtours-gen5ou-59402" class="blocklink">
|
||||||
<small><br />Reymedy's superior grasp over BW OU lead to his claim of victory over Leftiez in the No Johns Tournament.</small>
|
<small>[gen5-ou]<br /></small>
|
||||||
</a></li>
|
<strong>Reymedy</strong> vs. <strong>Leftiez</strong>
|
||||||
<li><a href="smogtours-gen3ou-56583" class="blocklink">
|
<small><br />
|
||||||
<small>[gen3-ou]<br /></small>
|
Reymedy's superior grasp over BW OU lead to his claim of victory over Leftiez in the No Johns Tournament.
|
||||||
<strong>pokebasket</strong> vs. <strong>Alf'</strong>
|
</small>
|
||||||
<small><br />pokebasket proved Blissey isn't really one to take a Focus Punch well in his victory match over Alf' in the Fuck Trappers ADV OU tournament.</small>
|
</a></li>
|
||||||
</a></li>
|
<li><a href="smogtours-gen3ou-56583" class="blocklink">
|
||||||
<li><a href="smogtours-ou-55891" class="blocklink">
|
<small>[gen3-ou]<br /></small>
|
||||||
<small>[gen6-ou]<br /></small>
|
<strong>pokebasket</strong> vs. <strong>Alf'</strong>
|
||||||
<strong>Marshall.Law</strong> vs. <strong>Malekith</strong>
|
<small><br />
|
||||||
<small><br />In a "match full of reverses", Marshall.Law takes on Malekith in the finals of It's No Use.</small>
|
pokebasket proved Blissey isn't really one to take a Focus Punch well in his victory match over Alf' in the
|
||||||
</a></li>
|
Fuck Trappers ADV OU tournament.
|
||||||
<li><a href="smogtours-ubers-54583" class="blocklink">
|
</small>
|
||||||
<small>[gen6-custom]<br /></small>
|
</a></li>
|
||||||
<strong>hard</strong> vs. <strong>panamaxis</strong>
|
<li><a href="smogtours-ou-55891" class="blocklink">
|
||||||
<small><br />Dark horse panamaxis proves his worth as the rightful winner of The Walkthrough Tournament in this exciting final versus hard.</small>
|
<small>[gen6-ou]<br /></small>
|
||||||
</a></li>
|
<strong>Marshall.Law</strong> vs. <strong>Malekith</strong>
|
||||||
{!this.moreCompetitive && <li style={{paddingLeft: '8px'}}>
|
<small><br />
|
||||||
<button class="button" onClick={this.showMoreCompetitive}>More <i class="fa fa-caret-right" aria-hidden></i></button>
|
In a "match full of reverses", Marshall.Law takes on Malekith in the finals of It's No Use.
|
||||||
</li>}
|
</small>
|
||||||
{this.moreCompetitive && <li><a href="smogtours-ubers-34646" class="blocklink">
|
</a></li>
|
||||||
<small>[gen6-ubers]<br /></small>
|
<li><a href="smogtours-ubers-54583" class="blocklink">
|
||||||
<strong>steelphoenix</strong> vs. <strong>Jibaku</strong>
|
<small>[gen6-custom]<br /></small>
|
||||||
<small><br />In this SPL Week 4 battle, Jibaku's clever plays with Mega Sableye keep the momentum mostly in his favor.</small>
|
<strong>hard</strong> vs. <strong>panamaxis</strong>
|
||||||
</a></li>}
|
<small><br />
|
||||||
{this.moreCompetitive && <li><a href="smogtours-uu-36860" class="blocklink">
|
Dark horse panamaxis proves his worth as the rightful winner of The Walkthrough Tournament in this exciting
|
||||||
<small>[gen6-uu]<br /></small>
|
final versus hard.
|
||||||
<strong>IronBullet93</strong> vs. <strong>Laurel</strong>
|
</small>
|
||||||
<small><br />Laurel outplays IronBullet's Substitute Tyrantrum with the sly use of a Shuca Berry Cobalion, but luck was inevitably the deciding factor in this SPL Week 6 match.</small>
|
</a></li>
|
||||||
</a></li>}
|
{!this.moreCompetitive && <li style={{ paddingLeft: '8px' }}>
|
||||||
{this.moreCompetitive && <li><a href="smogtours-gen5ou-36900" class="blocklink">
|
<button class="button" onClick={this.showMoreCompetitive}>More <i class="fa fa-caret-right" aria-hidden></i></button>
|
||||||
<small>[gen5-ou]<br /></small>
|
</li>}
|
||||||
<strong>Lowgock</strong> vs. <strong>Meridian</strong>
|
{this.moreCompetitive && <li><a href="smogtours-ubers-34646" class="blocklink">
|
||||||
<small><br />This SPL Week 6 match features impressive plays, from Jirachi sacrificing itself to paralysis to avoid a burn to some clever late-game switches.</small>
|
<small>[gen6-ubers]<br /></small>
|
||||||
</a></li>}
|
<strong>steelphoenix</strong> vs. <strong>Jibaku</strong>
|
||||||
{this.moreCompetitive && <li><a href="smogtours-gen4ou-36782" class="blocklink">
|
<small><br />
|
||||||
<small>[gen4-ou]<br /></small>
|
In this SPL Week 4 battle, Jibaku's clever plays with Mega Sableye keep the momentum mostly in his favor.
|
||||||
<strong>Heist</strong> vs. <strong>liberty32</strong>
|
</small>
|
||||||
<small><br />Starting out as an entry hazard-filled stallfest, this close match is eventually decided by liberty32's efficient use of Aerodactyl.</small>
|
</a></li>}
|
||||||
</a></li>}
|
{this.moreCompetitive && <li><a href="smogtours-uu-36860" class="blocklink">
|
||||||
{this.moreCompetitive && <li><a href="randombattle-213274483" class="blocklink">
|
<small>[gen6-uu]<br /></small>
|
||||||
<small>[gen6-randombattle]<br /></small>
|
<strong>IronBullet93</strong> vs. <strong>Laurel</strong>
|
||||||
<strong>The Immortal</strong> vs. <strong>Amphinobite</strong>
|
<small><br />
|
||||||
<small><br />Substitute Lugia and Rotom-Fan take advantage of Slowking's utility and large HP stat, respectively, in this high ladder match.</small>
|
Laurel outplays IronBullet's Substitute Tyrantrum with the sly use of a Shuca Berry Cobalion, but luck was
|
||||||
</a></li>}
|
inevitably the deciding factor in this SPL Week 6 match.
|
||||||
</ul>
|
</small>
|
||||||
</section>;
|
</a></li>}
|
||||||
}
|
{this.moreCompetitive && <li><a href="smogtours-gen5ou-36900" class="blocklink">
|
||||||
|
<small>[gen5-ou]<br /></small>
|
||||||
|
<strong>Lowgock</strong> vs. <strong>Meridian</strong>
|
||||||
|
<small><br />
|
||||||
|
This SPL Week 6 match features impressive plays, from Jirachi sacrificing itself to paralysis to avoid a
|
||||||
|
burn to some clever late-game switches.
|
||||||
|
</small>
|
||||||
|
</a></li>}
|
||||||
|
{this.moreCompetitive && <li><a href="smogtours-gen4ou-36782" class="blocklink">
|
||||||
|
<small>[gen4-ou]<br /></small>
|
||||||
|
<strong>Heist</strong> vs. <strong>liberty32</strong>
|
||||||
|
<small><br />
|
||||||
|
Starting out as an entry hazard-filled stallfest, this close match is eventually decided by liberty32's
|
||||||
|
efficient use of Aerodactyl.
|
||||||
|
</small>
|
||||||
|
</a></li>}
|
||||||
|
{this.moreCompetitive && <li><a href="randombattle-213274483" class="blocklink">
|
||||||
|
<small>[gen6-randombattle]<br /></small>
|
||||||
|
<strong>The Immortal</strong> vs. <strong>Amphinobite</strong>
|
||||||
|
<small><br />
|
||||||
|
Substitute Lugia and Rotom-Fan take advantage of Slowking's utility and large HP stat, respectively,
|
||||||
|
in this high ladder match.
|
||||||
|
</small>
|
||||||
|
</a></li>}
|
||||||
|
</ul>
|
||||||
|
</section>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PSRouter = new class extends PSModel {
|
export const PSRouter = new class extends PSModel {
|
||||||
baseLoc: string;
|
baseLoc: string;
|
||||||
leftLoc: string | null = null;
|
leftLoc: string | null = null;
|
||||||
rightLoc: string | null = null;
|
rightLoc: string | null = null;
|
||||||
forceSinglePanel = false;
|
forceSinglePanel = false;
|
||||||
stickyRight = true;
|
stickyRight = true;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
const baseLocSlashIndex = document.location.href.lastIndexOf('/');
|
const baseLocSlashIndex = document.location.href.lastIndexOf('/');
|
||||||
this.baseLoc = document.location.href.slice(0, baseLocSlashIndex + 1);
|
this.baseLoc = document.location.href.slice(0, baseLocSlashIndex + 1);
|
||||||
this.go(document.location.href);
|
this.go(document.location.href);
|
||||||
this.setSinglePanel(true);
|
this.setSinglePanel(true);
|
||||||
if (window.history) window.addEventListener('popstate', e => {
|
if (window.history) window.addEventListener('popstate', e => {
|
||||||
PSRouter.popState(e);
|
PSRouter.popState(e);
|
||||||
this.update();
|
this.update();
|
||||||
});
|
});
|
||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
PSRouter.setSinglePanel();
|
PSRouter.setSinglePanel();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
showingLeft() {
|
showingLeft() {
|
||||||
return this.leftLoc !== null && (!this.forceSinglePanel || this.rightLoc === null);
|
return this.leftLoc !== null && (!this.forceSinglePanel || this.rightLoc === null);
|
||||||
}
|
}
|
||||||
showingRight() {
|
showingRight() {
|
||||||
return this.rightLoc !== null;
|
return this.rightLoc !== null;
|
||||||
}
|
}
|
||||||
setSinglePanel(init?: boolean) {
|
setSinglePanel(init?: boolean) {
|
||||||
const singlePanel = window.innerWidth < 1300;
|
const singlePanel = window.innerWidth < 1300;
|
||||||
const stickyRight = (window.innerHeight > 614);
|
const stickyRight = (window.innerHeight > 614);
|
||||||
if (this.forceSinglePanel !== singlePanel || this.stickyRight !== stickyRight) {
|
if (this.forceSinglePanel !== singlePanel || this.stickyRight !== stickyRight) {
|
||||||
this.forceSinglePanel = singlePanel;
|
this.forceSinglePanel = singlePanel;
|
||||||
this.stickyRight = stickyRight;
|
this.stickyRight = stickyRight;
|
||||||
if (!init) this.update();
|
if (!init) this.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
push(href: string): boolean {
|
push(href: string): boolean {
|
||||||
if (!href.startsWith(this.baseLoc)) return false;
|
if (!href.startsWith(this.baseLoc)) return false;
|
||||||
|
|
||||||
if (this.go(href)) {
|
if (this.go(href)) {
|
||||||
window.history?.pushState([this.leftLoc, this.rightLoc], '', href);
|
window.history?.pushState([this.leftLoc, this.rightLoc], '', href);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/** returns whether the URL should change */
|
/** returns whether the URL should change */
|
||||||
go(href: string): boolean {
|
go(href: string): boolean {
|
||||||
if (!href.startsWith(this.baseLoc)) return false;
|
if (!href.startsWith(this.baseLoc)) return false;
|
||||||
|
|
||||||
const loc = href.slice(this.baseLoc.length);
|
const loc = href.slice(this.baseLoc.length);
|
||||||
if (!loc || loc.startsWith('?')) {
|
if (!loc || loc.startsWith('?')) {
|
||||||
this.leftLoc = loc;
|
this.leftLoc = loc;
|
||||||
if (this.forceSinglePanel) {
|
if (this.forceSinglePanel) {
|
||||||
this.rightLoc = null;
|
this.rightLoc = null;
|
||||||
} else {
|
} else {
|
||||||
return this.rightLoc === null;
|
return this.rightLoc === null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.rightLoc = loc;
|
this.rightLoc = loc;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
replace(loc: string) {
|
replace(loc: string) {
|
||||||
const href = this.baseLoc + loc;
|
const href = this.baseLoc + loc;
|
||||||
if (this.go(href)) {
|
if (this.go(href)) {
|
||||||
window.history?.replaceState([this.leftLoc, this.rightLoc], '', href);
|
window.history?.replaceState([this.leftLoc, this.rightLoc], '', href);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
popState(e: PopStateEvent) {
|
popState(e: PopStateEvent) {
|
||||||
if (Array.isArray(e.state)) {
|
if (Array.isArray(e.state)) {
|
||||||
const [leftLoc, rightLoc] = e.state;
|
const [leftLoc, rightLoc] = e.state;
|
||||||
this.leftLoc = leftLoc;
|
this.leftLoc = leftLoc;
|
||||||
this.rightLoc = rightLoc;
|
this.rightLoc = rightLoc;
|
||||||
if (this.forceSinglePanel) this.leftLoc = null;
|
if (this.forceSinglePanel) this.leftLoc = null;
|
||||||
} else {
|
} else {
|
||||||
this.leftLoc = null;
|
this.leftLoc = null;
|
||||||
this.rightLoc = null;
|
this.rightLoc = null;
|
||||||
this.go(document.location.href);
|
this.go(document.location.href);
|
||||||
}
|
}
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PSReplays extends preact.Component {
|
export class PSReplays extends preact.Component {
|
||||||
static darkMode: 'dark' | 'light' | 'auto' = 'auto';
|
static darkMode: 'dark' | 'light' | 'auto' = 'auto';
|
||||||
static updateDarkMode() {
|
static updateDarkMode() {
|
||||||
let dark = this.darkMode === 'dark' ? 'dark' : '';
|
let dark = this.darkMode === 'dark' ? 'dark' : '';
|
||||||
if (this.darkMode === 'auto') {
|
if (this.darkMode === 'auto') {
|
||||||
dark = window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : '';
|
dark = window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : '';
|
||||||
}
|
}
|
||||||
document.documentElement.className = dark;
|
document.documentElement.className = dark;
|
||||||
}
|
}
|
||||||
override componentDidMount() {
|
override componentDidMount() {
|
||||||
PSRouter.subscribe(() => this.forceUpdate());
|
PSRouter.subscribe(() => this.forceUpdate());
|
||||||
if (window.history) {
|
if (window.history) {
|
||||||
this.base!.addEventListener('click', e => {
|
this.base!.addEventListener('click', e => {
|
||||||
let el = e.target as HTMLElement;
|
let el = e.target as HTMLElement;
|
||||||
for (; el; el = el.parentNode as HTMLElement) {
|
for (; el; el = el.parentNode as HTMLElement) {
|
||||||
if (el.tagName === 'A' && PSRouter.push((el as HTMLAnchorElement).href)) {
|
if (el.tagName === 'A' && PSRouter.push((el as HTMLAnchorElement).href)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// load custom colors from loginserver
|
// load custom colors from loginserver
|
||||||
Net(`https://${Config.routes.client}/config/colors.json`).get().then(response => {
|
Net(`https://${Config.routes.client}/config/colors.json`).get().then(response => {
|
||||||
const data = JSON.parse(response);
|
const data = JSON.parse(response);
|
||||||
Object.assign(Config.customcolors, data);
|
Object.assign(Config.customcolors, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
override render() {
|
override render() {
|
||||||
const position = PSRouter.showingLeft() && PSRouter.showingRight() && !PSRouter.stickyRight ?
|
const position = PSRouter.showingLeft() && PSRouter.showingRight() && !PSRouter.stickyRight ?
|
||||||
{display: 'flex', flexDirection: 'column', justifyContent: 'flex-end'} : {};
|
{ display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' } : {};
|
||||||
return <div class={'bar-wrapper' + (PSRouter.showingLeft() && PSRouter.showingRight() ? ' has-sidebar' : '')} style={position}>
|
return <div
|
||||||
{PSRouter.showingLeft() && <SearchPanel id={PSRouter.leftLoc!} />}
|
class={'bar-wrapper' + (PSRouter.showingLeft() && PSRouter.showingRight() ? ' has-sidebar' : '')} style={position}
|
||||||
{PSRouter.showingRight() && <BattlePanel id={PSRouter.rightLoc!} />}
|
>
|
||||||
<div style={{clear: 'both'}}></div>
|
{PSRouter.showingLeft() && <SearchPanel id={PSRouter.leftLoc!} />}
|
||||||
</div>;
|
{PSRouter.showingRight() && <BattlePanel id={PSRouter.rightLoc!} />}
|
||||||
}
|
<div style={{ clear: 'both' }}></div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preact.render(<PSReplays />, document.getElementById('main')!);
|
preact.render(<PSReplays />, document.getElementById('main')!);
|
||||||
|
|
||||||
if (window.matchMedia?.('(prefers-color-scheme: dark)').matches) {
|
if (window.matchMedia?.('(prefers-color-scheme: dark)').matches) {
|
||||||
document.documentElement.className = 'dark';
|
document.documentElement.className = 'dark';
|
||||||
}
|
}
|
||||||
window.matchMedia?.('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
window.matchMedia?.('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||||
if (PSReplays.darkMode === 'auto') document.documentElement.className = event.matches ? "dark" : "";
|
if (PSReplays.darkMode === 'auto') document.documentElement.className = event.matches ? "dark" : "";
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export class HttpError extends Error {
|
||||||
this.body = body;
|
this.body = body;
|
||||||
try {
|
try {
|
||||||
(Error as any).captureStackTrace(this, HttpError);
|
(Error as any).captureStackTrace(this, HttpError);
|
||||||
} catch (err) {}
|
} catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class NetRequest {
|
export class NetRequest {
|
||||||
|
|
@ -100,8 +100,8 @@ Net.encodeQuery = function (data: string | PostData) {
|
||||||
}
|
}
|
||||||
return urlencodedData;
|
return urlencodedData;
|
||||||
};
|
};
|
||||||
Net.decodeQuery = function (query: string): {[key: string]: string} {
|
Net.decodeQuery = function (query: string): { [key: string]: string } {
|
||||||
let out = {};
|
let out: { [key: string]: string } = {};
|
||||||
const questionIndex = query.indexOf('?');
|
const questionIndex = query.indexOf('?');
|
||||||
if (questionIndex >= 0) query = query.slice(questionIndex + 1);
|
if (questionIndex >= 0) query = query.slice(questionIndex + 1);
|
||||||
for (const queryPart of query.split('&')) {
|
for (const queryPart of query.split('&')) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"types": [],
|
"types": [],
|
||||||
"include": [
|
"include": [
|
||||||
"./play.pokemonshowdown.com/js/lib/preact.d.ts",
|
"./play.pokemonshowdown.com/js/lib/preact.d.ts",
|
||||||
"./play.pokemonshowdown.com/src/*"
|
"./play.pokemonshowdown.com/src/*",
|
||||||
|
"./replay.pokemonshowdown.com/src/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
58
tslint.json
58
tslint.json
|
|
@ -1,58 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "tslint:latest",
|
|
||||||
"defaultSeverity": "error",
|
|
||||||
"jsRules": {},
|
|
||||||
"rules": {
|
|
||||||
"array-type": [true, "array"],
|
|
||||||
"arrow-parens": [true, "ban-single-arg-parens"],
|
|
||||||
"curly": [true, "ignore-same-line"],
|
|
||||||
"indent": [true, "tabs", 2],
|
|
||||||
"quotemark": false,
|
|
||||||
"member-access": [true, "no-public"],
|
|
||||||
"no-empty": [true, "allow-empty-catch", "allow-empty-functions"],
|
|
||||||
"no-string-literal": false,
|
|
||||||
"interface-over-type-literal": false,
|
|
||||||
// TODO: turn on
|
|
||||||
"prefer-const": false,
|
|
||||||
"max-line-length": {
|
|
||||||
"options": {
|
|
||||||
"limit": 120,
|
|
||||||
"ignore-pattern": "template\\.replace\\(('\\[|/)|[a-z0-9]=|^\\s*(// \\s*)?((let |const )?[a-zA-Z0-9$.]+ \\+?= (\\$\\()?|(return |throw )?(new )?([a-zA-Z0-9$.]+\\()?)?['\"`/]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"interface-name": false,
|
|
||||||
"forin": false,
|
|
||||||
// maybe one day
|
|
||||||
"member-ordering": false,
|
|
||||||
"max-classes-per-file": false,
|
|
||||||
// they look weird in `new class {...}`
|
|
||||||
"new-parens": false,
|
|
||||||
"no-bitwise": false,
|
|
||||||
"no-console": false,
|
|
||||||
"no-namespace": [true, "allow-declarations"],
|
|
||||||
"only-arrow-functions": false,
|
|
||||||
"prefer-conditional-expression": false,
|
|
||||||
"no-shadowed-variable": [true, {"temporalDeadZone": false}],
|
|
||||||
"no-switch-case-fall-through": true,
|
|
||||||
"no-unnecessary-initializer": false,
|
|
||||||
"object-literal-sort-keys": false,
|
|
||||||
"object-literal-key-quotes": false,
|
|
||||||
"ordered-imports": false,
|
|
||||||
"trailing-comma": [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
"multiline": {
|
|
||||||
"objects": "always",
|
|
||||||
"arrays": "always",
|
|
||||||
"functions": "never",
|
|
||||||
"typeLiterals": "always"
|
|
||||||
},
|
|
||||||
"singleline": "never",
|
|
||||||
"esSpecCompliant": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"semicolon": [true, "always", "strict-bound-class-methods"],
|
|
||||||
"space-before-function-paren": [true, {"anonymous": "always", "named": "never", "asyncArrow": "always"}]
|
|
||||||
},
|
|
||||||
"rulesDirectory": []
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user