├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .prettierignore ├── LICENSE ├── README.md ├── devWrapper.ts ├── package.json ├── src ├── base.ts ├── classes │ ├── Apk.ts │ ├── Invasion.ts │ ├── Item.ts │ ├── Masterfile.ts │ ├── Misc.ts │ ├── Move.ts │ ├── PokeApi.ts │ ├── Pokemon.ts │ ├── Quest.ts │ ├── Translations.ts │ ├── Types.ts │ └── Weather.ts ├── index.ts └── typings │ ├── dataTypes.ts │ ├── general.ts │ ├── inputs.ts │ ├── pogoinfo.ts │ ├── pokeapi.ts │ └── protos.ts ├── static ├── baseStats.json ├── cpm.json ├── tempEvos.json └── types.json ├── tests ├── customValues.json ├── defaultValues.json ├── rawValues.json ├── statCalculating.json └── templates.test.js ├── tsconfig.json └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: TurtIeSocks 4 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | name: Run tests 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 2 16 | 17 | - name: Setup Node.js environment 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: 18 21 | cache: 'yarn' 22 | 23 | - name: Install Dependencies 24 | run: yarn 25 | 26 | - name: Test 27 | run: yarn test 28 | 29 | publish-npm: 30 | needs: build 31 | name: Publish to NPM 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout Code 35 | uses: actions/checkout@v3 36 | with: 37 | fetch-depth: 2 38 | 39 | - name: Setup Node.js environment 40 | uses: actions/setup-node@v3 41 | with: 42 | node-version: 18 43 | registry-url: https://registry.npmjs.org/ 44 | - run: yarn 45 | - run: tsc 46 | - run: npm publish 47 | env: 48 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 49 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [push] 3 | 4 | jobs: 5 | lint: 6 | name: Run Tests 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout Code 10 | uses: actions/checkout@v3 11 | with: 12 | fetch-depth: 2 13 | - name: Setup Node.js environment 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: 18 17 | cache: 'yarn' 18 | 19 | - name: Install Dependencies 20 | run: yarn 21 | 22 | - name: Generate Check 23 | run: yarn generate 24 | 25 | - name: Invasion Check 26 | run: yarn invasions 27 | 28 | - name: Run Tests 29 | run: yarn test 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /dist/ 3 | masterfile.json 4 | master-latest.json 5 | .prettierrc 6 | .DS_Store 7 | invasions.json 8 | latest.json -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pogo Data Generator 2 | 3 | [![npm version](https://badge.fury.io/js/pogo-data-generator.svg)](https://badge.fury.io/js/pogo-data-generator) 4 | [![Discord](https://img.shields.io/discord/552003258000998401.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/zZ9h9Xa) 5 | 6 | Generates templated data for Pokemon GO related projects, including: 7 | 8 | - Pokemon 9 | - Forms 10 | - Costumes 11 | - Moves 12 | - Items 13 | - Team Rocket Invasions 14 | - Pokemon Types 15 | - Weather 16 | - Translations 17 | - Quest Conditions 18 | - Quest Types 19 | - Quest Reward Types 20 | - Future Pokemon via PokeAPI 21 | 22 | ## Current Status 23 | - Internally the typing is strong, however, the return results are typed pretty horribly due to this being my first TypeScript project. Going to work on this in the 2.0 release. 24 | 25 | 26 | ## Installing/Usage 27 | 28 | ### Package 29 | 30 | ```markdown 31 | // with npm 32 | npm install pogo-data-generator 33 | 34 | // with yarn 35 | yarn add pogo-data-generator 36 | ``` 37 | 38 | Usage: 39 | 40 | ```js 41 | // commonJS 42 | const { generate } = require('pogo-data-generator') 43 | // es6 with invasion function 44 | import { generate, invasions } from 'pogo-data-generator' 45 | 46 | const data = await generate() // returns the default settings 47 | 48 | const template = { 49 | pokemon: { 50 | enabled: true, 51 | options: { 52 | snake_case: true, 53 | unsetDefaultForm: true, 54 | }, 55 | template: { 56 | pokemonName: true, 57 | pokedexId: true, 58 | forms: { 59 | formName: true, 60 | proto: true, 61 | }, 62 | }, 63 | }, 64 | types: { 65 | enabled: true, 66 | template: { 67 | typeName: true, 68 | }, 69 | }, 70 | moves: { 71 | enabled: false, 72 | }, 73 | items: { 74 | enabled: true, 75 | options: { 76 | customFields: { 77 | itemId: 'id', 78 | }, 79 | }, 80 | template: { 81 | itemId: true, 82 | type: true, 83 | minTrainerLevel: true, 84 | }, 85 | }, 86 | questRewards: { 87 | enabled: false, 88 | }, 89 | questConditions: { 90 | enabled: false, 91 | }, 92 | invasions: { 93 | enabled: true, 94 | }, 95 | weather: { 96 | enabled: true, 97 | }, 98 | translations: { 99 | enabled: true, 100 | options: { 101 | masterfileLocale: 'de', 102 | }, 103 | locales: { 104 | en: true, 105 | de: true, 106 | }, 107 | }, 108 | } 109 | const customData = await generate({ template }) // returns custom templated data 110 | 111 | const tr = await invasions() 112 | // returns the default settings 113 | ``` 114 | 115 | ### Local Usage/Testing 116 | 117 | 1. Clone the repo 118 | 2. `yarn install` 119 | 3. `tsc` will compile the TS into JS, then you can run `yarn generate`, which will generate a local `masterfile.json` for you to checkout 120 | 121 | - `tsc -w` will auto recompile the TypeScript during development 122 | - You can play with the input options by changing the scripts in `package.json` or modifying the `base.ts` file. 123 | - `yarn pokeapi`, which will generate a local `masterfile.json` and refresh the data in the `static` folder from PokeAPI 124 | - `yarn raw` will generate a local `masterfile.json` and with the raw data format 125 | 126 | The generate function accepts an object with the following properties: 127 | 128 | - `template` (object): Your template for each of the categories 129 | - `safe` (boolean): Fetches an already built masterfile with known safe values 130 | - `url` (string): Custom url to fetch the masterfile from, results not guaranteed 131 | - `test` (boolean): Writes the masterfile to a local json 132 | - `raw` (boolean): Returns the data in its raw format without any template processing 133 | - `pokeApi` (boolean): Fetches fresh data from PokeAPI 134 | 135 | To view some static examples of what this library can create, check out these repos: 136 | [Masterfiles](https://github.com/WatWowMap/Masterfile-Generator) 137 | [Translations](https://github.com/WatWowMap/pogo-translations) 138 | 139 | To view the full list of available options, check out the [Wiki](https://github.com/WatWowMap/Pogo-Data-Generator/wiki/Full-API-Options)! 140 | -------------------------------------------------------------------------------- /devWrapper.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | import { generate, invasions } from './src/index' 3 | import baseStats from './static/baseStats.json' 4 | import tempEvos from './static/tempEvos.json' 5 | import types from './static/types.json' 6 | 7 | const main = async () => { 8 | const mfData = await fetch( 9 | 'https://raw.githubusercontent.com/PokeMiners/game_masters/master/latest/latest.json', 10 | ) 11 | const mf = await mfData.json() 12 | fs.writeFileSync('./latest.json', JSON.stringify(mf, null, 2), 'utf8') 13 | 14 | console.time('Generated in') 15 | const data = await generate({ 16 | raw: process.argv.includes('--raw'), 17 | test: process.argv.includes('--test'), 18 | pokeApi: process.argv.includes('--pokeapi') || { 19 | baseStats, 20 | tempEvos, 21 | types, 22 | }, 23 | }) 24 | 25 | if (process.argv.includes('--test')) { 26 | if (process.argv.includes('--invasions')) { 27 | fs.writeFile( 28 | './invasions.json', 29 | JSON.stringify(await invasions(), null, 2), 30 | 'utf8', 31 | () => {}, 32 | ) 33 | } 34 | if (data?.AllPokeApi) { 35 | const { baseStats, tempEvos, types } = data.AllPokeApi 36 | fs.writeFile( 37 | './static/baseStats.json', 38 | JSON.stringify(baseStats, null, 2), 39 | 'utf8', 40 | () => {}, 41 | ) 42 | fs.writeFile( 43 | './static/tempEvos.json', 44 | JSON.stringify(tempEvos, null, 2), 45 | 'utf8', 46 | () => {}, 47 | ) 48 | fs.writeFile( 49 | './static/types.json', 50 | JSON.stringify(types, null, 2), 51 | 'utf8', 52 | () => {}, 53 | ) 54 | delete data.AllPokeApi 55 | } 56 | if (data) { 57 | fs.writeFile( 58 | './masterfile.json', 59 | JSON.stringify(data, null, 2), 60 | 'utf8', 61 | () => {}, 62 | ) 63 | } 64 | } 65 | console.timeEnd('Generated in') 66 | } 67 | 68 | main() 69 | .catch((e) => console.log(e)) 70 | .then(() => console.log('New masterfile generated')) 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pogo-data-generator", 3 | "version": "1.18.6", 4 | "description": "Pokemon GO project data generator", 5 | "author": "TurtIeSocks", 6 | "license": "Apache-2.0", 7 | "keywords": [ 8 | "pogo", 9 | "pokemon", 10 | "go", 11 | "data", 12 | "generator", 13 | "masterfile" 14 | ], 15 | "files": [ 16 | "/dist" 17 | ], 18 | "main": "dist/index.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "start": "node .", 22 | "generate": "ts-node devWrapper.ts --test", 23 | "pokeapi": "ts-node devWrapper.ts --test --pokeapi", 24 | "raw": "ts-node devWrapper.ts --test --raw", 25 | "invasions": "ts-node devWrapper.ts --test --invasions", 26 | "test": "tsc && ./node_modules/.bin/jest", 27 | "format": "prettier --config .prettierrc 'src/**/*.ts' --write", 28 | "publishBuild": "rm -r dist && tsc", 29 | "prettier": "prettier --write \"**/*.ts\"" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/WatWowMap/pogo-data-generator" 34 | }, 35 | "bugs": { 36 | "url": "https://github.com/WatWowMap/Pogo-Data-Generator/issues" 37 | }, 38 | "dependencies": { 39 | "@na-ji/pogo-protos": "<3.0.0", 40 | "jszip": "^3.10.1", 41 | "node-fetch": "2.6.7" 42 | }, 43 | "engines": { 44 | "node": ">=16.0.0" 45 | }, 46 | "prettier": { 47 | "arrowParens": "always", 48 | "trailingComma": "all", 49 | "semi": false, 50 | "singleQuote": true 51 | }, 52 | "devDependencies": { 53 | "@types/node": "*", 54 | "@types/node-fetch": "^2.6.2", 55 | "jest": "^29.2.1", 56 | "prettier": "^2.7.1", 57 | "ts-node": "^10.9.1", 58 | "typescript": "^4.5.5" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/base.ts: -------------------------------------------------------------------------------- 1 | import { FullTemplate } from './typings/inputs' 2 | 3 | const baseTemplate: FullTemplate = { 4 | globalOptions: { 5 | keyJoiner: '_', 6 | customChildObj: {}, 7 | customFields: {}, 8 | genderString: false, 9 | snake_case: true, 10 | includeProtos: true, 11 | }, 12 | pokemon: { 13 | enabled: true, 14 | options: { 15 | topLevelName: 'pokemon', 16 | keys: { 17 | main: 'pokedexId', 18 | forms: 'formId', 19 | evolutions: false, 20 | tempEvolutions: 'tempEvoId', 21 | types: false, 22 | quickMoves: false, 23 | chargedMoves: false, 24 | questRequirement: false, 25 | costumeOverrideEvos: false, 26 | // sizeSettings: 'name', 27 | }, 28 | customFields: { 29 | evoId: 'pokemon', 30 | formId: 'form', 31 | formName: 'name', 32 | pokemonName: 'name', 33 | }, 34 | makeSingular: { 35 | itemRequirement: false, 36 | questRequirement: false, 37 | }, 38 | unsetDefaultForm: false, 39 | includeUnset: false, 40 | skipNormalIfUnset: false, 41 | skipForms: [], 42 | noFormPlaceholders: false, 43 | includeEstimatedPokemon: { 44 | baseStats: true, 45 | mega: true, 46 | }, 47 | processFormsSeparately: false, 48 | includeRawForms: false, 49 | }, 50 | template: { 51 | pokedexId: true, 52 | pokemonName: true, 53 | formId: false, 54 | forms: { 55 | formId: false, 56 | formName: true, 57 | proto: true, 58 | isCostume: true, 59 | evolutions: { 60 | evoId: true, 61 | formId: true, 62 | genderRequirement: true, 63 | candyCost: false, 64 | itemRequirement: false, 65 | tradeBonus: false, 66 | questRequirement: false, 67 | }, 68 | tempEvolutions: {}, 69 | attack: true, 70 | defense: true, 71 | stamina: true, 72 | height: true, 73 | weight: true, 74 | types: 'typeName', 75 | quickMoves: 'moveName', 76 | chargedMoves: 'moveName', 77 | family: true, 78 | little: true, 79 | purificationCandy: false, 80 | purificationDust: false, 81 | tradable: false, 82 | transferable: false, 83 | bonusCandyCapture: false, 84 | bonusStardustCapture: false, 85 | costumeOverrideEvos: { 86 | costumeId: true, 87 | costumeProto: true, 88 | costumeName: true, 89 | }, 90 | // sizeSettings: 'value', 91 | }, 92 | // sizeSettings: 'value', 93 | defaultFormId: true, 94 | genId: true, 95 | generation: true, 96 | types: 'typeName', 97 | quickMoves: 'moveName', 98 | chargedMoves: 'moveName', 99 | attack: true, 100 | defense: true, 101 | stamina: true, 102 | height: true, 103 | weight: true, 104 | fleeRate: true, 105 | captureRate: true, 106 | tempEvolutions: { 107 | tempEvoId: false, 108 | attack: true, 109 | defense: true, 110 | stamina: true, 111 | height: true, 112 | weight: true, 113 | types: 'typeName', 114 | unreleased: true, 115 | firstEnergyCost: false, 116 | subsequentEnergyCost: false, 117 | }, 118 | costumeOverrideEvos: 'costumeId', 119 | evolutions: { 120 | evoId: true, 121 | formId: true, 122 | genderRequirement: true, 123 | candyCost: false, 124 | itemRequirement: false, 125 | tradeBonus: false, 126 | questRequirement: false, 127 | }, 128 | legendary: true, 129 | mythic: true, 130 | buddyGroupNumber: true, 131 | buddyDistance: true, 132 | buddyMegaEnergy: false, 133 | thirdMoveStardust: true, 134 | thirdMoveCandy: true, 135 | gymDefenderEligible: true, 136 | tradable: false, 137 | transferable: false, 138 | family: true, 139 | little: true, 140 | jungle: false, 141 | unreleased: false, 142 | bonusCandyCapture: false, 143 | bonusStardustCapture: false, 144 | }, 145 | }, 146 | costumes: { 147 | enabled: false, 148 | options: { 149 | keys: { 150 | main: 'id', 151 | }, 152 | }, 153 | template: { 154 | id: true, 155 | name: true, 156 | proto: true, 157 | noEvolve: true, 158 | }, 159 | }, 160 | types: { 161 | enabled: true, 162 | options: { 163 | topLevelName: 'types', 164 | keys: { 165 | main: 'typeId', 166 | }, 167 | }, 168 | template: 'typeName', 169 | }, 170 | moves: { 171 | enabled: true, 172 | options: { 173 | topLevelName: 'moves', 174 | keys: { 175 | main: 'moveId', 176 | type: false, 177 | }, 178 | customFields: { 179 | moveId: 'id', 180 | moveName: 'name', 181 | }, 182 | }, 183 | template: { 184 | moveId: true, 185 | moveName: true, 186 | proto: true, 187 | type: 'typeName', 188 | power: true, 189 | }, 190 | }, 191 | items: { 192 | enabled: true, 193 | options: { 194 | topLevelName: 'items', 195 | keys: { 196 | main: 'itemId', 197 | }, 198 | customFields: { 199 | itemId: 'id', 200 | itemName: 'name', 201 | }, 202 | snake_case: false, 203 | minTrainerLevel: 50, 204 | }, 205 | template: { 206 | itemId: false, 207 | itemName: true, 208 | proto: true, 209 | type: true, 210 | category: true, 211 | minTrainerLevel: true, 212 | }, 213 | }, 214 | questTypes: { 215 | enabled: true, 216 | options: { 217 | topLevelName: 'questTypes', 218 | keys: { 219 | main: 'id', 220 | }, 221 | }, 222 | template: { 223 | id: false, 224 | proto: true, 225 | formatted: true, 226 | }, 227 | }, 228 | questConditions: { 229 | enabled: true, 230 | options: { 231 | topLevelName: 'questConditions', 232 | keys: { 233 | main: 'id', 234 | }, 235 | }, 236 | template: { 237 | id: false, 238 | proto: true, 239 | formatted: true, 240 | }, 241 | }, 242 | questRewardTypes: { 243 | enabled: true, 244 | options: { 245 | topLevelName: 'questRewardTypes', 246 | keys: { 247 | main: 'id', 248 | }, 249 | }, 250 | template: { 251 | id: false, 252 | proto: true, 253 | formatted: true, 254 | }, 255 | }, 256 | invasions: { 257 | enabled: true, 258 | options: { 259 | topLevelName: 'invasions', 260 | keys: { 261 | main: 'id', 262 | encounters: 'position', 263 | }, 264 | includeBalloons: true, 265 | customFields: { 266 | first: 'first', 267 | second: 'second', 268 | third: 'third', 269 | }, 270 | placeholderData: true, 271 | }, 272 | template: { 273 | active: true, 274 | id: false, 275 | type: true, 276 | gender: true, 277 | grunt: true, 278 | firstReward: true, 279 | secondReward: true, 280 | thirdReward: true, 281 | encounters: 'id', 282 | }, 283 | }, 284 | weather: { 285 | enabled: true, 286 | options: { 287 | topLevelName: 'weather', 288 | keys: { 289 | main: 'weatherId', 290 | }, 291 | customFields: { 292 | weatherName: 'name', 293 | }, 294 | }, 295 | template: { 296 | weatherId: false, 297 | weatherName: true, 298 | proto: false, 299 | types: 'typeName', 300 | }, 301 | }, 302 | raids: { 303 | enabled: false, 304 | options: { 305 | keys: { 306 | main: 'id', 307 | }, 308 | }, 309 | template: 'formatted', 310 | }, 311 | teams: { 312 | enabled: false, 313 | options: { 314 | keys: { 315 | main: 'id', 316 | }, 317 | }, 318 | template: 'formatted', 319 | }, 320 | routeTypes: { 321 | enabled: false, 322 | options: { 323 | keys: { 324 | main: 'id', 325 | }, 326 | }, 327 | template: 'formatted', 328 | }, 329 | translations: { 330 | enabled: true, 331 | options: { 332 | topLevelName: 'translations', 333 | prefix: { 334 | pokemon: 'poke_', 335 | forms: 'form_', 336 | costumes: 'costume_', 337 | alignment: 'alignment_', 338 | evolutions: 'evo_', 339 | descriptions: 'desc_', 340 | moves: 'move_', 341 | items: 'item_', 342 | weather: 'weather_', 343 | types: 'poke_type_', 344 | grunts: 'grunt_', 345 | gruntsAlt: 'grunt_a_', 346 | characterCategories: 'character_category_', 347 | lures: 'lure_', 348 | throwTypes: 'throw_type_', 349 | pokemonCategories: 'pokemon_categories_', 350 | questTypes: 'quest_', 351 | questConditions: 'quest_condition_', 352 | questRewardTypes: 'quest_reward_', 353 | questTitles: 'quest_title_', 354 | raidLevel: 'raid_', 355 | eggLevel: 'egg_', 356 | }, 357 | questVariables: { 358 | prefix: '{{', 359 | suffix: '}}', 360 | }, 361 | questTitleTermsToSkip: [ 362 | 'gofest', 363 | 'gotour', 364 | 'dialog', 365 | 'title', 366 | 'summer_return', 367 | '_complete_', 368 | 'location', 369 | 'vote', 370 | ], 371 | masterfileLocale: false, 372 | manualTranslations: true, 373 | mergeCategories: true, 374 | useLanguageAsRef: false, 375 | }, 376 | locales: { 377 | de: true, 378 | en: true, 379 | es: false, 380 | fr: true, 381 | hi: false, 382 | id: false, 383 | it: false, 384 | ja: false, 385 | ko: false, 386 | 'pt-br': false, 387 | ru: false, 388 | th: false, 389 | 'zh-tw': false, 390 | tr: false, 391 | }, 392 | template: { 393 | pokemon: { 394 | names: true, 395 | forms: true, 396 | descriptions: true, 397 | }, 398 | moves: true, 399 | items: true, 400 | types: true, 401 | characters: true, 402 | weather: true, 403 | misc: true, 404 | pokemonCategories: true, 405 | quests: true, 406 | }, 407 | }, 408 | } 409 | 410 | export default baseTemplate 411 | -------------------------------------------------------------------------------- /src/classes/Apk.ts: -------------------------------------------------------------------------------- 1 | import JSZip from 'jszip' 2 | 3 | export default class ApkReader { 4 | texts: Record> 5 | codeMap: Record 6 | files: JSZip | null 7 | 8 | constructor() { 9 | this.texts = {} 10 | this.codeMap = { 11 | 'pt-br': 'pt-br', 12 | 'zh-tw': 'zh-tw', 13 | 'en-us': 'en', 14 | 'fr-fr': 'fr', 15 | 'de-de': 'de', 16 | 'hi-in': 'hi', 17 | 'id-id': 'id', 18 | 'it-it': 'it', 19 | 'ja-jp': 'ja', 20 | 'ko-kr': 'ko', 21 | 'ru-ru': 'ru', 22 | 'es-es': 'es', 23 | 'es-mx': 'es-mx', 24 | 'th-th': 'th', 25 | 'tr-tr': 'tr', 26 | } 27 | this.files = null 28 | } 29 | 30 | removeEscapes(str: string) { 31 | return str.replace(/\r/g, '').replace(/\n/g, '').replace(/\"/g, '”') 32 | } 33 | 34 | async fetchApk() { 35 | try { 36 | const index = await fetch('https://mirror.unownhash.com/index.json') 37 | 38 | if (!index.ok) { 39 | throw new Error('Unable to fetch index') 40 | } 41 | const data = await index.json() 42 | const first = data[0].filename 43 | 44 | const response = await fetch(`https://mirror.unownhash.com/apks/${first}`) 45 | const apk = await response.arrayBuffer() 46 | const zip = new JSZip() 47 | const raw = await zip.loadAsync(apk) 48 | const file = raw.files['base.apk'] 49 | const buffer = await file.async('nodebuffer') 50 | this.files = await zip.loadAsync(buffer) 51 | } catch (e) { 52 | console.warn(e, 'Issue with downloading APK') 53 | } 54 | } 55 | 56 | async extractTexts() { 57 | if (!this.files) return 58 | try { 59 | const textFiles = Object.keys(this.files.files).filter((file) => 60 | file.startsWith('assets/text'), 61 | ) 62 | await Promise.all( 63 | textFiles.map(async (file) => { 64 | try { 65 | const text = await this.files.file(file)?.async('text') 66 | const { data } = JSON.parse(text) 67 | const relativePath = file 68 | .replace('assets/text/', '') 69 | .replace('.json', '') 70 | .replace('i18n_', '') 71 | const code = this.codeMap[relativePath] 72 | 73 | if (!code) { 74 | throw new Error(relativePath) 75 | } 76 | 77 | this.texts[code] = {} 78 | for (let i = 0; i < data.length; i++) { 79 | this.texts[code][data[i]] = this.removeEscapes(data[++i]) 80 | } 81 | } catch (e) { 82 | if (e instanceof Error) { 83 | console.error('Unknown language code', e.message) 84 | } 85 | } 86 | }), 87 | ) 88 | } catch (e) { 89 | console.warn(e, 'Issue with extracting texts') 90 | } 91 | } 92 | 93 | cleanup() { 94 | if (this.files) delete this.files 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/classes/Invasion.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | 3 | import { AllInvasions } from '../typings/dataTypes' 4 | import { Options } from '../typings/inputs' 5 | import { InvasionInfo } from '../typings/pogoinfo' 6 | import Masterfile from './Masterfile' 7 | 8 | export default class Invasion extends Masterfile { 9 | parsedInvasions: AllInvasions 10 | options: Options 11 | 12 | constructor(options: Options) { 13 | super() 14 | this.options = options 15 | this.parsedInvasions = {} 16 | } 17 | 18 | async customInvasions(override: boolean = false): Promise { 19 | try { 20 | if ( 21 | this.options.customInvasions === true || 22 | (this.options.customInvasions === undefined && override) 23 | ) { 24 | return this.fetch( 25 | 'https://raw.githubusercontent.com/WatWowMap/Masterfile-Generator/master/custom-invasions.json', 26 | ) 27 | } else if (this.options.customInvasions) { 28 | return this.options.customInvasions as InvasionInfo 29 | } else { 30 | return {} 31 | } 32 | } catch (e) { 33 | console.warn(e, 'Unable to get custom invasions') 34 | } 35 | } 36 | 37 | mergeInvasions(existing: InvasionInfo, custom: InvasionInfo = {}) { 38 | const invasions = existing 39 | Object.entries(custom).forEach(([key, info]) => { 40 | if (invasions[key] === undefined) { 41 | invasions[key] = info 42 | } else { 43 | invasions[key] = { 44 | ...invasions[key], 45 | ...info, 46 | } 47 | } 48 | }) 49 | return invasions 50 | } 51 | 52 | formatGrunts(character: string) { 53 | const base = character 54 | .replace('CHARACTER_', '') 55 | .replace('_MALE', '') 56 | .replace('_FEMALE', '') 57 | const type = base 58 | .replace('EXECUTIVE_', '') 59 | .replace('_GRUNT', '') 60 | .replace('EVENT_', '') 61 | const grunt = 62 | base.split('_').length > 1 63 | ? base.replace(`${type}`, '').replace('_', '') 64 | : base 65 | let gender = 66 | character.includes('MALE') || character.includes('FEMALE') ? 1 : 0 67 | if (character.includes('FEMALE')) { 68 | gender = 2 69 | } 70 | return { 71 | type: type === 'GRUNT' ? 'Mixed' : this.capitalize(type), 72 | gender: this.options.genderString ? this.genders[gender] : gender, 73 | grunt: this.capitalize(grunt), 74 | } 75 | } 76 | 77 | invasions(invasionData: InvasionInfo) { 78 | const positions = [ 79 | this.customFieldNames.first || 'first', 80 | this.customFieldNames.second || 'second', 81 | this.customFieldNames.third || 'third', 82 | ] 83 | Object.entries(Rpc.EnumWrapper.InvasionCharacter).forEach((proto) => { 84 | try { 85 | const [name, id] = proto 86 | if ( 87 | (this.options.includeBalloons && name.includes('BALLOON')) || 88 | !name.includes('BALLOON_') 89 | ) { 90 | const pogoInfo = invasionData[id] 91 | this.parsedInvasions[id] = { 92 | id: +id, 93 | ...this.formatGrunts(name), 94 | proto: name, 95 | active: !!pogoInfo?.active, 96 | firstReward: false, 97 | secondReward: false, 98 | thirdReward: false, 99 | } 100 | if (pogoInfo && pogoInfo.active) { 101 | this.parsedInvasions[id].firstReward = 102 | pogoInfo.lineup.rewards.includes(0) 103 | this.parsedInvasions[id].secondReward = 104 | pogoInfo.lineup.rewards.includes(1) 105 | this.parsedInvasions[id].thirdReward = 106 | pogoInfo.lineup.rewards.includes(2) 107 | this.parsedInvasions[id].encounters = [] 108 | 109 | positions.forEach((position, i) => { 110 | pogoInfo.lineup.team[i].forEach((pkmn) => { 111 | this.parsedInvasions[id].encounters.push({ 112 | id: pkmn.id, 113 | formId: pkmn.form, 114 | position, 115 | }) 116 | }) 117 | this.parsedInvasions[id].encounters.sort((a, b) => a.id - b.id) 118 | this.parsedInvasions[id].encounters.sort((a, b) => 119 | a.position.localeCompare(b.position), 120 | ) 121 | }) 122 | } else if (this.options.placeholderData) { 123 | this.parsedInvasions[id].encounters = positions.map((position) => ({ 124 | position, 125 | })) 126 | } 127 | } 128 | } catch (e) { 129 | console.warn(e, proto) 130 | } 131 | }) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/classes/Item.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | 3 | import Masterfile from './Masterfile' 4 | import { AllItems } from '../typings/dataTypes' 5 | import { NiaMfObj } from '../typings/general' 6 | import { ItemProto } from '../typings/protos' 7 | import { Options } from '../typings/inputs' 8 | 9 | export default class Item extends Masterfile { 10 | options: Options 11 | parsedItems: AllItems 12 | 13 | constructor(options: Options) { 14 | super() 15 | this.options = options 16 | this.parsedItems = {} 17 | } 18 | 19 | addItem(object: NiaMfObj) { 20 | try { 21 | const { 22 | data: { 23 | itemSettings: { itemId, itemType, category, dropTrainerLevel }, 24 | }, 25 | templateId, 26 | } = object 27 | if ( 28 | !this.options.minTrainerLevel || 29 | !dropTrainerLevel || 30 | dropTrainerLevel <= this.options.minTrainerLevel 31 | ) { 32 | const id = 33 | typeof itemId === 'string' ? Rpc.Item[itemId as ItemProto] : itemId 34 | this.parsedItems[id] = { 35 | itemId: id, 36 | itemName: templateId 37 | ? this.capitalize(templateId.replace('ITEM_', '')) 38 | : '', 39 | proto: templateId, 40 | type: 41 | typeof itemType === 'string' 42 | ? this.capitalize(itemType.replace('ITEM_TYPE_', '')) 43 | : '', 44 | category: category 45 | ? this.capitalize(category.replace('ITEM_CATEGORY_', '')) 46 | : '', 47 | minTrainerLevel: dropTrainerLevel, 48 | } 49 | } 50 | } catch (e) { 51 | console.warn(e, '\n', object) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/classes/Masterfile.ts: -------------------------------------------------------------------------------- 1 | import { Options, FullTemplate } from '../typings/inputs' 2 | import { FinalResult } from '../typings/dataTypes' 3 | 4 | if (typeof fetch === 'undefined') { 5 | global.fetch = require('node-fetch') 6 | } 7 | 8 | export default class Masterfile { 9 | customFieldNames: { [id: string]: string } 10 | genders: { [id: string]: string } 11 | 12 | constructor() { 13 | this.customFieldNames = {} 14 | this.genders = { 15 | 1: 'Male', 16 | 2: 'Female', 17 | } 18 | } 19 | 20 | static templateMerger( 21 | template: { [key: string]: any }, 22 | base: FullTemplate, 23 | ): FullTemplate { 24 | const baseline: { [key: string]: any } = base 25 | const merged: { [key: string]: any } = {} 26 | Object.keys(base).forEach((category) => { 27 | merged[category] = template[category] || {} 28 | Object.keys(baseline[category]).forEach((subKey) => { 29 | if (merged[category][subKey] === undefined) { 30 | merged[category][subKey] = 31 | typeof baseline[category][subKey] === 'boolean' 32 | ? false 33 | : baseline[category][subKey] 34 | } 35 | }) 36 | if (category !== 'globalOptions') { 37 | const globalOptions = template.globalOptions || baseline.globalOptions 38 | Object.entries(globalOptions).forEach((option) => { 39 | const [optionKey, optionValue] = option 40 | if (merged[category].options[optionKey] === undefined) { 41 | if (template.globalOptions) { 42 | merged[category].options[optionKey] = optionValue 43 | } else { 44 | merged[category].options[optionKey] = 45 | typeof optionValue === 'boolean' ? false : optionValue 46 | } 47 | } 48 | }) 49 | } 50 | if (category === 'translations' && template.translations) { 51 | merged.translations.options.questVariables = { 52 | ...base.translations.options.questVariables, 53 | ...template.translations.options.questVariables, 54 | } 55 | merged.translations.options.prefix = { 56 | ...base.translations.options.prefix, 57 | ...template.translations.options.prefix, 58 | } 59 | if (!template.translations.options.questTitleTermsToSkip) { 60 | merged.translations.options.questTitleTermsToSkip = 61 | base.translations.options.questTitleTermsToSkip 62 | } 63 | } 64 | }) 65 | return merged 66 | } 67 | 68 | async fetch(url: string, text = false): Promise { 69 | try { 70 | const data = await fetch(url) 71 | if (!data.ok) { 72 | throw new Error(`${data.status} ${data.statusText} URL: ${url}`) 73 | } 74 | return text ? data.text() : data.json() 75 | } catch (e) { 76 | if (e instanceof Error) { 77 | console.warn(e.message, `Unable to fetch ${url}`) 78 | } 79 | } 80 | } 81 | 82 | capitalize(string: string) { 83 | if (string) { 84 | const capitalizeList = ['pvp', 'xl', 'npc', 'cp', 'poi', 'gbl'] 85 | try { 86 | string = string.toLowerCase() 87 | if (string.split('_').length > 1) { 88 | let processed = '' 89 | string.split('_').forEach((word: string) => { 90 | if (capitalizeList.includes(word)) { 91 | processed += ` ${word.toUpperCase()}` 92 | } else { 93 | processed += ` ${word.charAt(0).toUpperCase() + word.slice(1)}` 94 | } 95 | }) 96 | return processed.slice(1) 97 | } else { 98 | return string.charAt(0).toUpperCase() + string.slice(1) 99 | } 100 | } catch (e) { 101 | if (e instanceof Error) { 102 | console.warn(e.message, '\n', string) 103 | } 104 | } 105 | } 106 | } 107 | 108 | compare(formData: number[], parentData: number[]) { 109 | try { 110 | if (formData && parentData) { 111 | try { 112 | return ( 113 | formData.every((x, i) => x === parentData[i]) && 114 | formData.length === parentData.length 115 | ) 116 | } catch (e) { 117 | console.warn(e, '\nForm:', formData, '\nParent:', parentData) 118 | } 119 | } 120 | } catch (e) { 121 | console.warn(e, `Failed to compare ${formData} and ${parentData}`) 122 | } 123 | } 124 | 125 | templater( 126 | data: any, 127 | settings: { template: any; options: Options }, 128 | reference: FinalResult = {}, 129 | ) { 130 | // loops through the raw data and outputs the desired template 131 | const { template, options } = settings 132 | if (!options.customFields) { 133 | options.customFields = {} 134 | } 135 | if (!options.customChildObj) { 136 | options.customChildObj = {} 137 | } 138 | if (!options.makeSingular) { 139 | options.makeSingular = {} 140 | } 141 | const resolved: any = options.keys.main ? {} : [] 142 | 143 | const parseData = ( 144 | fieldKey: string, 145 | fieldValue: any, 146 | templateChild: any, 147 | data: any, 148 | ) => { 149 | // turns integer references into values 150 | const isObj = options.keys[fieldKey] 151 | let returnValue: any = isObj ? {} : [] 152 | 153 | if (!Array.isArray(fieldValue)) { 154 | fieldValue = [fieldValue] 155 | } 156 | fieldValue.forEach((x: any) => { 157 | const child = loopFields(fieldKey, x, templateChild, data) 158 | 159 | if (child) { 160 | if (isObj) { 161 | const customKey = reference[fieldKey] 162 | ? this.keyResolver(fieldKey, reference[fieldKey][x], options) 163 | : this.keyResolver(fieldKey, x, options) 164 | if (fieldKey === 'encounters') { 165 | // edge case for encounters 166 | if (returnValue[customKey]) { 167 | returnValue[customKey].push(child) 168 | } else if (typeof child === 'object') { 169 | if (Object.keys(child || {}).length) { 170 | returnValue[customKey] = [child] 171 | } else { 172 | returnValue[customKey] = [] 173 | } 174 | } else { 175 | returnValue[customKey] = [child] 176 | } 177 | } else { 178 | returnValue[customKey] = child 179 | } 180 | } else if (options.makeSingular[fieldKey]) { 181 | returnValue = child 182 | } else if (Array.isArray(returnValue)) { 183 | returnValue.push(child) 184 | } 185 | } 186 | }) 187 | if (options.processFormsSeparately && fieldKey === 'forms') { 188 | returnValue = returnValue[0] 189 | } 190 | // edge case for single move type 191 | return fieldKey === 'type' && !isObj ? returnValue[0] : returnValue 192 | } 193 | 194 | const loopFields = ( 195 | fieldKey: string, 196 | x: number, 197 | templateChild: any, 198 | data: any, 199 | ) => { 200 | // checks which fields are in the template and if the data is an object, loops through again 201 | let returnedObj: any = {} 202 | const ref = reference[fieldKey] ? reference[fieldKey][x] : x 203 | 204 | try { 205 | Object.entries(ref).forEach((subField) => { 206 | let [subFieldKey, subFieldValue] = subField 207 | 208 | if (templateChild[fieldKey] === subFieldKey) { 209 | // allows for singular returns 210 | returnedObj = subFieldValue 211 | } else if (templateChild[fieldKey][subFieldKey]) { 212 | const customKey = this.keyFormatter(subFieldKey, options) 213 | 214 | if ( 215 | typeof subFieldValue === 'object' || 216 | (reference[subFieldKey] && subFieldValue) 217 | ) { 218 | if ( 219 | subFieldKey === 'evolutions' && 220 | (x === 776 || x === 777 || x === 778) 221 | ) { 222 | // Nidoran hack 223 | subFieldValue = 224 | data.pokedexId === 29 ? ref.evolutions[0] : ref.evolutions[1] 225 | } 226 | returnedObj[customKey] = parseData( 227 | subFieldKey, 228 | subFieldValue, 229 | templateChild[fieldKey], 230 | data, 231 | ) 232 | } else { 233 | if (options.customChildObj[subFieldKey]) { 234 | customChildObj( 235 | returnedObj, 236 | subFieldKey, 237 | customKey, 238 | subFieldValue, 239 | ) 240 | } else if (subFieldValue !== undefined) { 241 | returnedObj[customKey] = subFieldValue 242 | } 243 | } 244 | } 245 | }) 246 | } catch (e) { 247 | console.warn( 248 | `Ref or X is undefined and it probably shouldn't be for ${reference}[${fieldKey}][${x}]`, 249 | ) 250 | } 251 | return returnedObj 252 | } 253 | 254 | const customChildObj = ( 255 | target: any, 256 | key: string, 257 | customKey: string, 258 | field: any, 259 | ) => { 260 | const customObj = options.customChildObj[key] 261 | if (!target[customObj]) { 262 | target[customObj] = {} 263 | } 264 | target[customObj][customKey] = field 265 | } 266 | 267 | Object.keys(data).forEach((id) => { 268 | let parent: any = {} 269 | const mainKey = this.keyResolver('main', data[id], options) 270 | 271 | try { 272 | Object.entries(data[id]).forEach((field) => { 273 | const [fieldKey, fieldValue] = field 274 | 275 | if (template === fieldKey || template[fieldKey] === fieldKey) { 276 | // allows for singular returns 277 | parent = fieldValue 278 | } else if (template[fieldKey] && fieldValue !== undefined) { 279 | const customKey = this.keyFormatter(fieldKey, options) 280 | 281 | if (typeof fieldValue === 'object' || reference[fieldKey]) { 282 | parent[customKey] = parseData( 283 | fieldKey, 284 | fieldValue, 285 | template, 286 | data[id], 287 | ) 288 | } else { 289 | if (options.customChildObj[fieldKey]) { 290 | customChildObj(parent, fieldKey, customKey, fieldValue) 291 | } else { 292 | parent[customKey] = fieldValue 293 | } 294 | } 295 | } 296 | }) 297 | if (mainKey !== undefined && mainKey !== null) { 298 | resolved[mainKey] = parent 299 | } else if (Array.isArray(resolved)) { 300 | resolved.push(parent) 301 | } 302 | } catch (e) { 303 | console.warn(e, '\n', mainKey, data[id]) 304 | } 305 | }) 306 | return resolved 307 | } 308 | 309 | keyFormatter(key: string, options: Options) { 310 | // formats any custom set keys as well as the snake_case option 311 | if (options.customFields[key]) { 312 | return options.customFields[key] 313 | } 314 | if (options.snake_case) { 315 | return key.replace(/([a-z](?=[A-Z]))/g, '$1_').toLowerCase() 316 | } 317 | return key 318 | } 319 | 320 | keyResolver(key: string, data: any, options: Options) { 321 | // combines values together if parent objects have custom keys 322 | try { 323 | if (options.keys[key]) { 324 | const split = (options.keys[key] as string).split(' ') 325 | let newKey = '' 326 | if (split.length === 1) { 327 | newKey = data[split[0]] 328 | } else { 329 | split.forEach((field: string) => { 330 | newKey += 331 | data[field] || data[field] === 0 332 | ? `${data[field].toString().replace(' ', options.keyJoiner)}${ 333 | options.keyJoiner 334 | }` 335 | : '' 336 | }) 337 | if (newKey.endsWith(options.keyJoiner)) { 338 | newKey = newKey.slice(0, -options.keyJoiner.length) 339 | } 340 | } 341 | return newKey 342 | } 343 | } catch (e) { 344 | console.warn(e, '\n', data) 345 | } 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /src/classes/Misc.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | 3 | import Masterfile from './Masterfile' 4 | import type { MiscProto } from '../typings/dataTypes' 5 | 6 | export default class Misc extends Masterfile { 7 | routeTypes: { [key: string]: MiscProto } 8 | raidLevels: { [key: string]: MiscProto } 9 | teams: { [key: string]: MiscProto } 10 | 11 | constructor() { 12 | super() 13 | this.routeTypes = {} 14 | this.raidLevels = {} 15 | this.teams = {} 16 | } 17 | 18 | parse(proto: typeof Rpc[keyof typeof Rpc]) { 19 | return Object.fromEntries( 20 | Object.entries(proto).map(([key, value]) => [ 21 | value, 22 | { 23 | id: Number(value), 24 | formatted: this.capitalize(key), 25 | proto: key, 26 | }, 27 | ]), 28 | ) 29 | } 30 | 31 | parseRouteTypes() { 32 | try { 33 | this.routeTypes = this.parse(Rpc.RouteType) 34 | } catch (e) { 35 | console.warn('Issue parsing route type protos', e) 36 | } 37 | } 38 | 39 | parseRaidLevels() { 40 | try { 41 | this.raidLevels = this.parse(Rpc.RaidLevel) 42 | } catch (e) { 43 | console.warn('Issue parsing raid level protos', e) 44 | } 45 | } 46 | 47 | parseTeams() { 48 | try { 49 | this.teams = this.parse(Rpc.Team) 50 | } catch (e) { 51 | console.warn('Issue parsing team protos', e) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/classes/Move.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | 3 | import { NiaMfObj } from '../typings/general' 4 | import { AllMoves } from '../typings/dataTypes' 5 | import Masterfile from './Masterfile' 6 | import { MoveProto, TypeProto } from '../typings/protos' 7 | 8 | export default class Moves extends Masterfile { 9 | parsedMoves: AllMoves 10 | 11 | constructor() { 12 | super() 13 | this.parsedMoves = {} 14 | } 15 | 16 | protoMoves() { 17 | Object.entries(Rpc.HoloPokemonMove).forEach((proto) => { 18 | const [name, id] = proto 19 | if (!this.parsedMoves[id] && (id || id === 0)) { 20 | this.parsedMoves[id] = { 21 | moveId: +id, 22 | moveName: this.capitalize(name.replace('_FAST', '')), 23 | proto: name, 24 | fast: name.endsWith('_FAST'), 25 | } 26 | } 27 | }) 28 | } 29 | 30 | addMoveSettings(object: NiaMfObj) { 31 | const { 32 | templateId, 33 | data: { moveSettings }, 34 | } = object 35 | try { 36 | const isMax = templateId.startsWith('VN_BM_') 37 | const proto = isMax ? templateId : templateId.substring(11) 38 | const id = Rpc.HoloPokemonMove[proto as MoveProto] 39 | if (id || id === 0) { 40 | if (!this.parsedMoves[id]) { 41 | this.parsedMoves[id] = { 42 | moveId: id, 43 | moveName: this.capitalize( 44 | isMax ? moveSettings.vfxName : proto.replace('_FAST', '') 45 | ), 46 | proto, 47 | fast: templateId.endsWith('_FAST'), 48 | } 49 | } 50 | this.parsedMoves[id].type = 51 | Rpc.HoloPokemonType[moveSettings.pokemonType as TypeProto] 52 | this.parsedMoves[id].power = isMax 53 | ? moveSettings.obMoveSettingsNumber18[2] : moveSettings.power 54 | this.parsedMoves[id].durationMs = moveSettings.durationMs 55 | this.parsedMoves[id].energyDelta = moveSettings.energyDelta 56 | } 57 | } catch (e) { 58 | console.warn(e, '\n', object) 59 | } 60 | } 61 | 62 | addCombatMove(object: NiaMfObj) { 63 | const { 64 | templateId, 65 | data: { combatMove }, 66 | } = object 67 | try { 68 | const id: number = 69 | Rpc.HoloPokemonMove[templateId.substring(18) as MoveProto] 70 | if (id || id === 0) { 71 | if (!this.parsedMoves[id]) { 72 | this.parsedMoves[id] = { 73 | moveId: id, 74 | moveName: this.capitalize( 75 | templateId.substring(18).replace('_FAST', ''), 76 | ), 77 | proto: templateId.substring(18), 78 | fast: templateId.endsWith('_FAST'), 79 | } 80 | } 81 | this.parsedMoves[id].type = 82 | Rpc.HoloPokemonType[combatMove.type as TypeProto] 83 | this.parsedMoves[id].pvpPower = combatMove.power 84 | this.parsedMoves[id].pvpEnergyDelta = combatMove.energyDelta 85 | if (combatMove.durationTurns) { 86 | this.parsedMoves[id].pvpDurationTurns = combatMove.durationTurns 87 | } 88 | if (combatMove.buffs) { 89 | this.parsedMoves[id].pvpBuffs = combatMove.buffs 90 | } 91 | } 92 | } catch (e) { 93 | console.warn(e, '\n', object) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/classes/PokeApi.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | import Masterfile from './Masterfile' 3 | import { 4 | AllMoves, 5 | AllPokemon, 6 | AllTypes, 7 | TempEvolutions, 8 | } from '../typings/dataTypes' 9 | import { TypeProto, PokemonIdProto, MoveProto } from '../typings/protos' 10 | import { 11 | BasePokeApiStruct, 12 | PokeApiStats, 13 | PokeApiTypes, 14 | } from '../typings/pokeapi' 15 | import { SpeciesApi } from '../typings/general' 16 | 17 | export default class PokeApi extends Masterfile { 18 | baseStats: AllPokemon 19 | tempEvos: { [id: string]: AllPokemon } 20 | types: AllTypes 21 | maxPokemon: number 22 | inconsistentStats: { 23 | [id: string]: { attack?: number; defense?: number; stamina?: number } 24 | } 25 | moveReference: AllMoves 26 | 27 | constructor() { 28 | super() 29 | this.baseStats = {} 30 | this.tempEvos = {} 31 | this.types = {} 32 | this.maxPokemon = 1008 33 | this.inconsistentStats = { 34 | 24: { 35 | attack: 167, 36 | }, 37 | 51: { 38 | attack: 167, 39 | defense: 134, 40 | }, 41 | 83: { 42 | attack: 124, 43 | }, 44 | 85: { 45 | attack: 218, 46 | defense: 140, 47 | }, 48 | 101: { 49 | attack: 173, 50 | defense: 173, 51 | }, 52 | 103: { 53 | defense: 149, 54 | }, 55 | 164: { 56 | attack: 145, 57 | }, 58 | 168: { 59 | defense: 124, 60 | }, 61 | 176: { 62 | attack: 139, 63 | }, 64 | 211: { 65 | defense: 138, 66 | }, 67 | 219: { 68 | attack: 139, 69 | stamina: 137, 70 | }, 71 | 222: { 72 | defense: 156, 73 | stamina: 146, 74 | }, 75 | 226: { 76 | attack: 148, 77 | stamina: 163, 78 | }, 79 | 227: { 80 | attack: 148, 81 | stamina: 163, 82 | }, 83 | 241: { 84 | attack: 157, 85 | }, 86 | 292: { 87 | stamina: 1, 88 | }, 89 | 809: { 90 | stamina: 264, 91 | }, 92 | } 93 | } 94 | set moves(parsed: AllMoves) { 95 | this.moveReference = parsed 96 | } 97 | 98 | static attack( 99 | normal: number, 100 | special: number, 101 | speed: number, 102 | nerf: boolean = false, 103 | ): number { 104 | return Math.round( 105 | Math.round( 106 | 2 * 107 | (0.875 * Math.max(normal, special) + 108 | 0.125 * Math.min(normal, special)), 109 | ) * 110 | (1 + (speed - 75) / 500) * 111 | (nerf ? 0.91 : 1), 112 | ) 113 | } 114 | 115 | static defense( 116 | normal: number, 117 | special: number, 118 | speed: number, 119 | nerf: boolean = false, 120 | ): number { 121 | return Math.round( 122 | Math.round( 123 | 2 * 124 | (0.625 * Math.max(normal, special) + 125 | 0.375 * Math.min(normal, special)), 126 | ) * 127 | (1 + (speed - 75) / 500) * 128 | (nerf ? 0.91 : 1), 129 | ) 130 | } 131 | 132 | static stamina(hp: number, nerf: boolean = false): number { 133 | return nerf 134 | ? Math.round((1.75 * hp + 50) * 0.91) 135 | : Math.floor(1.75 * hp + 50) 136 | } 137 | 138 | cp(atk: number, def: number, sta: number, cpm: number): number { 139 | return Math.floor( 140 | ((atk + 15) * (def + 15) ** 0.5 * (sta + 15) ** 0.5 * cpm ** 2) / 10, 141 | ) 142 | } 143 | 144 | megaLookup(id: string, type: string): string | 1 | 2 | 3 { 145 | switch (true) { 146 | case id.endsWith('mega-y'): 147 | return 3 148 | case id.endsWith('mega-x'): 149 | return 2 150 | case id.endsWith('mega'): 151 | return 1 152 | } 153 | return this.capitalize(type) 154 | } 155 | 156 | async setMaxPokemonId() { 157 | const { count } = await this.fetch( 158 | `https://pokeapi.co/api/v2/pokemon-species/?limit=1&offset=0`, 159 | ) 160 | this.maxPokemon = +count 161 | return +count 162 | } 163 | 164 | async baseStatsApi(parsedPokemon: AllPokemon, pokeApiIds?: number[]) { 165 | await Promise.all( 166 | Object.keys(parsedPokemon).map(async (id) => { 167 | if ( 168 | !parsedPokemon[id].attack || 169 | !parsedPokemon[id].defense || 170 | !parsedPokemon[id].stamina || 171 | parsedPokemon[id].types.length === 0 || 172 | (pokeApiIds && pokeApiIds.includes(+id)) 173 | ) { 174 | await this.pokemonApi(id) 175 | } 176 | }), 177 | ) 178 | } 179 | 180 | async extraPokemon(parsedPokemon: AllPokemon) { 181 | const extraPokemon: number[] = [] 182 | for (let i = 1; i <= this.maxPokemon; i++) { 183 | if (!parsedPokemon[i]) { 184 | extraPokemon.push(i) 185 | } 186 | } 187 | await Promise.all(extraPokemon.map((id) => this.pokemonApi(id))) 188 | } 189 | 190 | async pokemonApi(id: string | number) { 191 | try { 192 | const statsData: PokeApiStats = await this.fetch( 193 | `https://pokeapi.co/api/v2/pokemon/${id}/`, 194 | ) 195 | 196 | const baseStats: { [stat: string]: number } = {} 197 | statsData.stats.forEach((stat) => { 198 | baseStats[stat.stat.name] = stat.base_stat 199 | }) 200 | const initial: { 201 | attack: number 202 | defense: number 203 | stamina: number 204 | cp?: number 205 | } = { 206 | attack: PokeApi.attack( 207 | baseStats.attack, 208 | baseStats['special-attack'], 209 | baseStats.speed, 210 | ), 211 | defense: PokeApi.defense( 212 | baseStats.defense, 213 | baseStats['special-defense'], 214 | baseStats.speed, 215 | ), 216 | stamina: PokeApi.stamina(baseStats.hp), 217 | } 218 | initial.cp = this.cp( 219 | initial.attack, 220 | initial.defense, 221 | initial.stamina, 222 | 0.79030001, 223 | ) 224 | 225 | const nerfCheck = { 226 | attack: 227 | initial.cp > 4000 228 | ? PokeApi.attack( 229 | baseStats.attack, 230 | baseStats['special-attack'], 231 | baseStats.speed, 232 | true, 233 | ) 234 | : initial.attack, 235 | defense: 236 | initial.cp > 4000 237 | ? PokeApi.defense( 238 | baseStats.defense, 239 | baseStats['special-defense'], 240 | baseStats.speed, 241 | true, 242 | ) 243 | : initial.defense, 244 | stamina: 245 | initial.cp > 4000 246 | ? PokeApi.stamina(baseStats.hp, true) 247 | : initial.stamina, 248 | } 249 | this.baseStats[id] = { 250 | pokemonName: this.capitalize(statsData.name), 251 | quickMoves: statsData.moves 252 | .map( 253 | (move) => 254 | Rpc.HoloPokemonMove[ 255 | `${move.move.name 256 | .toUpperCase() 257 | .replace(/-/g, '_')}_FAST` as MoveProto 258 | ], 259 | ) 260 | .filter((move) => move && this.moveReference[move]?.power) 261 | .sort((a, b) => a - b), 262 | chargedMoves: statsData.moves 263 | .map( 264 | (move) => 265 | Rpc.HoloPokemonMove[ 266 | move.move.name.toUpperCase().replace(/-/g, '_') as MoveProto 267 | ], 268 | ) 269 | .filter((move) => move && this.moveReference[move]?.power) 270 | .sort((a, b) => a - b), 271 | attack: this.inconsistentStats[id] 272 | ? this.inconsistentStats[id].attack || nerfCheck.attack 273 | : nerfCheck.attack, 274 | defense: this.inconsistentStats[id] 275 | ? this.inconsistentStats[id].defense || nerfCheck.defense 276 | : nerfCheck.defense, 277 | stamina: this.inconsistentStats[id] 278 | ? this.inconsistentStats[id].stamina || nerfCheck.stamina 279 | : nerfCheck.stamina, 280 | types: statsData.types 281 | .map( 282 | (type) => 283 | Rpc.HoloPokemonType[ 284 | `POKEMON_TYPE_${type.type.name.toUpperCase()}` as TypeProto 285 | ], 286 | ) 287 | .sort((a, b) => a - b), 288 | unreleased: true, 289 | } 290 | } catch (e) { 291 | console.warn(e, `Failed to parse PokeApi Stats for #${id}`) 292 | } 293 | } 294 | 295 | async evoApi(evolvedPokemon: Set, parsedPokemon: AllPokemon) { 296 | await Promise.all( 297 | Object.keys(parsedPokemon).map(async (id) => { 298 | try { 299 | if (!evolvedPokemon.has(+id)) { 300 | const evoData: SpeciesApi = await this.fetch( 301 | `https://pokeapi.co/api/v2/pokemon-species/${id}`, 302 | ) 303 | if (this.baseStats[id]) { 304 | this.baseStats[id].legendary = evoData.is_legendary 305 | this.baseStats[id].mythic = evoData.is_mythical 306 | } 307 | if (evoData.evolves_from_species) { 308 | const prevEvoId = 309 | Rpc.HoloPokemonId[ 310 | evoData.evolves_from_species.name 311 | .toUpperCase() 312 | .replace('-', '_') as PokemonIdProto 313 | ] ?? +evoData.evolves_from_species.url.split('/').at(-2) 314 | if (prevEvoId) { 315 | if (!this.baseStats[prevEvoId]) { 316 | this.baseStats[prevEvoId] = {} 317 | } 318 | if (!this.baseStats[prevEvoId].evolutions) { 319 | this.baseStats[prevEvoId].evolutions = [] 320 | } 321 | this.baseStats[prevEvoId].evolutions.push({ 322 | evoId: +id, 323 | formId: 324 | parsedPokemon[id]?.defaultFormId || 325 | +Object.keys(parsedPokemon[id]?.forms || {})[0] || 326 | 0, 327 | }) 328 | this.baseStats[prevEvoId].evolutions.sort( 329 | (a, b) => a.evoId - b.evoId, 330 | ) 331 | evolvedPokemon.add(+id) 332 | } else { 333 | console.warn( 334 | 'Unable to find proto ID for', 335 | evoData.evolves_from_species.name 336 | .toUpperCase() 337 | .replace('-', '_'), 338 | ) 339 | } 340 | } 341 | } 342 | } catch (e) { 343 | console.warn(e, `Failed to parse PokeApi Evolutions for #${id}`) 344 | } 345 | }), 346 | ) 347 | } 348 | 349 | async tempEvoApi(parsedPokemon: AllPokemon) { 350 | const theoretical = { 351 | mega: [ 352 | 'alakazam-mega', 353 | 'kangaskhan-mega', 354 | 'pinsir-mega', 355 | 'aerodactyl-mega', 356 | 'mewtwo-mega-x', 357 | 'mewtwo-mega-y', 358 | 'steelix-mega', 359 | 'scizor-mega', 360 | 'heracross-mega', 361 | 'tyranitar-mega', 362 | 'sceptile-mega', 363 | 'blaziken-mega', 364 | 'swampert-mega', 365 | 'gardevoir-mega', 366 | 'sableye-mega', 367 | 'mawile-mega', 368 | 'aggron-mega', 369 | 'medicham-mega', 370 | 'sharpedo-mega', 371 | 'camerupt-mega', 372 | 'banette-mega', 373 | 'absol-mega', 374 | 'glalie-mega', 375 | 'garchomp-mega', 376 | 'lucario-mega', 377 | 'latias-mega', 378 | 'latios-mega', 379 | 'rayquaza-mega', 380 | 'metagross-mega', 381 | 'salamence-mega', 382 | 'gallade-mega', 383 | 'audino-mega', 384 | 'diancie-mega', 385 | ], 386 | } 387 | 388 | for (const [type, ids] of Object.entries(theoretical)) { 389 | this.tempEvos[type] = {} 390 | await Promise.all( 391 | ids.map(async (id) => { 392 | try { 393 | const pokemonId = 394 | Rpc.HoloPokemonId[ 395 | id.split('-')[0].toUpperCase() as PokemonIdProto 396 | ] 397 | const statsData: PokeApiStats = await this.fetch( 398 | `https://pokeapi.co/api/v2/pokemon/${id}/`, 399 | ) 400 | const baseStats: { [stat: string]: number } = {} 401 | statsData.stats.forEach((stat) => { 402 | baseStats[stat.stat.name] = stat.base_stat 403 | }) 404 | const types = statsData.types 405 | .map( 406 | (type) => 407 | Rpc.HoloPokemonType[ 408 | `POKEMON_TYPE_${type.type.name.toUpperCase()}` as TypeProto 409 | ], 410 | ) 411 | .sort((a, b) => a - b) 412 | 413 | const newTheoretical: TempEvolutions = { 414 | tempEvoId: this.megaLookup(id, type), 415 | attack: PokeApi.attack( 416 | baseStats.attack, 417 | baseStats['special-attack'], 418 | baseStats.speed, 419 | ), 420 | defense: PokeApi.defense( 421 | baseStats.defense, 422 | baseStats['special-defense'], 423 | baseStats.speed, 424 | ), 425 | stamina: PokeApi.stamina(baseStats.hp), 426 | types: this.compare(types, parsedPokemon[pokemonId].types) 427 | ? undefined 428 | : types, 429 | unreleased: true, 430 | } 431 | if (!this.tempEvos[type][pokemonId]) { 432 | this.tempEvos[type][pokemonId] = {} 433 | } 434 | if (!this.tempEvos[type][pokemonId].tempEvolutions) { 435 | this.tempEvos[type][pokemonId].tempEvolutions = [] 436 | } 437 | if ( 438 | !parsedPokemon[pokemonId].tempEvolutions || 439 | (parsedPokemon[pokemonId].tempEvolutions && 440 | !parsedPokemon[pokemonId].tempEvolutions.some( 441 | (temp) => temp.tempEvoId === newTheoretical.tempEvoId, 442 | )) 443 | ) { 444 | this.tempEvos[type][pokemonId].tempEvolutions.push(newTheoretical) 445 | } 446 | this.tempEvos[type][pokemonId].tempEvolutions.sort((a, b) => 447 | typeof a.tempEvoId === 'number' && typeof b.tempEvoId === 'number' 448 | ? a.tempEvoId - b.tempEvoId 449 | : a.tempEvoId.toString().localeCompare(b.tempEvoId.toString()), 450 | ) 451 | } catch (e) { 452 | console.warn(e, `Failed to parse PokeApi ${type} Evos for ${id}`) 453 | } 454 | }), 455 | ) 456 | } 457 | } 458 | 459 | async typesApi() { 460 | const getTypeIds = (types: { name: string }[]) => 461 | types 462 | .map( 463 | (type) => 464 | Rpc.HoloPokemonType[ 465 | `POKEMON_TYPE_${type.name.toUpperCase()}` as TypeProto 466 | ], 467 | ) 468 | .sort((a, b) => a - b) 469 | 470 | await Promise.all( 471 | Object.entries(Rpc.HoloPokemonType).map(async ([type, id]) => { 472 | try { 473 | const { 474 | damage_relations: { 475 | double_damage_from, 476 | double_damage_to, 477 | half_damage_from, 478 | half_damage_to, 479 | no_damage_from, 480 | no_damage_to, 481 | }, 482 | }: PokeApiTypes = id 483 | ? await this.fetch( 484 | `https://pokeapi.co/api/v2/type/${type 485 | .substring(13) 486 | .toLowerCase()}`, 487 | ) 488 | : { damage_relations: {} } 489 | this.types[id] = { 490 | strengths: id ? getTypeIds(double_damage_to) : [], 491 | weaknesses: id ? getTypeIds(double_damage_from) : [], 492 | veryWeakAgainst: id ? getTypeIds(no_damage_to) : [], 493 | immunes: id ? getTypeIds(no_damage_from) : [], 494 | weakAgainst: id ? getTypeIds(half_damage_to) : [], 495 | resistances: id ? getTypeIds(half_damage_from) : [], 496 | } 497 | } catch (e) { 498 | console.warn(`Unable to fetch ${type}`, e) 499 | } 500 | }), 501 | ) 502 | } 503 | 504 | async getGenerations() { 505 | const generations: { results: BasePokeApiStruct[] } = await this.fetch( 506 | 'https://pokeapi.co/api/v2/generation', 507 | ) 508 | const results = await Promise.all( 509 | generations.results.map(async (gen, index) => { 510 | const { 511 | main_region, 512 | pokemon_species, 513 | }: { 514 | main_region: BasePokeApiStruct 515 | pokemon_species: BasePokeApiStruct[] 516 | } = await this.fetch(gen.url) 517 | const name = this.capitalize(main_region.name) 518 | const pokemonIds = pokemon_species.map( 519 | (pokemon) => +pokemon.url.split('/').at(-2), 520 | ) 521 | const min = Math.min(...pokemonIds) 522 | const max = Math.max(...pokemonIds) 523 | return { id: index + 1, name, range: [min, max] } 524 | }), 525 | ) 526 | return Object.fromEntries(results.map(({ id, ...rest }) => [id, rest])) 527 | } 528 | } 529 | -------------------------------------------------------------------------------- /src/classes/Quest.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | 3 | import { AllQuests } from '../typings/dataTypes' 4 | import Masterfile from './Masterfile' 5 | 6 | export default class Quests extends Masterfile { 7 | parsedQuestTypes: AllQuests 8 | parsedRewardTypes: AllQuests 9 | parsedConditions: AllQuests 10 | 11 | constructor() { 12 | super() 13 | this.parsedQuestTypes = {} 14 | this.parsedRewardTypes = {} 15 | this.parsedConditions = {} 16 | } 17 | 18 | addQuest(category: string) { 19 | let parseTarget 20 | let protoTarget 21 | try { 22 | switch (category) { 23 | case 'types': 24 | parseTarget = this.parsedQuestTypes 25 | protoTarget = Rpc.QuestType 26 | break 27 | case 'rewards': 28 | parseTarget = this.parsedRewardTypes 29 | protoTarget = Rpc.QuestRewardProto.Type 30 | break 31 | case 'conditions': 32 | parseTarget = this.parsedConditions 33 | protoTarget = Rpc.QuestConditionProto.ConditionType 34 | break 35 | } 36 | } catch (e) { 37 | console.warn(e, `Failed to parse quest ${category}`) 38 | } 39 | Object.entries(protoTarget).forEach((proto) => { 40 | try { 41 | const [name, id] = proto 42 | parseTarget[id] = { 43 | id, 44 | proto: name, 45 | formatted: 46 | category === 'types' 47 | ? this.capitalize(name.replace('QUEST_', '')) 48 | : this.capitalize(name), 49 | } 50 | } catch (e) { 51 | console.warn(e, `Failed to parse quest ${proto}`) 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/classes/Types.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | import { AllTypes } from '../typings/dataTypes' 3 | import Masterfile from './Masterfile' 4 | 5 | export default class Types extends Masterfile { 6 | parsedTypes: AllTypes 7 | 8 | constructor() { 9 | super() 10 | this.parsedTypes = {} 11 | } 12 | 13 | buildTypes() { 14 | Object.entries(Rpc.HoloPokemonType).forEach((proto) => { 15 | try { 16 | const [name, id] = proto 17 | this.parsedTypes[id] = { 18 | typeId: +id, 19 | typeName: this.capitalize(name.substring(13)), 20 | } 21 | } catch (e) { 22 | console.warn(e, proto) 23 | } 24 | }) 25 | } 26 | 27 | parsePokeApi(types: AllTypes) { 28 | Object.keys(types).forEach((typeId) => { 29 | this.parsedTypes[typeId] = { 30 | ...this.parsedTypes[typeId], 31 | ...types[typeId], 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/classes/Weather.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | import { AllWeather } from '../typings/dataTypes' 3 | import { NiaMfObj } from '../typings/general' 4 | import { TypeProto } from '../typings/protos' 5 | 6 | import Masterfile from './Masterfile' 7 | export default class Weather extends Masterfile { 8 | rawWeather: { [id: string]: number[] } 9 | parsedWeather: AllWeather 10 | 11 | constructor() { 12 | super() 13 | this.rawWeather = {} 14 | this.parsedWeather = {} 15 | } 16 | 17 | buildWeather() { 18 | Object.entries(Rpc.GameplayWeatherProto.WeatherCondition).forEach( 19 | (proto) => { 20 | try { 21 | const [name, id] = proto 22 | this.parsedWeather[id] = { 23 | weatherId: +id, 24 | weatherName: this.capitalize(name), 25 | proto: name, 26 | types: this.rawWeather[name] || [], 27 | } 28 | } catch (e) { 29 | console.warn(e, proto) 30 | } 31 | }, 32 | ) 33 | } 34 | 35 | addWeather(object: NiaMfObj) { 36 | const { 37 | data: { 38 | weatherAffinities: { weatherCondition, pokemonType }, 39 | }, 40 | } = object 41 | this.rawWeather[weatherCondition] = pokemonType 42 | .map((type) => { 43 | return Rpc.HoloPokemonType[type as TypeProto] 44 | }) 45 | .sort((a, b) => a - b) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import Masterfile from './classes/Masterfile' 2 | import Pokemon from './classes/Pokemon' 3 | import Items from './classes/Item' 4 | import Moves from './classes/Move' 5 | import Quests from './classes/Quest' 6 | import Invasions from './classes/Invasion' 7 | import Types from './classes/Types' 8 | import Weather from './classes/Weather' 9 | import Translations from './classes/Translations' 10 | import PokeApi from './classes/PokeApi' 11 | import base from './base' 12 | 13 | import { 14 | Input, 15 | InvasionsOnly, 16 | Locales, 17 | PokemonTemplate, 18 | TranslationsTemplate, 19 | } from './typings/inputs' 20 | import { AllInvasions, FinalResult } from './typings/dataTypes' 21 | import { InvasionInfo } from './typings/pogoinfo' 22 | import { NiaMfObj } from './typings/general' 23 | import ApkReader from './classes/Apk' 24 | import Misc from './classes/Misc' 25 | 26 | export async function generate({ 27 | template, 28 | url, 29 | translationApkUrl, 30 | translationRemoteUrl, 31 | raw, 32 | pokeApi, 33 | test, 34 | }: Input = {}): Promise { 35 | const final: FinalResult = {} 36 | const urlToFetch = 37 | url || 38 | 'https://raw.githubusercontent.com/alexelgt/game_masters/refs/heads/master/GAME_MASTER.json' 39 | const { 40 | pokemon, 41 | types, 42 | costumes, 43 | moves, 44 | items, 45 | questTypes, 46 | questConditions, 47 | questRewardTypes, 48 | invasions, 49 | weather, 50 | translations, 51 | raids, 52 | routeTypes, 53 | teams, 54 | } = Masterfile.templateMerger(template || base, base) 55 | const localeCheck = 56 | translations.enabled && translations.options.masterfileLocale 57 | 58 | const AllPokemon = new Pokemon(pokemon.options) 59 | const AllItems = new Items(items.options) 60 | const AllMoves = new Moves() 61 | const AllQuests = new Quests() 62 | const AllInvasions = new Invasions(invasions.options) 63 | const AllTypes = new Types() 64 | const AllWeather = new Weather() 65 | const AllTranslations = new Translations( 66 | translations.options, 67 | translationApkUrl, 68 | translationRemoteUrl, 69 | ) 70 | const AllPokeApi = new PokeApi() 71 | await AllPokeApi.setMaxPokemonId() 72 | const generations = await AllPokeApi.getGenerations() 73 | AllPokemon.generations = generations 74 | const AllMisc = new Misc() 75 | const apk = new ApkReader() 76 | 77 | AllMisc.parseRaidLevels() 78 | AllMisc.parseRouteTypes() 79 | AllMisc.parseTeams() 80 | 81 | await apk.fetchApk() 82 | await apk.extractTexts() 83 | apk.cleanup() 84 | AllTranslations.fromApk = apk.texts 85 | 86 | const data: NiaMfObj[] = await AllPokemon.fetch(urlToFetch) 87 | 88 | // add data from old gm (this setting is removed from latest) 89 | AllPokemon.addSmeargleMovesSettings({ 90 | "templateId": "SMEARGLE_MOVES_SETTINGS", 91 | "data": { 92 | "templateId": "SMEARGLE_MOVES_SETTINGS", 93 | "smeargleMovesSettings": { 94 | "quickMoves": [ 95 | "TACKLE_FAST", 96 | "FURY_CUTTER_FAST", 97 | "BUG_BITE_FAST", 98 | "BITE_FAST", 99 | "SUCKER_PUNCH_FAST", 100 | "DRAGON_BREATH_FAST", 101 | "THUNDER_SHOCK_FAST", 102 | "SPARK_FAST", 103 | "LOW_KICK_FAST", 104 | "KARATE_CHOP_FAST", 105 | "EMBER_FAST", 106 | "WING_ATTACK_FAST", 107 | "PECK_FAST", 108 | "LICK_FAST", 109 | "SHADOW_CLAW_FAST", 110 | "VINE_WHIP_FAST", 111 | "RAZOR_LEAF_FAST", 112 | "MUD_SHOT_FAST", 113 | "ICE_SHARD_FAST", 114 | "FROST_BREATH_FAST", 115 | "QUICK_ATTACK_FAST", 116 | "SCRATCH_FAST", 117 | "POUND_FAST", 118 | "CUT_FAST", 119 | "POISON_JAB_FAST", 120 | "ACID_FAST", 121 | "PSYCHO_CUT_FAST", 122 | "ROCK_THROW_FAST", 123 | "METAL_CLAW_FAST", 124 | "BULLET_PUNCH_FAST", 125 | "WATER_GUN_FAST", 126 | "SPLASH_FAST", 127 | "MUD_SLAP_FAST", 128 | "ZEN_HEADBUTT_FAST", 129 | "CONFUSION_FAST", 130 | "POISON_STING_FAST", 131 | "BUBBLE_FAST", 132 | "FEINT_ATTACK_FAST", 133 | "STEEL_WING_FAST", 134 | "FIRE_FANG_FAST", 135 | "ROCK_SMASH_FAST", 136 | "COUNTER_FAST", 137 | "POWDER_SNOW_FAST", 138 | "CHARGE_BEAM_FAST", 139 | "VOLT_SWITCH_FAST", 140 | "DRAGON_TAIL_FAST", 141 | "AIR_SLASH_FAST", 142 | "INFESTATION_FAST", 143 | "STRUGGLE_BUG_FAST", 144 | "ASTONISH_FAST", 145 | "HEX_FAST", 146 | "IRON_TAIL_FAST", 147 | "FIRE_SPIN_FAST", 148 | "BULLET_SEED_FAST", 149 | "EXTRASENSORY_FAST", 150 | "SNARL_FAST", 151 | "HIDDEN_POWER_FAST", 152 | "TAKE_DOWN_FAST", 153 | "WATERFALL_FAST", 154 | "YAWN_FAST", 155 | "PRESENT_FAST", 156 | "SMACK_DOWN_FAST", 157 | "CHARM_FAST", 158 | "LOCK_ON_FAST", 159 | "THUNDER_FANG_FAST", 160 | "ICE_FANG_FAST", 161 | "GUST_FAST", 162 | "INCINERATE_FAST" 163 | ], 164 | "cinematicMoves": [ 165 | "STRUGGLE", 166 | "WRAP", 167 | "HYPER_BEAM", 168 | "DARK_PULSE", 169 | "SLUDGE", 170 | "VICE_GRIP", 171 | "FLAME_WHEEL", 172 | "MEGAHORN", 173 | "FLAMETHROWER", 174 | "DIG", 175 | "CROSS_CHOP", 176 | "PSYBEAM", 177 | "EARTHQUAKE", 178 | "STONE_EDGE", 179 | "ICE_PUNCH", 180 | "DISCHARGE", 181 | "FLASH_CANNON", 182 | "DRILL_PECK", 183 | "ICE_BEAM", 184 | "BLIZZARD", 185 | "HEAT_WAVE", 186 | "AERIAL_ACE", 187 | "DRILL_RUN", 188 | "PETAL_BLIZZARD", 189 | "BUG_BUZZ", 190 | "POISON_FANG", 191 | "NIGHT_SLASH", 192 | "BUBBLE_BEAM", 193 | "SUBMISSION", 194 | "LOW_SWEEP", 195 | "AQUA_JET", 196 | "AQUA_TAIL", 197 | "SEED_BOMB", 198 | "PSYSHOCK", 199 | "ANCIENT_POWER", 200 | "ROCK_TOMB", 201 | "ROCK_SLIDE", 202 | "POWER_GEM", 203 | "SHADOW_SNEAK", 204 | "SHADOW_PUNCH", 205 | "OMINOUS_WIND", 206 | "SHADOW_BALL", 207 | "MAGNET_BOMB", 208 | "IRON_HEAD", 209 | "THUNDER_PUNCH", 210 | "THUNDER", 211 | "THUNDERBOLT", 212 | "TWISTER", 213 | "DRAGON_PULSE", 214 | "DRAGON_CLAW", 215 | "DISARMING_VOICE", 216 | "DRAINING_KISS", 217 | "DAZZLING_GLEAM", 218 | "MOONBLAST", 219 | "PLAY_ROUGH", 220 | "CROSS_POISON", 221 | "SLUDGE_BOMB", 222 | "SLUDGE_WAVE", 223 | "GUNK_SHOT", 224 | "BONE_CLUB", 225 | "BULLDOZE", 226 | "MUD_BOMB", 227 | "SIGNAL_BEAM", 228 | "X_SCISSOR", 229 | "FLAME_CHARGE", 230 | "FLAME_BURST", 231 | "FIRE_BLAST", 232 | "WATER_PULSE", 233 | "HYDRO_PUMP", 234 | "PSYCHIC", 235 | "ICY_WIND", 236 | "FIRE_PUNCH", 237 | "SOLAR_BEAM", 238 | "LEAF_BLADE", 239 | "POWER_WHIP", 240 | "AIR_CUTTER", 241 | "HURRICANE", 242 | "BRICK_BREAK", 243 | "SWIFT", 244 | "HORN_ATTACK", 245 | "STOMP", 246 | "HYPER_FANG", 247 | "BODY_SLAM", 248 | "CLOSE_COMBAT", 249 | "DYNAMIC_PUNCH", 250 | "FOCUS_BLAST", 251 | "AURORA_BEAM", 252 | "WILD_CHARGE", 253 | "ZAP_CANNON", 254 | "AVALANCHE", 255 | "BRAVE_BIRD", 256 | "SKY_ATTACK", 257 | "SAND_TOMB", 258 | "ROCK_BLAST", 259 | "SILVER_WIND", 260 | "NIGHT_SHADE", 261 | "GYRO_BALL", 262 | "HEAVY_SLAM", 263 | "OVERHEAT", 264 | "GRASS_KNOT", 265 | "ENERGY_BALL", 266 | "FUTURESIGHT", 267 | "MIRROR_COAT", 268 | "OUTRAGE", 269 | "CRUNCH", 270 | "FOUL_PLAY", 271 | "SURF", 272 | "DRACO_METEOR", 273 | "PSYCHO_BOOST", 274 | "FRENZY_PLANT", 275 | "BLAST_BURN", 276 | "HYDRO_CANNON", 277 | "LAST_RESORT", 278 | "METEOR_MASH", 279 | "BRINE", 280 | "SCALD", 281 | "PSYSTRIKE", 282 | "DOOM_DESIRE", 283 | "WEATHER_BALL_FIRE", 284 | "WEATHER_BALL_ICE", 285 | "WEATHER_BALL_WATER", 286 | "SKULL_BASH", 287 | "ACID_SPRAY", 288 | "EARTH_POWER", 289 | "CRABHAMMER", 290 | "LUNGE", 291 | "OCTAZOOKA", 292 | "MIRROR_SHOT", 293 | "SUPER_POWER", 294 | "FELL_STINGER", 295 | "LEAF_TORNADO", 296 | "SHADOW_BONE", 297 | "MUDDY_WATER", 298 | "BLAZE_KICK", 299 | "POWER_UP_PUNCH", 300 | "GIGA_IMPACT", 301 | "SYNCHRONOISE", 302 | "SACRED_SWORD", 303 | "FLYING_PRESS", 304 | "AURA_SPHERE", 305 | "PAYBACK", 306 | "ROCK_WRECKER", 307 | "AEROBLAST", 308 | "TECHNO_BLAST_NORMAL", 309 | "TECHNO_BLAST_BURN", 310 | "TECHNO_BLAST_CHILL", 311 | "TECHNO_BLAST_WATER", 312 | "TECHNO_BLAST_SHOCK", 313 | "FLY", 314 | "V_CREATE", 315 | "TRI_ATTACK" 316 | ] 317 | } 318 | } 319 | }) 320 | 321 | for (let i = 0; i < data.length; i += 1) { 322 | if (data[i]) { 323 | if (data[i].data.formSettings) { 324 | AllPokemon.addForm(data[i]) 325 | } else if (data[i].data.pokemonSettings) { 326 | AllPokemon.addPokemon(data[i]) 327 | } else if (data[i].data.sourdoughMoveMappingSettings) { 328 | AllPokemon.addSourdoughMoveMappings(data[i]) 329 | } else if (data[i].data.smeargleMovesSettings) { 330 | AllPokemon.addSmeargleMovesSettings(data[i]) 331 | } else if (data[i].data.itemSettings) { 332 | AllItems.addItem(data[i]) 333 | } else if (data[i].data.moveSettings) { 334 | AllMoves.addMoveSettings(data[i]) 335 | } else if (data[i].data.combatMove) { 336 | AllMoves.addCombatMove(data[i]) 337 | } else if ( 338 | data[i].templateId === 'COMBAT_LEAGUE_VS_SEEKER_GREAT_LITTLE' 339 | ) { 340 | AllPokemon.lcBanList = new Set(data[i].data.combatLeague.bannedPokemon) 341 | } else if (data[i].data.weatherAffinities) { 342 | AllWeather.addWeather(data[i]) 343 | } else if (data[i].data.evolutionQuestTemplate) { 344 | AllPokemon.addEvolutionQuest(data[i]) 345 | } else if ( 346 | data[i].templateId === 'COMBAT_LEAGUE_VS_SEEKER_LITTLE_JUNGLE' 347 | ) { 348 | AllPokemon.jungleCup(data[i]) 349 | } else if (data[i].data.pokemonExtendedSettings) { 350 | AllPokemon.addExtendedStats(data[i]) 351 | } 352 | } 353 | } 354 | 355 | AllTypes.buildTypes() 356 | AllPokemon.cleanExtendedStats() 357 | AllPokemon.missingPokemon() 358 | AllPokemon.parseCostumes() 359 | if (pokemon.options.includeProtos || translations.options.includeProtos) { 360 | AllPokemon.generateProtoForms() 361 | } 362 | AllPokemon.sortForms() 363 | 364 | if (pokeApi === true) { 365 | AllPokeApi.moves = AllMoves.parsedMoves 366 | await AllPokeApi.baseStatsApi( 367 | AllPokemon.parsedPokemon, 368 | pokemon.options.pokeApiIds, 369 | ) 370 | await AllPokeApi.extraPokemon(AllPokemon.parsedPokemon) 371 | await AllPokeApi.evoApi(AllPokemon.evolvedPokemon, AllPokemon.parsedPokemon) 372 | await AllPokeApi.tempEvoApi(AllPokemon.parsedPokemon) 373 | await AllPokeApi.typesApi() 374 | } 375 | 376 | const getDataSource = async ( 377 | category: 'baseStats' | 'tempEvos' | 'types', 378 | ) => { 379 | if (pokeApi === true) return AllPokeApi[category] 380 | if (pokeApi) return pokeApi[category] 381 | return AllPokeApi.fetch( 382 | `https://raw.githubusercontent.com/WatWowMap/Pogo-Data-Generator/main/static/${category}.json`, 383 | ) 384 | } 385 | 386 | AllTypes.parsePokeApi(await getDataSource('types')) 387 | 388 | if (pokemon.options.includeEstimatedPokemon) { 389 | AllPokemon.parsePokeApi( 390 | await getDataSource('baseStats'), 391 | await getDataSource('tempEvos'), 392 | ) 393 | } 394 | 395 | if ((pokemon.template as PokemonTemplate).little) { 396 | AllPokemon.littleCup() 397 | } 398 | if ((pokemon.template as PokemonTemplate).jungle) { 399 | AllPokemon.jungleEligibility() 400 | } 401 | if (pokemon.options.processFormsSeparately) { 402 | AllPokemon.makeFormsSeparate() 403 | } 404 | AllQuests.addQuest('types') 405 | AllQuests.addQuest('rewards') 406 | AllQuests.addQuest('conditions') 407 | if (moves.options.includeProtos) { 408 | AllMoves.protoMoves() 409 | } 410 | AllWeather.buildWeather() 411 | if ( 412 | invasions.enabled || 413 | (translations.template as TranslationsTemplate).characters 414 | ) { 415 | const invasionData: InvasionInfo = await AllInvasions.fetch( 416 | 'https://raw.githubusercontent.com/WatWowMap/event-info/main/grunts/classic.json', 417 | ) 418 | AllInvasions.invasions( 419 | AllInvasions.mergeInvasions( 420 | invasionData, 421 | await AllInvasions.customInvasions(), 422 | ), 423 | ) 424 | } 425 | 426 | if (translations.enabled) { 427 | const availableManualTranslations = await AllTranslations.fetch( 428 | 'https://raw.githubusercontent.com/WatWowMap/pogo-translations/master/index.json', 429 | ) 430 | await Promise.all( 431 | Object.entries(translations.locales).map(async (langCode) => { 432 | const [localeCode, bool] = langCode 433 | if (bool) { 434 | await AllTranslations.fetchTranslations( 435 | localeCode as Locales[number], 436 | availableManualTranslations, 437 | ) 438 | 439 | if (translations.template.misc) { 440 | AllTranslations.misc(localeCode) 441 | } 442 | if (translations.template.types) { 443 | AllTranslations.types(localeCode) 444 | } 445 | if (translations.template.pokemon) { 446 | AllTranslations.pokemon( 447 | localeCode, 448 | translations.template.pokemon, 449 | AllPokemon.parsedPokemon, 450 | AllPokemon.parsedForms, 451 | pokemon.options.unsetFormName, 452 | ) 453 | } 454 | if (translations.template.moves) { 455 | AllTranslations.moves(localeCode) 456 | } 457 | if (translations.template.items) { 458 | AllTranslations.items(localeCode) 459 | } 460 | if (translations.template.characters) { 461 | AllTranslations.characters(localeCode, AllInvasions.parsedInvasions) 462 | } 463 | if (translations.template.weather) { 464 | AllTranslations.weather(localeCode) 465 | } 466 | if (translations.template.pokemonCategories) { 467 | AllTranslations.pokemonCategories(localeCode) 468 | } 469 | if (translations.template.quests) { 470 | AllTranslations.quests(localeCode, { 471 | questTypes: AllQuests.parsedQuestTypes, 472 | questConditions: AllQuests.parsedConditions, 473 | questRewardTypes: AllQuests.parsedRewardTypes, 474 | }) 475 | AllTranslations.parseEvoQuests( 476 | localeCode, 477 | AllPokemon.evolutionQuests, 478 | ) 479 | } 480 | } 481 | }), 482 | ) 483 | Object.entries(translations.locales).forEach((langCode) => { 484 | const [localeCode, bool] = langCode 485 | if (bool) { 486 | AllTranslations.mergeManualTranslations(localeCode) 487 | if (typeof translations.options.useLanguageAsRef === 'string') { 488 | AllTranslations.languageRef(localeCode) 489 | } 490 | if (translations.options.mergeCategories) { 491 | AllTranslations.mergeCategories(localeCode) 492 | } 493 | } 494 | }) 495 | if (localeCheck) { 496 | AllTranslations.translateMasterfile( 497 | { 498 | pokemon: AllPokemon.parsedPokeForms || AllPokemon.parsedPokemon, 499 | evolutionQuests: AllPokemon.evolutionQuests, 500 | moves: AllMoves.parsedMoves, 501 | items: AllItems.parsedItems, 502 | forms: AllPokemon.parsedForms, 503 | types: AllTypes.parsedTypes, 504 | weather: AllWeather.parsedWeather, 505 | }, 506 | translations.options.masterfileLocale as string, 507 | pokemon.options.processFormsSeparately, 508 | ) 509 | } 510 | } 511 | const localPokemon = localeCheck 512 | ? AllTranslations.masterfile.pokemon 513 | : AllPokemon.parsedPokeForms || AllPokemon.parsedPokemon 514 | const localTypes = localeCheck 515 | ? AllTranslations.masterfile.types 516 | : AllTypes.parsedTypes 517 | const localMoves = localeCheck 518 | ? AllTranslations.masterfile.moves 519 | : AllMoves.parsedMoves 520 | const localForms = localeCheck 521 | ? AllTranslations.masterfile.forms 522 | : AllPokemon.parsedForms 523 | const localItems = localeCheck 524 | ? AllTranslations.masterfile.items 525 | : AllItems.parsedItems 526 | const localWeather = localeCheck 527 | ? AllTranslations.masterfile.weather 528 | : AllWeather.parsedWeather 529 | const localEvolutionQuests = localeCheck 530 | ? AllTranslations.masterfile.evolutionQuests 531 | : AllPokemon.evolutionQuests 532 | 533 | if (pokemon.enabled) { 534 | final[pokemon.options.topLevelName || 'pokemon'] = raw 535 | ? localPokemon 536 | : AllPokemon.templater(localPokemon, pokemon, { 537 | quickMoves: localMoves, 538 | chargedMoves: localMoves, 539 | eliteQuickMoves: localMoves, 540 | eliteChargedMoves: localMoves, 541 | types: localTypes, 542 | forms: localForms, 543 | itemRequirement: localItems, 544 | questRequirement: localEvolutionQuests, 545 | // TODO gmaxMove 546 | }) 547 | if (pokemon.options.includeRawForms || raw) { 548 | final.forms = localForms 549 | } 550 | } 551 | if (types.enabled) { 552 | final[types.options.topLevelName || 'types'] = raw 553 | ? localTypes 554 | : AllTypes.templater(localTypes, types, { 555 | strengths: localTypes, 556 | weaknesses: localTypes, 557 | veryWeakAgainst: localTypes, 558 | immunes: localTypes, 559 | weakAgainst: localTypes, 560 | resistances: localTypes, 561 | }) 562 | } 563 | if (costumes.enabled) { 564 | final[costumes.options.topLevelName || 'costumes'] = raw 565 | ? AllPokemon.parsedCostumes 566 | : AllPokemon.templater(AllPokemon.parsedCostumes, costumes) 567 | } 568 | if (items.enabled) { 569 | final[items.options.topLevelName || 'items'] = raw 570 | ? localItems 571 | : AllItems.templater(localItems, items) 572 | } 573 | if (moves.enabled) { 574 | final[moves.options.topLevelName || 'moves'] = raw 575 | ? localMoves 576 | : AllMoves.templater(localMoves, moves, { 577 | type: localTypes, 578 | }) 579 | } 580 | if (questTypes.enabled) { 581 | final[questTypes.options.topLevelName || 'questTypes'] = raw 582 | ? AllQuests.parsedQuestTypes 583 | : AllQuests.templater(AllQuests.parsedQuestTypes, questTypes) 584 | } 585 | if (questRewardTypes.enabled) { 586 | final[questRewardTypes.options.topLevelName || 'questRewardTypes'] = raw 587 | ? AllQuests.parsedRewardTypes 588 | : AllQuests.templater(AllQuests.parsedRewardTypes, questRewardTypes) 589 | } 590 | if (questConditions.enabled) { 591 | final[questConditions.options.topLevelName || 'questConditions'] = raw 592 | ? AllQuests.parsedConditions 593 | : AllQuests.templater(AllQuests.parsedConditions, questConditions) 594 | } 595 | if (invasions.enabled) { 596 | final[invasions.options.topLevelName || 'invasions'] = raw 597 | ? AllInvasions.parsedInvasions 598 | : AllInvasions.templater(AllInvasions.parsedInvasions, invasions) 599 | } 600 | if (weather.enabled) { 601 | final[weather.options.topLevelName || 'weather'] = raw 602 | ? localWeather 603 | : AllWeather.templater(localWeather, weather, { types: localTypes }) 604 | } 605 | if (translations.enabled) { 606 | final[translations.options.topLevelName || 'translations'] = 607 | AllTranslations.parsedTranslations 608 | } 609 | if (raids.enabled) { 610 | final[raids.options.topLevelName || 'raids'] = raw 611 | ? AllMisc.raidLevels 612 | : AllMisc.templater(AllMisc.raidLevels, raids) 613 | } 614 | if (routeTypes.enabled) { 615 | final[routeTypes.options.topLevelName || 'routeTypes'] = raw 616 | ? AllMisc.routeTypes 617 | : AllMisc.templater(AllMisc.routeTypes, routeTypes) 618 | } 619 | if (teams.enabled) { 620 | final[teams.options.topLevelName || 'teams'] = raw 621 | ? AllMisc.teams 622 | : AllMisc.templater(AllMisc.teams, teams) 623 | } 624 | 625 | if (test && pokeApi === true) { 626 | final.AllPokeApi = AllPokeApi 627 | } 628 | return final 629 | } 630 | 631 | export async function invasions({ 632 | template, 633 | }: InvasionsOnly = {}): Promise { 634 | const finalTemplate = template || base.invasions 635 | const AllInvasions = new Invasions(finalTemplate.options) 636 | const invasionData: InvasionInfo = await AllInvasions.fetch( 637 | 'https://raw.githubusercontent.com/WatWowMap/event-info/main/grunts/classic.json', 638 | ) 639 | AllInvasions.invasions( 640 | AllInvasions.mergeInvasions( 641 | invasionData, 642 | await AllInvasions.customInvasions(true), 643 | ), 644 | ) 645 | return AllInvasions.templater(AllInvasions.parsedInvasions, finalTemplate) 646 | } 647 | -------------------------------------------------------------------------------- /src/typings/dataTypes.ts: -------------------------------------------------------------------------------- 1 | import PokeApi from '../classes/PokeApi' 2 | 3 | export interface AllWeather { 4 | [id: string]: { 5 | weatherId: number 6 | weatherName: string 7 | proto: string 8 | types: number[] 9 | } 10 | } 11 | 12 | export interface AllTypes { 13 | [id: string]: { 14 | typeId?: number 15 | typeName?: string 16 | strengths?: number[] 17 | weaknesses?: number[] 18 | veryWeakAgainst?: number[] 19 | immunes?: number[] 20 | weakAgainst?: number[] 21 | resistances?: number[] 22 | } 23 | } 24 | 25 | export interface AllInvasions { 26 | [id: string]: SingleInvasion 27 | } 28 | 29 | type SingleInvasion = { 30 | id: number 31 | type: string 32 | gender: number | string 33 | grunt: string 34 | firstReward: boolean 35 | secondReward: boolean 36 | thirdReward: boolean 37 | encounters?: InvasionTeam[] 38 | active: boolean 39 | proto: string 40 | } 41 | 42 | type InvasionTeam = { 43 | id?: number 44 | formId?: number 45 | position?: string 46 | } 47 | 48 | export interface AllMoves { 49 | [id: string]: SingleMove 50 | } 51 | 52 | export interface SingleMove { 53 | moveId: number 54 | moveName: string 55 | proto?: string 56 | type?: number 57 | power?: number 58 | fast?: boolean 59 | durationMs?: number 60 | energyDelta?: number 61 | pvpPower?: number 62 | pvpDurationTurns?: number 63 | pvpEnergyDelta?: number 64 | pvpBuffs?: { 65 | attackerAttackStatStageChange?: number 66 | attackerDefenseStatStageChange?: number 67 | targetAttackStatStageChange?: number 68 | targetDefenseStatStageChange?: number 69 | buffActivationChance: number 70 | }[] 71 | } 72 | 73 | export interface AllItems { 74 | [id: string]: { 75 | itemId: number 76 | itemName: string 77 | proto: string 78 | type: string 79 | category: string 80 | minTrainerLevel: number 81 | } 82 | } 83 | 84 | export interface MiscProto { 85 | id: number 86 | proto: string 87 | formatted: string 88 | } 89 | 90 | export interface AllQuests { 91 | [id: string]: QuestSubCategory 92 | } 93 | 94 | type QuestSubCategory = { 95 | questId: number 96 | proto: string 97 | formatted: string 98 | } 99 | 100 | export interface AllPokemon { 101 | [id: string]: SinglePokemon 102 | } 103 | 104 | export interface AllForms { 105 | [id: string]: SingleForm 106 | } 107 | 108 | export interface SinglePokemon extends SingleForm { 109 | pokedexId?: number 110 | pokemonName?: string 111 | forms?: number[] 112 | defaultFormId?: number 113 | genId?: number 114 | generation?: string 115 | fleeRate?: number 116 | captureRate?: number 117 | legendary?: boolean 118 | mythic?: boolean 119 | ultraBeast?: boolean 120 | buddyGroupNumber?: number 121 | buddyDistance?: number 122 | buddyMegaEnergy?: number 123 | thirdMoveStardust?: number 124 | thirdMoveCandy?: number 125 | gymDefenderEligible?: boolean 126 | unreleased?: boolean 127 | jungle?: boolean 128 | } 129 | 130 | interface SingleForm extends BaseStats { 131 | formName?: string 132 | proto?: string 133 | formId?: number 134 | isCostume?: boolean 135 | evolutions?: Evolutions[] 136 | tempEvolutions?: TempEvolutions[] 137 | quickMoves?: number[] 138 | chargedMoves?: number[] 139 | eliteQuickMoves?: number[] 140 | eliteChargedMoves?: number[] 141 | family?: number 142 | little?: boolean 143 | purificationDust?: number 144 | purificationCandy?: number 145 | bonusCandyCapture?: number 146 | bonusStardustCapture?: number 147 | tradable?: boolean 148 | transferable?: boolean 149 | costumeOverrideEvos?: { 150 | costumeId: number 151 | costumeProto: string 152 | costumeName: string 153 | }[] 154 | sizeSettings?: { name: string; value: number }[] 155 | gmaxMove?: number 156 | } 157 | 158 | export interface TempEvolutions extends BaseStats { 159 | tempEvoId: number | string 160 | unreleased?: boolean 161 | firstEnergyCost?: number 162 | subsequentEnergyCost?: number 163 | } 164 | 165 | export interface Evolutions { 166 | evoId?: number 167 | formId?: number 168 | genderRequirement?: number | string 169 | candyCost?: number 170 | itemRequirement?: number 171 | tradeBonus?: boolean 172 | mustBeBuddy?: boolean 173 | onlyDaytime?: boolean 174 | onlyNighttime?: boolean 175 | questRequirement?: string 176 | } 177 | 178 | type BaseStats = { 179 | attack?: number 180 | defense?: number 181 | stamina?: number 182 | height?: number 183 | weight?: number 184 | types?: number[] 185 | } 186 | 187 | export interface TranslationKeys { 188 | [category: string]: { [key: string]: string } 189 | } 190 | 191 | export interface FinalResult { 192 | [category: string]: any 193 | pokemon?: AllPokemon 194 | forms?: AllForms 195 | items?: AllItems 196 | moves?: AllMoves 197 | types?: AllTypes 198 | weather?: AllWeather 199 | questRewardTypes?: AllQuests 200 | questConditions?: AllQuests 201 | invasions?: AllInvasions 202 | translations?: { [locale: string]: TranslationKeys } 203 | AllPokeApi?: PokeApi 204 | } 205 | -------------------------------------------------------------------------------- /src/typings/general.ts: -------------------------------------------------------------------------------- 1 | import type { Rpc } from '@na-ji/pogo-protos' 2 | 3 | export interface GuessedMega { 4 | attack?: number 5 | defense?: number 6 | stamina?: number 7 | tempEvoId?: number 8 | type1?: string 9 | type2?: string 10 | } 11 | 12 | export interface Generation { 13 | [id: string]: { 14 | name: string 15 | range: number[] 16 | } 17 | } 18 | 19 | export interface EvolutionQuest { 20 | questType?: number 21 | target?: number 22 | assetsRef?: string 23 | i18n?: string 24 | translated?: string 25 | } 26 | 27 | export interface NiaMfObj { 28 | templateId: string 29 | data: { 30 | templateId: string 31 | pokemonSettings?: { 32 | pokemonId: string 33 | modelScale: number 34 | type: string 35 | type2: string 36 | encounter: { 37 | baseCaptureRate: number 38 | baseFleeRate: number 39 | bonusCandyCaptureReward: number 40 | bonusStardustCaptureReward: number 41 | } 42 | stats: { 43 | baseStamina: number 44 | baseAttack: number 45 | baseDefense: number 46 | } 47 | quickMoves: string[] 48 | cinematicMoves: string[] 49 | eliteQuickMove: string[] 50 | eliteCinematicMove: string[] 51 | evolutionIds: string[] 52 | evolutionPips: number 53 | pokedexHeightM: number 54 | pokedexWeightKg: number 55 | familyId: string 56 | candyToEvolve: number 57 | kmBuddyDistance: number 58 | evolutionBranch: EvoBranch[] 59 | tempEvoOverrides: TempEvo[] 60 | thirdMove: { 61 | stardustToUnlock: number 62 | candyToUnlock: number 63 | } 64 | isTransferable: boolean 65 | isDeployable: boolean 66 | isTradable: boolean 67 | buddyGroupNumber: number 68 | buddyWalkedMegaEnergyAward: number 69 | rarity: string 70 | pokemonClass: keyof typeof Rpc.HoloPokemonClass 71 | shadow: { 72 | purificationStardustNeeded: number 73 | purificationCandyNeeded: number 74 | } 75 | allowNoevolveEvolution: string[] 76 | } 77 | formSettings?: { 78 | pokemon: string 79 | forms: { 80 | form?: string | number 81 | isCostume: boolean 82 | }[] 83 | } 84 | moveSettings?: { 85 | movementId: string 86 | pokemonType: string 87 | power?: number 88 | durationMs: number 89 | energyDelta?: number 90 | vfxName: string 91 | obMoveSettingsNumber18: number[] 92 | } 93 | combatMove?: { 94 | uniqueId: string | number 95 | type: string 96 | power: number 97 | durationTurns?: number 98 | energyDelta: number 99 | buffs?: { 100 | attackerAttackStatStageChange?: number 101 | attackerDefenseStatStageChange?: number 102 | targetAttackStatStageChange?: number 103 | targetDefenseStatStageChange?: number 104 | buffActivationChance: number 105 | }[] 106 | } 107 | sourdoughMoveMappingSettings?: { 108 | mappings: { 109 | pokemonId: string 110 | form?: string 111 | move: string 112 | }[] 113 | } 114 | smeargleMovesSettings?: { 115 | quickMoves: string[] 116 | cinematicMoves: string[] 117 | } 118 | itemSettings?: { 119 | itemId: string | number 120 | itemType: string | number 121 | category: string 122 | dropTrainerLevel: number 123 | } 124 | combatLeague?: { 125 | bannedPokemon: string[] 126 | pokemonCondition: { 127 | type: string 128 | withPokemonCpLimit: { 129 | maxCp: number 130 | } 131 | withPokemonType: { 132 | pokemonType: string[] 133 | } 134 | }[] 135 | } 136 | weatherAffinities?: { 137 | weatherCondition: string 138 | pokemonType: string[] 139 | } 140 | evolutionQuestTemplate?: { 141 | questTemplateId: string 142 | questType: string 143 | goals: { 144 | condition: { 145 | type: string 146 | withThrowType: { 147 | throwType: string 148 | } 149 | withPokemonType: { 150 | pokemonType: string[] 151 | } 152 | }[] 153 | target: number 154 | }[] 155 | context: string 156 | display: { 157 | description: string 158 | title: string 159 | } 160 | } 161 | pokemonExtendedSettings?: { 162 | uniqueId: string 163 | form?: string 164 | sizeSettings: PokemonSizeSettings 165 | } 166 | } 167 | } 168 | 169 | export interface PokemonSizeSettings { 170 | xxsLowerBound: number 171 | xsLowerBound: number 172 | mLowerBound: number 173 | mUpperBound: number 174 | xlUpperBound: number 175 | xxlUpperBound: number 176 | } 177 | 178 | export interface TempEvo { 179 | tempEvoId: string 180 | stats: { 181 | baseStamina: number 182 | baseAttack: number 183 | baseDefense: number 184 | } 185 | averageHeightM: number 186 | averageWeightKg: number 187 | typeOverride1: string 188 | typeOverride2?: string 189 | } 190 | 191 | export interface EvoBranch { 192 | evolution: string 193 | candyCost: number 194 | form: string 195 | genderRequirement: string 196 | evolutionItemRequirement: string 197 | temporaryEvolution: string 198 | temporaryEvolutionEnergyCost: number 199 | temporaryEvolutionEnergyCostSubsequent: number 200 | noCandyCostViaTrade: boolean 201 | buddyDistance: boolean 202 | mustBeBuddy: boolean 203 | onlyDaytime: boolean 204 | onlyNighttime: boolean 205 | questDisplay: { 206 | questRequirementTemplateId: string 207 | }[] 208 | } 209 | 210 | export interface SpeciesApi { 211 | evolves_from_species: { 212 | name: string 213 | url: string 214 | } 215 | is_legendary: boolean 216 | is_mythical: boolean 217 | } 218 | -------------------------------------------------------------------------------- /src/typings/inputs.ts: -------------------------------------------------------------------------------- 1 | import { MiscProto } from './dataTypes' 2 | import { InvasionInfo } from './pogoinfo' 3 | import { PokeApi } from './pokeapi' 4 | 5 | type StringBool = string | boolean 6 | 7 | export interface Options { 8 | topLevelName?: string 9 | keys?: { 10 | [key: string]: StringBool 11 | } 12 | customFields?: { 13 | [key: string]: string 14 | } 15 | customChildObj?: { 16 | [key: string]: string 17 | } 18 | makeSingular?: { 19 | [key: string]: boolean 20 | } 21 | prefix?: { 22 | [key: string]: string 23 | } 24 | questVariables?: { 25 | prefix?: string 26 | suffix?: string 27 | } 28 | keyJoiner?: string 29 | genderString?: boolean 30 | snake_case?: boolean 31 | unsetDefaultForm?: boolean 32 | skipNormalIfUnset?: boolean 33 | skipForms?: string[] 34 | includeProtos?: boolean 35 | includeEstimatedPokemon?: 36 | | { 37 | [key: string]: boolean 38 | baseStats?: boolean 39 | } 40 | | true 41 | minTrainerLevel?: number 42 | placeholderData?: boolean 43 | masterfileLocale?: StringBool 44 | manualTranslations?: boolean 45 | mergeCategories?: boolean 46 | processFormsSeparately?: boolean 47 | includeRawForms?: boolean 48 | includeBalloons?: boolean 49 | useLanguageAsRef?: StringBool 50 | includeUnset?: boolean 51 | unsetFormName?: string 52 | allUnset?: boolean 53 | pokeApiIds?: number[] 54 | noFormPlaceholders?: boolean 55 | customInvasions?: InvasionInfo | boolean 56 | questTitleTermsToSkip?: string[] 57 | } 58 | 59 | export interface PokemonTemplate extends Form { 60 | pokedexId?: boolean 61 | pokemonName?: boolean 62 | forms?: Form | string 63 | defaultFormId?: boolean 64 | genId?: boolean 65 | generation?: boolean 66 | fleeRate?: boolean 67 | captureRate?: boolean 68 | legendary?: boolean 69 | mythic?: boolean 70 | ultraBeast?: boolean 71 | buddyGroupNumber?: boolean 72 | buddyDistance?: boolean 73 | buddyMegaEnergy?: boolean 74 | thirdMoveStardust?: boolean 75 | thirdMoveCandy?: boolean 76 | gymDefenderEligible?: boolean 77 | unreleased?: boolean 78 | jungle?: boolean 79 | } 80 | 81 | interface CostumeTemplate { 82 | id?: boolean 83 | name?: boolean 84 | proto?: boolean 85 | noEvolve?: boolean 86 | } 87 | 88 | interface Form extends BaseStats { 89 | formName?: boolean 90 | proto?: boolean 91 | formId?: boolean 92 | isCostume?: boolean 93 | evolutions?: 94 | | { 95 | evoId?: boolean 96 | formId?: boolean 97 | genderRequirement?: boolean 98 | candyCost?: boolean 99 | itemRequirement?: boolean | string 100 | tradeBonus?: boolean 101 | mustBeBuddy?: boolean 102 | onlyDaytime?: boolean 103 | onlyNighttime?: boolean 104 | questRequirement?: 105 | | { 106 | target?: boolean 107 | assetsRef?: boolean 108 | i18n?: boolean 109 | questType?: boolean 110 | translated?: boolean 111 | } 112 | | StringBool 113 | } 114 | | StringBool 115 | tempEvolutions?: TempEvolution | StringBool 116 | quickMoves?: Move | StringBool 117 | chargedMoves?: Move | StringBool 118 | eliteQuickMoves?: Move | StringBool 119 | eliteChargedMoves?: Move | StringBool 120 | family?: boolean 121 | little?: boolean 122 | purificationCandy?: boolean 123 | purificationDust?: boolean 124 | bonusCandyCapture?: boolean 125 | bonusStardustCapture?: boolean 126 | tradable?: boolean 127 | transferable?: boolean 128 | costumeOverrideEvos?: CostumeOverrideEvo | StringBool 129 | sizeSettings?: { name: boolean; value: boolean } | string 130 | gmaxMove?: Move | StringBool 131 | } 132 | 133 | type CostumeOverrideEvo = { 134 | costumeId?: boolean 135 | costumeProto?: boolean 136 | costumeName?: boolean 137 | } 138 | 139 | type Move = { 140 | moveId?: boolean 141 | moveName?: boolean 142 | proto?: boolean 143 | fast?: boolean 144 | type: 145 | | { 146 | typeId?: boolean 147 | type?: boolean 148 | } 149 | | StringBool 150 | } 151 | 152 | interface TempEvolution extends BaseStats { 153 | tempEvoId?: boolean 154 | unreleased?: boolean 155 | firstEnergyCost?: boolean 156 | subsequentEnergyCost?: boolean 157 | } 158 | 159 | type BaseStats = { 160 | attack?: boolean 161 | defense?: boolean 162 | stamina?: boolean 163 | height?: boolean 164 | weight?: boolean 165 | types?: 166 | | { 167 | typeId?: boolean 168 | typeName?: boolean 169 | } 170 | | StringBool 171 | } 172 | 173 | export interface TypesTempOpt { 174 | enabled?: boolean 175 | options?: Options 176 | template?: TypesTemplate 177 | } 178 | 179 | export interface TypesTemplate { 180 | typeId?: boolean 181 | typeName?: boolean 182 | } 183 | 184 | export interface MoveTemplate { 185 | moveId?: boolean 186 | moveName?: boolean 187 | proto?: boolean 188 | type?: 189 | | { 190 | typeId?: boolean 191 | typeName?: boolean 192 | } 193 | | StringBool 194 | power?: boolean 195 | durationMs?: boolean 196 | energyDelta?: boolean 197 | pvpPower?: boolean 198 | pvpDurationTurns?: boolean 199 | pvpEnergyDelta?: boolean 200 | pvpBuffs?: 201 | | { 202 | attackerAttackStatStageChange?: boolean 203 | attackerDefenseStatStageChange?: boolean 204 | targetAttackStatStageChange?: boolean 205 | targetDefenseStatStageChange?: boolean 206 | buffActivationChance: boolean 207 | } 208 | | StringBool 209 | } 210 | 211 | export interface ItemTemplate { 212 | itemId?: boolean 213 | itemName?: boolean 214 | proto?: boolean 215 | type?: boolean 216 | category?: boolean 217 | minTrainerLevel?: boolean 218 | } 219 | 220 | export interface QuestTemplate { 221 | id?: boolean 222 | proto?: boolean 223 | formatted?: boolean 224 | } 225 | 226 | export interface InvasionTemplate { 227 | id?: boolean 228 | type?: boolean 229 | gender?: boolean 230 | grunt?: boolean 231 | firstReward?: boolean 232 | secondReward?: boolean 233 | thirdReward?: boolean 234 | proto?: boolean 235 | encounters?: 236 | | { 237 | id?: boolean 238 | formId?: boolean 239 | position?: boolean 240 | } 241 | | StringBool 242 | active?: boolean 243 | } 244 | 245 | export interface WeatherTemplate { 246 | weatherId?: boolean 247 | weatherName?: boolean 248 | proto?: boolean 249 | types?: 250 | | { 251 | typeId?: boolean 252 | typeName?: boolean 253 | } 254 | | StringBool 255 | } 256 | 257 | export interface TranslationsTemplate { 258 | pokemon?: { 259 | names?: boolean 260 | forms?: boolean 261 | descriptions?: boolean 262 | } 263 | moves?: boolean 264 | items?: boolean 265 | types?: boolean 266 | characters?: boolean 267 | weather?: boolean 268 | misc?: boolean 269 | pokemonCategories?: boolean 270 | quests?: boolean 271 | } 272 | 273 | export interface Input { 274 | url?: string 275 | translationApkUrl?: string 276 | translationRemoteUrl?: string 277 | template?: FullTemplate 278 | test?: boolean 279 | raw?: boolean 280 | pokeApi?: boolean | PokeApi 281 | } 282 | 283 | export interface FullTemplate { 284 | globalOptions?: { 285 | keyJoiner?: string 286 | genderString?: boolean 287 | snake_case?: boolean 288 | customChildObj?: { [key: string]: string } 289 | customFields?: { [key: string]: string } 290 | includeProtos?: boolean 291 | } 292 | pokemon?: { 293 | enabled?: boolean 294 | options: Options 295 | template: PokemonTemplate | string 296 | } 297 | costumes?: { 298 | enabled?: boolean 299 | options: Options 300 | template: CostumeTemplate | string 301 | } 302 | types?: { 303 | enabled?: boolean 304 | options: Options 305 | template: TypesTemplate | string 306 | } 307 | moves?: { 308 | enabled?: boolean 309 | options: Options 310 | template: MoveTemplate | string 311 | } 312 | items?: { 313 | enabled?: boolean 314 | options: Options 315 | template: ItemTemplate | string 316 | } 317 | questTypes?: { 318 | enabled?: boolean 319 | options: Options 320 | template: QuestTemplate | string 321 | } 322 | questConditions?: { 323 | enabled?: boolean 324 | options: Options 325 | template: QuestTemplate | string 326 | } 327 | questRewardTypes?: { 328 | enabled?: boolean 329 | options: Options 330 | template: QuestTemplate | string 331 | } 332 | invasions?: { 333 | enabled?: boolean 334 | options: Options 335 | template: InvasionTemplate | string 336 | } 337 | weather?: { 338 | enabled?: boolean 339 | options: Options 340 | template: WeatherTemplate | string 341 | } 342 | translations?: { 343 | enabled?: boolean 344 | options: Options 345 | template: TranslationsTemplate 346 | locales: { [key in Locales[number]]?: boolean } 347 | } 348 | raids?: { 349 | enabled?: boolean 350 | options: Options 351 | template: MiscProto | keyof MiscProto 352 | } 353 | teams?: { 354 | enabled?: boolean 355 | options: Options 356 | template: MiscProto | keyof MiscProto 357 | } 358 | routeTypes?: { 359 | enabled?: boolean 360 | options: Options 361 | template: MiscProto | keyof MiscProto 362 | } 363 | } 364 | 365 | export type Locales = [ 366 | 'de', 367 | 'en', 368 | 'es', 369 | 'es-mx', 370 | 'fr', 371 | 'hi', 372 | 'id', 373 | 'it', 374 | 'ja', 375 | 'ko', 376 | 'pt-br', 377 | 'ru', 378 | 'th', 379 | 'tr', 380 | 'zh-tw', 381 | ] 382 | 383 | export interface InvasionsOnly { 384 | template?: { 385 | enabled: boolean 386 | options: Options 387 | template: InvasionTemplate 388 | } 389 | test?: boolean 390 | } 391 | -------------------------------------------------------------------------------- /src/typings/pogoinfo.ts: -------------------------------------------------------------------------------- 1 | export interface InvasionInfo { 2 | [id: string]: { 3 | active?: boolean 4 | character?: Character 5 | lineup?: { 6 | rewards: number[] 7 | team: InvasionTeam[][] 8 | } 9 | } 10 | } 11 | 12 | export interface Character { 13 | template: string 14 | gender: number 15 | boss: boolean 16 | type: { 17 | id?: number 18 | name?: string 19 | } 20 | } 21 | 22 | export interface InvasionTeam { 23 | id: number 24 | template: string 25 | form: number 26 | } 27 | -------------------------------------------------------------------------------- /src/typings/pokeapi.ts: -------------------------------------------------------------------------------- 1 | import { AllPokemon, AllTypes } from './dataTypes' 2 | 3 | export interface PokeApiStats { 4 | abilities: { 5 | ability: BasePokeApiStruct 6 | is_hidden: boolean 7 | slot: number 8 | }[] 9 | base_experience: number 10 | forms: BasePokeApiStruct[] 11 | game_indices: { 12 | game_index: number 13 | version: BasePokeApiStruct 14 | }[] 15 | height: number 16 | held_items: [] 17 | id: number 18 | is_default: boolean 19 | location_area_encounters: string 20 | moves: { 21 | move: BasePokeApiStruct 22 | version_group_details: { 23 | level_learned_at: number 24 | move_learn_method: BasePokeApiStruct 25 | version_group: BasePokeApiStruct 26 | }[] 27 | }[] 28 | name: string 29 | order: number 30 | past_types: [] 31 | species: BasePokeApiStruct 32 | sprites: Sprites 33 | stats: { 34 | base_stat: number 35 | effort: number 36 | stat: BasePokeApiStruct 37 | }[] 38 | types: { 39 | slot: number 40 | type: BasePokeApiStruct 41 | }[] 42 | weight: number 43 | } 44 | 45 | interface Sprites extends Sprite { 46 | other?: { 47 | dream_world?: { 48 | front_default: string 49 | front_female: string 50 | } 51 | 'official-artwork'?: { 52 | front_default: string 53 | } 54 | } 55 | versions: { 56 | [generation: string]: { 57 | [game: string]: Animated 58 | } 59 | } 60 | } 61 | 62 | interface Animated extends Sprite { 63 | animated: Sprite 64 | } 65 | 66 | type Sprite = { 67 | back_default?: string 68 | back_female?: string 69 | back_shiny?: string 70 | back_shiny_female?: string 71 | front_default: string 72 | front_female?: string 73 | front_shiny: string 74 | front_shiny_female?: string 75 | } 76 | 77 | export type BasePokeApiStruct = { 78 | name: string 79 | url: string 80 | } 81 | 82 | export interface PokeApiTypes { 83 | damage_relations: { 84 | double_damage_from: BasePokeApiStruct[] 85 | double_damage_to: BasePokeApiStruct[] 86 | half_damage_from: BasePokeApiStruct[] 87 | half_damage_to: BasePokeApiStruct[] 88 | no_damage_from: BasePokeApiStruct[] 89 | no_damage_to: BasePokeApiStruct[] 90 | } 91 | } 92 | 93 | export interface PokeApi { 94 | baseStats: AllPokemon 95 | tempEvos: { [id: string]: AllPokemon } 96 | types: AllTypes 97 | } 98 | -------------------------------------------------------------------------------- /src/typings/protos.ts: -------------------------------------------------------------------------------- 1 | import { Rpc } from '@na-ji/pogo-protos' 2 | 3 | export type WeatherProto = 4 | keyof typeof Rpc.GameplayWeatherProto.WeatherCondition 5 | 6 | export type CostumeProto = keyof typeof Rpc.PokemonDisplayProto.Costume 7 | 8 | export type TypeProto = keyof typeof Rpc.HoloPokemonType 9 | 10 | export type PokemonIdProto = keyof typeof Rpc.HoloPokemonId 11 | 12 | export type MoveProto = keyof typeof Rpc.HoloPokemonMove 13 | 14 | export type FormProto = keyof typeof Rpc.PokemonDisplayProto.Form 15 | 16 | export type GenderProto = keyof typeof Rpc.PokemonDisplayProto.Gender 17 | 18 | export type FamilyProto = keyof typeof Rpc.HoloPokemonFamilyId 19 | 20 | export type MegaProto = keyof typeof Rpc.HoloTemporaryEvolutionId 21 | 22 | export type ItemProto = keyof typeof Rpc.Item 23 | 24 | export type QuestTypeProto = keyof typeof Rpc.QuestType 25 | 26 | export type QuestRewardProto = keyof typeof Rpc.QuestRewardProto.Type 27 | 28 | export type QuestConditionProto = 29 | keyof typeof Rpc.QuestConditionProto.ConditionType 30 | 31 | export type ActivityProto = keyof typeof Rpc.HoloActivityType 32 | -------------------------------------------------------------------------------- /static/baseStats.json: -------------------------------------------------------------------------------- 1 | { 2 | "123": { 3 | "evolutions": [ 4 | { 5 | "evoId": 900, 6 | "formId": 0 7 | } 8 | ] 9 | }, 10 | "203": { 11 | "evolutions": [ 12 | { 13 | "evoId": 981, 14 | "formId": 0 15 | } 16 | ] 17 | }, 18 | "206": { 19 | "evolutions": [ 20 | { 21 | "evoId": 982, 22 | "formId": 2994 23 | } 24 | ] 25 | }, 26 | "222": { 27 | "evolutions": [ 28 | { 29 | "evoId": 864, 30 | "formId": 2507 31 | } 32 | ], 33 | "legendary": false, 34 | "mythic": false 35 | }, 36 | "234": { 37 | "evolutions": [ 38 | { 39 | "evoId": 899, 40 | "formId": 0 41 | } 42 | ] 43 | }, 44 | "290": { 45 | "evolutions": [ 46 | { 47 | "evoId": 292, 48 | "formId": 0 49 | } 50 | ], 51 | "legendary": false, 52 | "mythic": false 53 | }, 54 | "550": { 55 | "evolutions": [ 56 | { 57 | "evoId": 902, 58 | "formId": 2807 59 | } 60 | ] 61 | }, 62 | "679": { 63 | "pokemonName": "Honedge", 64 | "quickMoves": [ 65 | 200, 66 | 213, 67 | 221, 68 | 223, 69 | 226, 70 | 241, 71 | 281, 72 | 402 73 | ], 74 | "chargedMoves": [ 75 | 36, 76 | 45, 77 | 51, 78 | 64, 79 | 66, 80 | 74, 81 | 123, 82 | 132, 83 | 245, 84 | 267, 85 | 322, 86 | 323, 87 | 330, 88 | 367 89 | ], 90 | "attack": 135, 91 | "defense": 139, 92 | "stamina": 128, 93 | "types": [ 94 | 8, 95 | 9 96 | ], 97 | "unreleased": true, 98 | "legendary": false, 99 | "mythic": false 100 | }, 101 | "680": { 102 | "pokemonName": "Doublade", 103 | "quickMoves": [ 104 | 200, 105 | 213, 106 | 221, 107 | 223, 108 | 226, 109 | 241, 110 | 281, 111 | 402 112 | ], 113 | "chargedMoves": [ 114 | 36, 115 | 45, 116 | 51, 117 | 64, 118 | 66, 119 | 74, 120 | 123, 121 | 132, 122 | 245, 123 | 267, 124 | 322, 125 | 323, 126 | 330, 127 | 367 128 | ], 129 | "attack": 188, 130 | "defense": 206, 131 | "stamina": 153, 132 | "types": [ 133 | 8, 134 | 9 135 | ], 136 | "unreleased": true 137 | }, 138 | "681": { 139 | "pokemonName": "Aegislash-shield", 140 | "quickMoves": [ 141 | 200, 142 | 213, 143 | 221, 144 | 223, 145 | 226, 146 | 241, 147 | 255, 148 | 281, 149 | 402 150 | ], 151 | "chargedMoves": [ 152 | 14, 153 | 36, 154 | 45, 155 | 51, 156 | 64, 157 | 66, 158 | 70, 159 | 74, 160 | 123, 161 | 132, 162 | 245, 163 | 267, 164 | 321, 165 | 322, 166 | 323, 167 | 330, 168 | 367 169 | ], 170 | "attack": 97, 171 | "defense": 272, 172 | "stamina": 155, 173 | "types": [ 174 | 8, 175 | 9 176 | ], 177 | "unreleased": true 178 | }, 179 | "902": { 180 | "pokemonName": "Basculegion-male", 181 | "quickMoves": [ 182 | 202, 183 | 216, 184 | 221, 185 | 230, 186 | 264, 187 | 282, 188 | 283, 189 | 327 190 | ], 191 | "chargedMoves": [ 192 | 14, 193 | 39, 194 | 40, 195 | 57, 196 | 70, 197 | 105, 198 | 107, 199 | 111, 200 | 132, 201 | 265, 202 | 277, 203 | 279, 204 | 284, 205 | 316, 206 | 321, 207 | 353, 208 | 383 209 | ], 210 | "attack": 217, 211 | "defense": 144, 212 | "stamina": 260, 213 | "types": [ 214 | 8, 215 | 11 216 | ], 217 | "unreleased": true, 218 | "legendary": false, 219 | "mythic": false 220 | }, 221 | "1009": { 222 | "pokemonName": "Walking-wake", 223 | "quickMoves": [ 224 | 202, 225 | 204, 226 | 207, 227 | 216, 228 | 240, 229 | 278, 230 | 282, 231 | 283 232 | ], 233 | "chargedMoves": [ 234 | 14, 235 | 24, 236 | 57, 237 | 80, 238 | 82, 239 | 83, 240 | 105, 241 | 106, 242 | 107, 243 | 122, 244 | 125, 245 | 131, 246 | 132, 247 | 277, 248 | 279, 249 | 284, 250 | 285, 251 | 321, 252 | 379, 253 | 383 254 | ], 255 | "attack": 256, 256 | "defense": 188, 257 | "stamina": 223, 258 | "types": [ 259 | 11, 260 | 16 261 | ], 262 | "unreleased": true 263 | }, 264 | "1010": { 265 | "pokemonName": "Iron-leaves", 266 | "quickMoves": [ 267 | 219, 268 | 255, 269 | 282, 270 | 357, 271 | 402 272 | ], 273 | "chargedMoves": [ 274 | 14, 275 | 22, 276 | 45, 277 | 51, 278 | 100, 279 | 114, 280 | 116, 281 | 117, 282 | 123, 283 | 125, 284 | 132, 285 | 245, 286 | 247, 287 | 251, 288 | 272, 289 | 273, 290 | 321, 291 | 330, 292 | 343, 293 | 392 294 | ], 295 | "attack": 259, 296 | "defense": 213, 297 | "stamina": 207, 298 | "types": [ 299 | 12, 300 | 14 301 | ], 302 | "unreleased": true 303 | }, 304 | "1011": { 305 | "pokemonName": "Dipplin", 306 | "quickMoves": [ 307 | 201, 308 | 203, 309 | 204, 310 | 253, 311 | 260, 312 | 263, 313 | 271, 314 | 282, 315 | 368 316 | ], 317 | "chargedMoves": [ 318 | 14, 319 | 59, 320 | 82, 321 | 114, 322 | 116, 323 | 131, 324 | 132, 325 | 267, 326 | 272, 327 | 273, 328 | 277, 329 | 285, 330 | 321, 331 | 343 332 | ], 333 | "attack": 173, 334 | "defense": 184, 335 | "stamina": 190, 336 | "types": [ 337 | 12, 338 | 16 339 | ], 340 | "unreleased": true 341 | }, 342 | "1012": { 343 | "pokemonName": "Poltchageist", 344 | "quickMoves": [ 345 | 263, 346 | 264, 347 | 357 348 | ], 349 | "chargedMoves": [ 350 | 48, 351 | 70, 352 | 106, 353 | 114, 354 | 116, 355 | 132, 356 | 265, 357 | 273, 358 | 280, 359 | 343, 360 | 376 361 | ], 362 | "attack": 134, 363 | "defense": 96, 364 | "stamina": 120, 365 | "types": [ 366 | 8, 367 | 12 368 | ], 369 | "unreleased": true 370 | }, 371 | "1013": { 372 | "pokemonName": "Sinistcha", 373 | "quickMoves": [ 374 | 263, 375 | 264, 376 | 357 377 | ], 378 | "chargedMoves": [ 379 | 14, 380 | 48, 381 | 70, 382 | 106, 383 | 114, 384 | 116, 385 | 132, 386 | 265, 387 | 273, 388 | 280, 389 | 343, 390 | 376 391 | ], 392 | "attack": 225, 393 | "defense": 191, 394 | "stamina": 174, 395 | "types": [ 396 | 8, 397 | 12 398 | ], 399 | "unreleased": true 400 | }, 401 | "1014": { 402 | "pokemonName": "Okidogi", 403 | "quickMoves": [ 404 | 202, 405 | 207, 406 | 213, 407 | 224, 408 | 228, 409 | 240, 410 | 243, 411 | 278, 412 | 282, 413 | 326, 414 | 327 415 | ], 416 | "chargedMoves": [ 417 | 14, 418 | 26, 419 | 33, 420 | 50, 421 | 56, 422 | 63, 423 | 74, 424 | 77, 425 | 90, 426 | 91, 427 | 92, 428 | 115, 429 | 123, 430 | 131, 431 | 132, 432 | 245, 433 | 247, 434 | 277, 435 | 279, 436 | 314, 437 | 321, 438 | 353, 439 | 367, 440 | 377 441 | ], 442 | "attack": 241, 443 | "defense": 210, 444 | "stamina": 204, 445 | "types": [ 446 | 2, 447 | 4 448 | ], 449 | "unreleased": true 450 | }, 451 | "1015": { 452 | "pokemonName": "Munkidori", 453 | "quickMoves": [ 454 | 213, 455 | 220, 456 | 224, 457 | 233, 458 | 235, 459 | 264 460 | ], 461 | "chargedMoves": [ 462 | 14, 463 | 30, 464 | 60, 465 | 70, 466 | 90, 467 | 91, 468 | 92, 469 | 108, 470 | 125, 471 | 132, 472 | 247, 473 | 265, 474 | 272, 475 | 303, 476 | 321, 477 | 376, 478 | 392 479 | ], 480 | "attack": 261, 481 | "defense": 172, 482 | "stamina": 204, 483 | "types": [ 484 | 4, 485 | 14 486 | ], 487 | "unreleased": true 488 | }, 489 | "1016": { 490 | "pokemonName": "Fezandipiti", 491 | "quickMoves": [ 492 | 210, 493 | 211, 494 | 213, 495 | 219, 496 | 224, 497 | 255, 498 | 264, 499 | 282, 500 | 320, 501 | 356 502 | ], 503 | "chargedMoves": [ 504 | 14, 505 | 16, 506 | 42, 507 | 45, 508 | 70, 509 | 84, 510 | 86, 511 | 87, 512 | 88, 513 | 89, 514 | 90, 515 | 92, 516 | 111, 517 | 121, 518 | 122, 519 | 125, 520 | 132, 521 | 256, 522 | 303, 523 | 321, 524 | 341, 525 | 364 526 | ], 527 | "attack": 185, 528 | "defense": 228, 529 | "stamina": 204, 530 | "types": [ 531 | 4, 532 | 18 533 | ], 534 | "unreleased": true 535 | }, 536 | "1017": { 537 | "pokemonName": "Ogerpon", 538 | "quickMoves": [ 539 | 207, 540 | 214, 541 | 219, 542 | 234, 543 | 243, 544 | 271, 545 | 282, 546 | 320, 547 | 356, 548 | 357 549 | ], 550 | "chargedMoves": [ 551 | 56, 552 | 59, 553 | 63, 554 | 88, 555 | 114, 556 | 116, 557 | 118, 558 | 123, 559 | 132, 560 | 272, 561 | 273, 562 | 321, 563 | 343, 564 | 392 565 | ], 566 | "attack": 241, 567 | "defense": 196, 568 | "stamina": 190, 569 | "types": [ 570 | 12 571 | ], 572 | "unreleased": true 573 | }, 574 | "1018": { 575 | "pokemonName": "Archaludon", 576 | "quickMoves": [ 577 | 228, 578 | 241, 579 | 253, 580 | 278, 581 | 282, 582 | 297, 583 | 402 584 | ], 585 | "chargedMoves": [ 586 | 14, 587 | 16, 588 | 31, 589 | 32, 590 | 36, 591 | 51, 592 | 63, 593 | 64, 594 | 74, 595 | 78, 596 | 79, 597 | 82, 598 | 83, 599 | 116, 600 | 123, 601 | 131, 602 | 132, 603 | 267, 604 | 268, 605 | 276, 606 | 277, 607 | 280, 608 | 285, 609 | 321, 610 | 332, 611 | 372, 612 | 379 613 | ], 614 | "attack": 250, 615 | "defense": 215, 616 | "stamina": 207, 617 | "types": [ 618 | 9, 619 | 16 620 | ], 621 | "unreleased": true 622 | }, 623 | "1019": { 624 | "pokemonName": "Hydrapple", 625 | "quickMoves": [ 626 | 201, 627 | 203, 628 | 204, 629 | 253, 630 | 260, 631 | 263, 632 | 271, 633 | 282, 634 | 357, 635 | 368 636 | ], 637 | "chargedMoves": [ 638 | 14, 639 | 31, 640 | 59, 641 | 82, 642 | 107, 643 | 114, 644 | 116, 645 | 118, 646 | 131, 647 | 132, 648 | 267, 649 | 268, 650 | 272, 651 | 273, 652 | 277, 653 | 285, 654 | 304, 655 | 321, 656 | 343, 657 | 379 658 | ], 659 | "attack": 216, 660 | "defense": 186, 661 | "stamina": 235, 662 | "types": [ 663 | 12, 664 | 16 665 | ], 666 | "unreleased": true 667 | }, 668 | "1020": { 669 | "pokemonName": "Gouging-fire", 670 | "quickMoves": [ 671 | 202, 672 | 240, 673 | 253, 674 | 269, 675 | 278, 676 | 282, 677 | 326, 678 | 346, 679 | 356 680 | ], 681 | "chargedMoves": [ 682 | 14, 683 | 24, 684 | 31, 685 | 32, 686 | 42, 687 | 62, 688 | 74, 689 | 82, 690 | 83, 691 | 95, 692 | 101, 693 | 103, 694 | 127, 695 | 131, 696 | 132, 697 | 270, 698 | 277, 699 | 279, 700 | 285, 701 | 321, 702 | 353, 703 | 379, 704 | 393 705 | ], 706 | "attack": 225, 707 | "defense": 228, 708 | "stamina": 233, 709 | "types": [ 710 | 10, 711 | 16 712 | ], 713 | "unreleased": true 714 | }, 715 | "1021": { 716 | "pokemonName": "Raging-bolt", 717 | "quickMoves": [ 718 | 204, 719 | 249, 720 | 250, 721 | 253, 722 | 278, 723 | 282, 724 | 326 725 | ], 726 | "chargedMoves": [ 727 | 14, 728 | 31, 729 | 35, 730 | 62, 731 | 78, 732 | 79, 733 | 80, 734 | 82, 735 | 116, 736 | 127, 737 | 131, 738 | 132, 739 | 251, 740 | 252, 741 | 268, 742 | 277, 743 | 279, 744 | 285, 745 | 321, 746 | 379 747 | ], 748 | "attack": 235, 749 | "defense": 165, 750 | "stamina": 245, 751 | "types": [ 752 | 13, 753 | 16 754 | ], 755 | "unreleased": true 756 | }, 757 | "1022": { 758 | "pokemonName": "Iron-boulder", 759 | "quickMoves": [ 760 | 219, 761 | 224, 762 | 226, 763 | 227, 764 | 234, 765 | 243, 766 | 255, 767 | 282 768 | ], 769 | "chargedMoves": [ 770 | 14, 771 | 22, 772 | 31, 773 | 32, 774 | 45, 775 | 60, 776 | 63, 777 | 74, 778 | 95, 779 | 100, 780 | 108, 781 | 123, 782 | 126, 783 | 131, 784 | 132, 785 | 245, 786 | 251, 787 | 259, 788 | 321, 789 | 330, 790 | 372 791 | ], 792 | "attack": 249, 793 | "defense": 214, 794 | "stamina": 207, 795 | "types": [ 796 | 6, 797 | 14 798 | ], 799 | "unreleased": true 800 | }, 801 | "1023": { 802 | "pokemonName": "Iron-crown", 803 | "quickMoves": [ 804 | 226, 805 | 228, 806 | 234, 807 | 235, 808 | 250, 809 | 255, 810 | 282, 811 | 402 812 | ], 813 | "chargedMoves": [ 814 | 14, 815 | 36, 816 | 60, 817 | 74, 818 | 95, 819 | 100, 820 | 108, 821 | 123, 822 | 131, 823 | 132, 824 | 247, 825 | 268, 826 | 321, 827 | 330 828 | ], 829 | "attack": 243, 830 | "defense": 220, 831 | "stamina": 207, 832 | "types": [ 833 | 9, 834 | 14 835 | ], 836 | "unreleased": true 837 | }, 838 | "1024": { 839 | "pokemonName": "Terapagos", 840 | "quickMoves": [ 841 | 234, 842 | 282 843 | ], 844 | "chargedMoves": [ 845 | 14, 846 | 16, 847 | 24, 848 | 31, 849 | 32, 850 | 36, 851 | 39, 852 | 49, 853 | 62, 854 | 64, 855 | 65, 856 | 74, 857 | 78, 858 | 79, 859 | 82, 860 | 86, 861 | 105, 862 | 116, 863 | 131, 864 | 132, 865 | 251, 866 | 267, 867 | 268, 868 | 273, 869 | 279, 870 | 284, 871 | 304, 872 | 321, 873 | 332, 874 | 344, 875 | 372, 876 | 393 877 | ], 878 | "attack": 126, 879 | "defense": 165, 880 | "stamina": 207, 881 | "types": [ 882 | 1 883 | ], 884 | "unreleased": true 885 | }, 886 | "1025": { 887 | "pokemonName": "Pecharunt", 888 | "quickMoves": [ 889 | 263, 890 | 264, 891 | 368 892 | ], 893 | "chargedMoves": [ 894 | 70, 895 | 90, 896 | 91, 897 | 92, 898 | 132, 899 | 265, 900 | 280, 901 | 303, 902 | 376 903 | ], 904 | "attack": 181, 905 | "defense": 273, 906 | "stamina": 204, 907 | "types": [ 908 | 4, 909 | 8 910 | ], 911 | "unreleased": true 912 | } 913 | } -------------------------------------------------------------------------------- /static/cpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": 0.0939999967813492, 3 | "1.5": 0.135137432089339, 4 | "2": 0.166397869586945, 5 | "2.5": 0.192650913155325, 6 | "3": 0.215732470154762, 7 | "3.5": 0.236572651424822, 8 | "4": 0.255720049142838, 9 | "4.5": 0.273530372106572, 10 | "5": 0.290249884128571, 11 | "5.5": 0.306057381389863, 12 | "6": 0.321087598800659, 13 | "6.5": 0.335445031996451, 14 | "7": 0.349212676286697, 15 | "7.5": 0.362457736609939, 16 | "8": 0.375235587358475, 17 | "8.5": 0.387592407713878, 18 | "9": 0.399567276239395, 19 | "9.5": 0.4111935532161, 20 | "10": 0.422500014305115, 21 | "10.5": 0.432926420512509, 22 | "11": 0.443107545375824, 23 | "11.5": 0.453059948165049, 24 | "12": 0.46279838681221, 25 | "12.5": 0.472336085311278, 26 | "13": 0.481684952974319, 27 | "13.5": 0.490855807179549, 28 | "14": 0.499858438968658, 29 | "14.5": 0.5087017489616, 30 | "15": 0.517393946647644, 31 | "15.5": 0.525942516110322, 32 | "16": 0.534354329109192, 33 | "16.5": 0.542635753803599, 34 | "17": 0.550792694091797, 35 | "17.5": 0.558830584490385, 36 | "18": 0.566754519939423, 37 | "18.5": 0.57456912814537, 38 | "19": 0.582278907299042, 39 | "19.5": 0.589887907888945, 40 | "20": 0.597400009632111, 41 | "20.5": 0.604823648665171, 42 | "21": 0.61215728521347, 43 | "21.5": 0.619404107958234, 44 | "22": 0.626567125320435, 45 | "22.5": 0.633649178748576, 46 | "23": 0.6406529545784, 47 | "23.5": 0.647580971386554, 48 | "24": 0.654435634613037, 49 | "24.5": 0.661219265805859, 50 | "25": 0.667934000492095, 51 | "25.5": 0.674581885647492, 52 | "26": 0.681164920330048, 53 | "26.5": 0.687684901255373, 54 | "27": 0.694143652915955, 55 | "27.5": 0.700542901033063, 56 | "28": 0.706884205341339, 57 | "28.5": 0.713169074873823, 58 | "29": 0.719399094581604, 59 | "29.5": 0.725575586915154, 60 | "30": 0.731700003147125, 61 | "30.5": 0.734741038550429, 62 | "31": 0.737769484519958, 63 | "31.5": 0.740785579737136, 64 | "32": 0.743789434432983, 65 | "32.5": 0.746781197247765, 66 | "33": 0.749761044979095, 67 | "33.5": 0.752729099732281, 68 | "34": 0.75568550825119, 69 | "34.5": 0.758630370209851, 70 | "35": 0.761563837528229, 71 | "35.5": 0.76448604959218, 72 | "36": 0.767397165298462, 73 | "36.5": 0.770297293677362, 74 | "37": 0.773186504840851, 75 | "37.5": 0.776064947064992, 76 | "38": 0.778932750225067, 77 | "38.5": 0.781790050767666, 78 | "39": 0.784636974334717, 79 | "39.5": 0.787473608513275, 80 | "40": 0.790300011634826, 81 | "40.5": 0.792803950958807, 82 | "41": 0.795300006866455, 83 | "41.5": 0.79780392148697, 84 | "42": 0.800300002098083, 85 | "42.5": 0.802803892322847, 86 | "43": 0.805299997329711, 87 | "43.5": 0.807803863460723, 88 | "44": 0.81029999256134, 89 | "44.5": 0.812803834895026, 90 | "45": 0.815299987792968, 91 | "45.5": 0.817803806620319, 92 | "46": 0.820299983024597, 93 | "46.5": 0.822803778631297, 94 | "47": 0.825299978256225, 95 | "47.5": 0.827803750922782, 96 | "48": 0.830299973487854, 97 | "48.5": 0.832803753381377, 98 | "49": 0.835300028324127, 99 | "49.5": 0.837803755931569, 100 | "50": 0.840300023555755, 101 | "50.5": 0.842803729034748, 102 | "51": 0.845300018787384, 103 | "51.5": 0.847803702398935, 104 | "52": 0.850300014019012, 105 | "52.5": 0.852803676019539, 106 | "53": 0.85530000925064, 107 | "53.5": 0.857803649892077, 108 | "54": 0.860300004482269, 109 | "54.5": 0.862803624012168, 110 | "55": 0.865299999713897 111 | } -------------------------------------------------------------------------------- /static/tempEvos.json: -------------------------------------------------------------------------------- 1 | { 2 | "mega": { 3 | "65": { 4 | "tempEvolutions": [] 5 | }, 6 | "115": { 7 | "tempEvolutions": [] 8 | }, 9 | "127": { 10 | "tempEvolutions": [] 11 | }, 12 | "142": { 13 | "tempEvolutions": [] 14 | }, 15 | "150": { 16 | "tempEvolutions": [ 17 | { 18 | "tempEvoId": 2, 19 | "attack": 412, 20 | "defense": 222, 21 | "stamina": 235, 22 | "types": [ 23 | 2, 24 | 14 25 | ], 26 | "unreleased": true 27 | }, 28 | { 29 | "tempEvoId": 3, 30 | "attack": 426, 31 | "defense": 229, 32 | "stamina": 235, 33 | "unreleased": true 34 | } 35 | ] 36 | }, 37 | "208": { 38 | "tempEvolutions": [] 39 | }, 40 | "212": { 41 | "tempEvolutions": [] 42 | }, 43 | "214": { 44 | "tempEvolutions": [ 45 | { 46 | "tempEvoId": 1, 47 | "attack": 334, 48 | "defense": 223, 49 | "stamina": 190, 50 | "unreleased": true 51 | } 52 | ] 53 | }, 54 | "248": { 55 | "tempEvolutions": [] 56 | }, 57 | "254": { 58 | "tempEvolutions": [] 59 | }, 60 | "257": { 61 | "tempEvolutions": [] 62 | }, 63 | "260": { 64 | "tempEvolutions": [] 65 | }, 66 | "282": { 67 | "tempEvolutions": [] 68 | }, 69 | "302": { 70 | "tempEvolutions": [] 71 | }, 72 | "303": { 73 | "tempEvolutions": [ 74 | { 75 | "tempEvoId": 1, 76 | "attack": 188, 77 | "defense": 217, 78 | "stamina": 137, 79 | "unreleased": true 80 | } 81 | ] 82 | }, 83 | "306": { 84 | "tempEvolutions": [] 85 | }, 86 | "308": { 87 | "tempEvolutions": [] 88 | }, 89 | "319": { 90 | "tempEvolutions": [ 91 | { 92 | "tempEvoId": 1, 93 | "attack": 289, 94 | "defense": 144, 95 | "stamina": 172, 96 | "unreleased": true 97 | } 98 | ] 99 | }, 100 | "323": { 101 | "tempEvolutions": [ 102 | { 103 | "tempEvoId": 1, 104 | "attack": 253, 105 | "defense": 183, 106 | "stamina": 172, 107 | "unreleased": true 108 | } 109 | ] 110 | }, 111 | "354": { 112 | "tempEvolutions": [] 113 | }, 114 | "359": { 115 | "tempEvolutions": [] 116 | }, 117 | "362": { 118 | "tempEvolutions": [] 119 | }, 120 | "373": { 121 | "tempEvolutions": [] 122 | }, 123 | "376": { 124 | "tempEvolutions": [ 125 | { 126 | "tempEvoId": 1, 127 | "attack": 300, 128 | "defense": 289, 129 | "stamina": 190, 130 | "unreleased": true 131 | } 132 | ] 133 | }, 134 | "380": { 135 | "tempEvolutions": [] 136 | }, 137 | "381": { 138 | "tempEvolutions": [] 139 | }, 140 | "384": { 141 | "tempEvolutions": [] 142 | }, 143 | "445": { 144 | "tempEvolutions": [] 145 | }, 146 | "448": { 147 | "tempEvolutions": [ 148 | { 149 | "tempEvoId": 1, 150 | "attack": 310, 151 | "defense": 175, 152 | "stamina": 172, 153 | "unreleased": true 154 | } 155 | ] 156 | }, 157 | "475": { 158 | "tempEvolutions": [ 159 | { 160 | "tempEvoId": 1, 161 | "attack": 326, 162 | "defense": 230, 163 | "stamina": 169, 164 | "unreleased": true 165 | } 166 | ] 167 | }, 168 | "531": { 169 | "tempEvolutions": [ 170 | { 171 | "tempEvoId": 1, 172 | "attack": 147, 173 | "defense": 239, 174 | "stamina": 230, 175 | "types": [ 176 | 1, 177 | 18 178 | ], 179 | "unreleased": true 180 | } 181 | ] 182 | }, 183 | "719": { 184 | "tempEvolutions": [] 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /static/types.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": { 3 | "strengths": [], 4 | "weaknesses": [], 5 | "veryWeakAgainst": [], 6 | "immunes": [], 7 | "weakAgainst": [], 8 | "resistances": [] 9 | }, 10 | "1": { 11 | "strengths": [], 12 | "weaknesses": [ 13 | 2 14 | ], 15 | "veryWeakAgainst": [ 16 | 8 17 | ], 18 | "immunes": [ 19 | 8 20 | ], 21 | "weakAgainst": [ 22 | 6, 23 | 9 24 | ], 25 | "resistances": [] 26 | }, 27 | "2": { 28 | "strengths": [ 29 | 1, 30 | 6, 31 | 9, 32 | 15, 33 | 17 34 | ], 35 | "weaknesses": [ 36 | 3, 37 | 14, 38 | 18 39 | ], 40 | "veryWeakAgainst": [ 41 | 8 42 | ], 43 | "immunes": [], 44 | "weakAgainst": [ 45 | 3, 46 | 4, 47 | 7, 48 | 14, 49 | 18 50 | ], 51 | "resistances": [ 52 | 6, 53 | 7, 54 | 17 55 | ] 56 | }, 57 | "3": { 58 | "strengths": [ 59 | 2, 60 | 7, 61 | 12 62 | ], 63 | "weaknesses": [ 64 | 6, 65 | 13, 66 | 15 67 | ], 68 | "veryWeakAgainst": [], 69 | "immunes": [ 70 | 5 71 | ], 72 | "weakAgainst": [ 73 | 6, 74 | 9, 75 | 13 76 | ], 77 | "resistances": [ 78 | 2, 79 | 7, 80 | 12 81 | ] 82 | }, 83 | "4": { 84 | "strengths": [ 85 | 12, 86 | 18 87 | ], 88 | "weaknesses": [ 89 | 5, 90 | 14 91 | ], 92 | "veryWeakAgainst": [ 93 | 9 94 | ], 95 | "immunes": [], 96 | "weakAgainst": [ 97 | 4, 98 | 5, 99 | 6, 100 | 8 101 | ], 102 | "resistances": [ 103 | 2, 104 | 4, 105 | 7, 106 | 12, 107 | 18 108 | ] 109 | }, 110 | "5": { 111 | "strengths": [ 112 | 4, 113 | 6, 114 | 9, 115 | 10, 116 | 13 117 | ], 118 | "weaknesses": [ 119 | 11, 120 | 12, 121 | 15 122 | ], 123 | "veryWeakAgainst": [ 124 | 3 125 | ], 126 | "immunes": [ 127 | 13 128 | ], 129 | "weakAgainst": [ 130 | 7, 131 | 12 132 | ], 133 | "resistances": [ 134 | 4, 135 | 6 136 | ] 137 | }, 138 | "6": { 139 | "strengths": [ 140 | 3, 141 | 7, 142 | 10, 143 | 15 144 | ], 145 | "weaknesses": [ 146 | 2, 147 | 5, 148 | 9, 149 | 11, 150 | 12 151 | ], 152 | "veryWeakAgainst": [], 153 | "immunes": [], 154 | "weakAgainst": [ 155 | 2, 156 | 5, 157 | 9 158 | ], 159 | "resistances": [ 160 | 1, 161 | 3, 162 | 4, 163 | 10 164 | ] 165 | }, 166 | "7": { 167 | "strengths": [ 168 | 12, 169 | 14, 170 | 17 171 | ], 172 | "weaknesses": [ 173 | 3, 174 | 6, 175 | 10 176 | ], 177 | "veryWeakAgainst": [], 178 | "immunes": [], 179 | "weakAgainst": [ 180 | 2, 181 | 3, 182 | 4, 183 | 8, 184 | 9, 185 | 10, 186 | 18 187 | ], 188 | "resistances": [ 189 | 2, 190 | 5, 191 | 12 192 | ] 193 | }, 194 | "8": { 195 | "strengths": [ 196 | 8, 197 | 14 198 | ], 199 | "weaknesses": [ 200 | 8, 201 | 17 202 | ], 203 | "veryWeakAgainst": [ 204 | 1 205 | ], 206 | "immunes": [ 207 | 1, 208 | 2 209 | ], 210 | "weakAgainst": [ 211 | 17 212 | ], 213 | "resistances": [ 214 | 4, 215 | 7 216 | ] 217 | }, 218 | "9": { 219 | "strengths": [ 220 | 6, 221 | 15, 222 | 18 223 | ], 224 | "weaknesses": [ 225 | 2, 226 | 5, 227 | 10 228 | ], 229 | "veryWeakAgainst": [], 230 | "immunes": [ 231 | 4 232 | ], 233 | "weakAgainst": [ 234 | 9, 235 | 10, 236 | 11, 237 | 13 238 | ], 239 | "resistances": [ 240 | 1, 241 | 3, 242 | 6, 243 | 7, 244 | 9, 245 | 12, 246 | 14, 247 | 15, 248 | 16, 249 | 18 250 | ] 251 | }, 252 | "10": { 253 | "strengths": [ 254 | 7, 255 | 9, 256 | 12, 257 | 15 258 | ], 259 | "weaknesses": [ 260 | 5, 261 | 6, 262 | 11 263 | ], 264 | "veryWeakAgainst": [], 265 | "immunes": [], 266 | "weakAgainst": [ 267 | 6, 268 | 10, 269 | 11, 270 | 16 271 | ], 272 | "resistances": [ 273 | 7, 274 | 9, 275 | 10, 276 | 12, 277 | 15, 278 | 18 279 | ] 280 | }, 281 | "11": { 282 | "strengths": [ 283 | 5, 284 | 6, 285 | 10 286 | ], 287 | "weaknesses": [ 288 | 12, 289 | 13 290 | ], 291 | "veryWeakAgainst": [], 292 | "immunes": [], 293 | "weakAgainst": [ 294 | 11, 295 | 12, 296 | 16 297 | ], 298 | "resistances": [ 299 | 9, 300 | 10, 301 | 11, 302 | 15 303 | ] 304 | }, 305 | "12": { 306 | "strengths": [ 307 | 5, 308 | 6, 309 | 11 310 | ], 311 | "weaknesses": [ 312 | 3, 313 | 4, 314 | 7, 315 | 10, 316 | 15 317 | ], 318 | "veryWeakAgainst": [], 319 | "immunes": [], 320 | "weakAgainst": [ 321 | 3, 322 | 4, 323 | 7, 324 | 9, 325 | 10, 326 | 12, 327 | 16 328 | ], 329 | "resistances": [ 330 | 5, 331 | 11, 332 | 12, 333 | 13 334 | ] 335 | }, 336 | "13": { 337 | "strengths": [ 338 | 3, 339 | 11 340 | ], 341 | "weaknesses": [ 342 | 5 343 | ], 344 | "veryWeakAgainst": [ 345 | 5 346 | ], 347 | "immunes": [], 348 | "weakAgainst": [ 349 | 12, 350 | 13, 351 | 16 352 | ], 353 | "resistances": [ 354 | 3, 355 | 9, 356 | 13 357 | ] 358 | }, 359 | "14": { 360 | "strengths": [ 361 | 2, 362 | 4 363 | ], 364 | "weaknesses": [ 365 | 7, 366 | 8, 367 | 17 368 | ], 369 | "veryWeakAgainst": [ 370 | 17 371 | ], 372 | "immunes": [], 373 | "weakAgainst": [ 374 | 9, 375 | 14 376 | ], 377 | "resistances": [ 378 | 2, 379 | 14 380 | ] 381 | }, 382 | "15": { 383 | "strengths": [ 384 | 3, 385 | 5, 386 | 12, 387 | 16 388 | ], 389 | "weaknesses": [ 390 | 2, 391 | 6, 392 | 9, 393 | 10 394 | ], 395 | "veryWeakAgainst": [], 396 | "immunes": [], 397 | "weakAgainst": [ 398 | 9, 399 | 10, 400 | 11, 401 | 15 402 | ], 403 | "resistances": [ 404 | 15 405 | ] 406 | }, 407 | "16": { 408 | "strengths": [ 409 | 16 410 | ], 411 | "weaknesses": [ 412 | 15, 413 | 16, 414 | 18 415 | ], 416 | "veryWeakAgainst": [ 417 | 18 418 | ], 419 | "immunes": [], 420 | "weakAgainst": [ 421 | 9 422 | ], 423 | "resistances": [ 424 | 10, 425 | 11, 426 | 12, 427 | 13 428 | ] 429 | }, 430 | "17": { 431 | "strengths": [ 432 | 8, 433 | 14 434 | ], 435 | "weaknesses": [ 436 | 2, 437 | 7, 438 | 18 439 | ], 440 | "veryWeakAgainst": [], 441 | "immunes": [ 442 | 14 443 | ], 444 | "weakAgainst": [ 445 | 2, 446 | 17, 447 | 18 448 | ], 449 | "resistances": [ 450 | 8, 451 | 17 452 | ] 453 | }, 454 | "18": { 455 | "strengths": [ 456 | 2, 457 | 16, 458 | 17 459 | ], 460 | "weaknesses": [ 461 | 4, 462 | 9 463 | ], 464 | "veryWeakAgainst": [], 465 | "immunes": [ 466 | 16 467 | ], 468 | "weakAgainst": [ 469 | 4, 470 | 9, 471 | 10 472 | ], 473 | "resistances": [ 474 | 2, 475 | 7, 476 | 17 477 | ] 478 | } 479 | } -------------------------------------------------------------------------------- /tests/customValues.json: -------------------------------------------------------------------------------- 1 | { 2 | "pokemon": { 3 | "6_%_178": { 4 | "name": "Glurak", 5 | "id": 6, 6 | "generation": "Kanto", 7 | "form": { 8 | "formName": "Normal", 9 | "id": 178 10 | }, 11 | "types": [ 12 | { 13 | "typeId": 3, 14 | "typeName": "Flug" 15 | }, 16 | { 17 | "typeId": 10, 18 | "typeName": "Feuer" 19 | } 20 | ], 21 | "stats": { 22 | "baseAttack": 223, 23 | "baseDefense": 173, 24 | "baseStamina": 186 25 | }, 26 | "quickMoves": { 27 | "255": "Luftschnitt", 28 | "269": "Feuerwirbel" 29 | }, 30 | "chargedMoves": { 31 | "83": "Drachenklaue", 32 | "103": "Feuersturm", 33 | "270": "Hitzekoller" 34 | }, 35 | "family": 4, 36 | "tempEvolutions": [ 37 | 2, 38 | 3 39 | ] 40 | }, 41 | "53_%_66": { 42 | "id": 53, 43 | "name": "Snobilikat", 44 | "form": { 45 | "formName": "Alola", 46 | "id": 66 47 | }, 48 | "types": [ 49 | { 50 | "typeId": 17, 51 | "typeName": "Unlicht" 52 | } 53 | ], 54 | "stats": { 55 | "baseAttack": 158, 56 | "baseDefense": 136, 57 | "baseStamina": 163 58 | }, 59 | "quickMoves": { 60 | "220": "Kratzer", 61 | "238": "Finte" 62 | }, 63 | "chargedMoves": { 64 | "16": "Finsteraura", 65 | "88": "Knuddler", 66 | "280": "Schmarotzer", 67 | "333": "Gegenstoß", 68 | "392": "Wegbereiter" 69 | }, 70 | "family": 52, 71 | "generation": "Kanto" 72 | } 73 | }, 74 | "types": { 75 | "2_%_Kampf": { 76 | "typeName": "Kampf" 77 | }, 78 | "3_%_Flug": { 79 | "typeName": "Flug" 80 | } 81 | }, 82 | "moves": { 83 | "23_%_Flügelschlag": { 84 | "moveName": "Flügelschlag", 85 | "proto": "WING_ATTACK" 86 | }, 87 | "24_%_Flammenwurf_%_65": { 88 | "moveName": "Flammenwurf", 89 | "proto": "FLAMETHROWER", 90 | "type": { 91 | "typeName": "Feuer" 92 | } 93 | } 94 | }, 95 | "items": { 96 | "3": { 97 | "itemName": "Hyperball", 98 | "proto": "ITEM_ULTRA_BALL", 99 | "minTrainerLevel": 20 100 | }, 101 | "4": { 102 | "itemName": "Meisterball", 103 | "proto": "ITEM_MASTER_BALL", 104 | "minTrainerLevel": 1 105 | } 106 | }, 107 | "invasions": { 108 | "Grunt_%_Darkness": { 109 | "type": "Darkness", 110 | "gender": "Male", 111 | "grunt": "Grunt" 112 | }, 113 | "Grunt_%_Dark": { 114 | "type": "Dark", 115 | "gender": "Male", 116 | "grunt": "Grunt" 117 | } 118 | }, 119 | "weather": { 120 | "0": "Extrem", 121 | "1": "Klar", 122 | "2": "Regen" 123 | }, 124 | "translations": { 125 | "de": { 126 | "pokemon_0": "Ersatzpokemon", 127 | "pokemon_1": "Bisasam", 128 | "forms_34": "Angriffsform", 129 | "forms_35": "Verteidigungsform", 130 | "pokemon_pokedex_descriptions_0": "Keiner", 131 | "pokemon_pokedex_descriptions_1": "Bisasam macht gern einmal ein Nickerchen im Sonnenschein. Auf seinem Rücken trägt es einen Samen. Indem es Sonnenstrahlen aufsaugt, wird der Samen zunehmend größer.", 132 | "stupid_costumes_12": "April 2020 Noevolve", 133 | "stupid_costumes_13": "Safari 2020 Noevolve", 134 | "fight_club!_0": "Unbekannt", 135 | "fight_club!_1": "Donnerschock", 136 | "pokemon%types_3": "Flug", 137 | "pokemon%types_4": "Gift", 138 | "weather_is_not_accurate_0": "Extrem", 139 | "weather_is_not_accurate_1": "Klar", 140 | "amazing_pokemon_spawners_502": "Gletscher", 141 | "amazing_pokemon_spawners_503": "Moos", 142 | "cool_helpers_1404": "Sternenstück", 143 | "cool_helpers_1405": "Geschenk" 144 | }, 145 | "en": { 146 | "pokemon_0": "Substitute", 147 | "pokemon_1": "Bulbasaur", 148 | "forms_34": "Attack", 149 | "forms_35": "Defense", 150 | "stupid_costumes_12": "April 2020 Noevolve", 151 | "stupid_costumes_13": "Safari 2020 Noevolve", 152 | "fight_club!_0": "Unknown", 153 | "fight_club!_1": "Thunder Shock", 154 | "cool_helpers_1404": "Star Piece", 155 | "cool_helpers_1405": "Gift", 156 | "amazing_pokemon_spawners_502": "Glacial", 157 | "amazing_pokemon_spawners_503": "Mossy", 158 | "pokemon%types_3": "Flying", 159 | "pokemon%types_4": "Poison" 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /tests/defaultValues.json: -------------------------------------------------------------------------------- 1 | { 2 | "pokemon": { 3 | "1": { 4 | "name": "Bulbasaur", 5 | "pokedex_id": 1, 6 | "default_form_id": 163, 7 | "forms": { 8 | "163": { 9 | "name": "Normal", 10 | "proto": "BULBASAUR_NORMAL", 11 | "evolutions": [ 12 | { 13 | "pokemon": 2, 14 | "form": 166 15 | } 16 | ] 17 | }, 18 | "897": { 19 | "name": "Fall 2019", 20 | "proto": "BULBASAUR_FALL_2019", 21 | "is_costume": true 22 | } 23 | }, 24 | "types": [ 25 | "Poison", 26 | "Grass" 27 | ], 28 | "attack": 118, 29 | "defense": 111, 30 | "stamina": 128, 31 | "height": 0.7, 32 | "weight": 6.9, 33 | "quick_moves": [ 34 | "Vine Whip", 35 | "Tackle" 36 | ], 37 | "charged_moves": [ 38 | "Seed Bomb", 39 | "Sludge Bomb", 40 | "Power Whip" 41 | ], 42 | "family": 1, 43 | "legendary": false, 44 | "mythic": false, 45 | "buddy_group_number": 2, 46 | "buddy_distance": 3, 47 | "third_move_stardust": 10000, 48 | "third_move_candy": 25, 49 | "gym_defender_eligible": true, 50 | "gen_id": 1, 51 | "generation": "Kanto", 52 | "evolutions": [ 53 | { 54 | "pokemon": 2, 55 | "form": 166 56 | } 57 | ], 58 | "little": true 59 | }, 60 | "6": { 61 | "name": "Charizard", 62 | "pokedex_id": 6, 63 | "default_form_id": 178, 64 | "forms": { 65 | "178": { 66 | "name": "Normal", 67 | "proto": "CHARIZARD_NORMAL", 68 | "temp_evolutions": { 69 | "2": {}, 70 | "3": {} 71 | } 72 | }, 73 | "951": { 74 | "name": "Copy 2019", 75 | "proto": "CHARIZARD_COPY_2019" 76 | } 77 | }, 78 | "types": [ 79 | "Flying", 80 | "Fire" 81 | ], 82 | "attack": 223, 83 | "defense": 173, 84 | "stamina": 186, 85 | "height": 1.7, 86 | "weight": 90.5, 87 | "quick_moves": [ 88 | "Air Slash", 89 | "Fire Spin" 90 | ], 91 | "charged_moves": [ 92 | "Dragon Claw", 93 | "Fire Blast", 94 | "Overheat" 95 | ], 96 | "family": 4, 97 | "legendary": false, 98 | "mythic": false, 99 | "buddy_group_number": 3, 100 | "buddy_distance": 3, 101 | "third_move_stardust": 10000, 102 | "third_move_candy": 25, 103 | "gym_defender_eligible": true, 104 | "gen_id": 1, 105 | "generation": "Kanto", 106 | "temp_evolutions": { 107 | "2": { 108 | "attack": 273, 109 | "defense": 213, 110 | "stamina": 186, 111 | "weight": 110.5, 112 | "types": [ 113 | "Fire", 114 | "Dragon" 115 | ] 116 | }, 117 | "3": { 118 | "attack": 319, 119 | "defense": 212, 120 | "stamina": 186, 121 | "weight": 100.5 122 | } 123 | } 124 | }, 125 | "52": { 126 | "name": "Meowth", 127 | "pokedex_id": 52, 128 | "default_form_id": 63, 129 | "forms": { 130 | "63": { 131 | "name": "Normal", 132 | "proto": "MEOWTH_NORMAL", 133 | "evolutions": [ 134 | { 135 | "pokemon": 53, 136 | "form": 65 137 | } 138 | ] 139 | }, 140 | "64": { 141 | "name": "Alola", 142 | "proto": "MEOWTH_ALOLA", 143 | "attack": 99, 144 | "defense": 78, 145 | "stamina": 120, 146 | "types": [ 147 | "Dark" 148 | ], 149 | "charged_moves": [ 150 | "Dark Pulse", 151 | "Night Slash", 152 | "Foul Play", 153 | "Trailblaze" 154 | ], 155 | "evolutions": [ 156 | { 157 | "pokemon": 53, 158 | "form": 66 159 | } 160 | ] 161 | }, 162 | "2335": { 163 | "name": "Galarian", 164 | "proto": "MEOWTH_GALARIAN", 165 | "attack": 115, 166 | "defense": 92, 167 | "stamina": 137, 168 | "height": 0.4, 169 | "weight": 7.5, 170 | "quick_moves": [ 171 | "Scratch", 172 | "Metal Claw" 173 | ], 174 | "charged_moves": [ 175 | "Dig", 176 | "Night Slash", 177 | "Gyro Ball", 178 | "Trailblaze" 179 | ], 180 | "types": [ 181 | "Steel" 182 | ], 183 | "evolutions": [ 184 | { 185 | "pokemon": 863, 186 | "form": 2504 187 | } 188 | ] 189 | } 190 | }, 191 | "types": [ 192 | "Normal" 193 | ], 194 | "attack": 92, 195 | "defense": 78, 196 | "stamina": 120, 197 | "height": 0.4, 198 | "weight": 4.2, 199 | "quick_moves": [ 200 | "Bite", 201 | "Scratch" 202 | ], 203 | "charged_moves": [ 204 | "Dark Pulse", 205 | "Night Slash", 206 | "Foul Play" 207 | ], 208 | "family": 52, 209 | "legendary": false, 210 | "mythic": false, 211 | "buddy_group_number": 2, 212 | "buddy_distance": 3, 213 | "third_move_stardust": 50000, 214 | "third_move_candy": 50, 215 | "gym_defender_eligible": true, 216 | "gen_id": 1, 217 | "generation": "Kanto", 218 | "evolutions": [ 219 | { 220 | "pokemon": 53, 221 | "form": 65 222 | } 223 | ], 224 | "little": true 225 | }, 226 | "531": { 227 | "name": "Audino", 228 | "pokedex_id": 531, 229 | "default_form_id": 0, 230 | "forms": { 231 | "0": { 232 | "name": "Unset", 233 | "proto": "FORM_UNSET" 234 | }, 235 | "1997": { 236 | "name": "Normal", 237 | "proto": "AUDINO_NORMAL", 238 | "temp_evolutions": { 239 | "1": {} 240 | } 241 | } 242 | }, 243 | "gen_id": 5, 244 | "generation": "Unova", 245 | "types": [ 246 | "Normal" 247 | ], 248 | "attack": 114, 249 | "defense": 163, 250 | "stamina": 230, 251 | "height": 1.1, 252 | "weight": 31, 253 | "quick_moves": [ 254 | "Pound", 255 | "Zen Headbutt" 256 | ], 257 | "charged_moves": [ 258 | "Hyper Beam", 259 | "Disarming Voice", 260 | "Dazzling Gleam", 261 | "Body Slam" 262 | ], 263 | "family": 531, 264 | "legendary": false, 265 | "mythic": false, 266 | "buddy_group_number": 2, 267 | "buddy_distance": 3, 268 | "third_move_stardust": 50000, 269 | "third_move_candy": 50, 270 | "gym_defender_eligible": true, 271 | "temp_evolutions": { 272 | "1": { 273 | "attack": 147, 274 | "defense": 239, 275 | "stamina": 230, 276 | "height": 1.5, 277 | "weight": 32, 278 | "types": [ 279 | "Normal", 280 | "Fairy" 281 | ] 282 | } 283 | } 284 | }, 285 | "650": { 286 | "name": "Chespin", 287 | "pokedex_id": 650, 288 | "default_form_id": 0, 289 | "forms": { 290 | "0": { 291 | "name": "Unset", 292 | "proto": "FORM_UNSET" 293 | } 294 | }, 295 | "gen_id": 6, 296 | "generation": "Kalos", 297 | "types": [ 298 | "Grass" 299 | ], 300 | "attack": 110, 301 | "defense": 106, 302 | "stamina": 148, 303 | "height": 0.4, 304 | "weight": 9, 305 | "quick_moves": [ 306 | "Vine Whip", 307 | "Take Down" 308 | ], 309 | "charged_moves": [ 310 | "Seed Bomb", 311 | "Body Slam", 312 | "Gyro Ball" 313 | ], 314 | "family": 650, 315 | "legendary": false, 316 | "mythic": false, 317 | "buddy_group_number": 1, 318 | "buddy_distance": 3, 319 | "third_move_stardust": 10000, 320 | "third_move_candy": 25, 321 | "gym_defender_eligible": true, 322 | "evolutions": [ 323 | { 324 | "pokemon": 651 325 | } 326 | ], 327 | "little": true 328 | }, 329 | "668": { 330 | "name": "Pyroar", 331 | "pokedex_id": 668, 332 | "default_form_id": 2587, 333 | "forms": { 334 | "2587": { 335 | "name": "Normal", 336 | "proto": "PYROAR_NORMAL" 337 | }, 338 | "2588": { 339 | "name": "Female", 340 | "proto": "PYROAR_FEMALE" 341 | } 342 | }, 343 | "types": [ 344 | "Normal", 345 | "Fire" 346 | ], 347 | "attack": 221, 348 | "defense": 149, 349 | "stamina": 200, 350 | "height": 1.5, 351 | "weight": 81.5, 352 | "quick_moves": [ 353 | "Ember", 354 | "Fire Fang", 355 | "Take Down", 356 | "Incinerate" 357 | ], 358 | "charged_moves": [ 359 | "Dark Pulse", 360 | "Flame Charge", 361 | "Solar Beam", 362 | "Overheat" 363 | ], 364 | "family": 667, 365 | "legendary": false, 366 | "mythic": false, 367 | "buddy_distance": 3, 368 | "third_move_stardust": 10000, 369 | "third_move_candy": 25, 370 | "gym_defender_eligible": true, 371 | "gen_id": 6, 372 | "generation": "Kalos" 373 | }, 374 | "718": { 375 | "name": "Zygarde", 376 | "pokedex_id": 718, 377 | "default_form_id": 2591, 378 | "types": [ 379 | "Ground", 380 | "Dragon" 381 | ], 382 | "quick_moves": [ 383 | "Bite", 384 | "Zen Headbutt", 385 | "Dragon Tail" 386 | ], 387 | "charged_moves": [ 388 | "Hyper Beam", 389 | "Earthquake", 390 | "Bulldoze", 391 | "Outrage", 392 | "Crunch" 393 | ], 394 | "gen_id": 6, 395 | "generation": "Kalos", 396 | "forms": { 397 | "2591": { 398 | "name": "Ten Percent", 399 | "proto": "ZYGARDE_TEN_PERCENT", 400 | "attack": 205, 401 | "defense": 173, 402 | "stamina": 144, 403 | "height": 1.2, 404 | "weight": 33.5, 405 | "evolutions": [ 406 | { 407 | "pokemon": 718, 408 | "form": 2592 409 | } 410 | ] 411 | }, 412 | "2592": { 413 | "name": "Fifty Percent", 414 | "proto": "ZYGARDE_FIFTY_PERCENT", 415 | "evolutions": [ 416 | { 417 | "pokemon": 718, 418 | "form": 2593 419 | } 420 | ] 421 | }, 422 | "2593": { 423 | "name": "Complete", 424 | "proto": "ZYGARDE_COMPLETE", 425 | "attack": 184, 426 | "defense": 207, 427 | "stamina": 389, 428 | "height": 4.5, 429 | "weight": 610 430 | }, 431 | "2823": { 432 | "name": "Complete Ten Percent", 433 | "proto": "ZYGARDE_COMPLETE_TEN_PERCENT", 434 | "attack": 205, 435 | "defense": 173, 436 | "stamina": 144, 437 | "height": 1.2, 438 | "weight": 33.5 439 | }, 440 | "2824": { 441 | "name": "Complete Fifty Percent", 442 | "proto": "ZYGARDE_COMPLETE_FIFTY_PERCENT" 443 | } 444 | }, 445 | "attack": 203, 446 | "defense": 232, 447 | "stamina": 239, 448 | "height": 5, 449 | "weight": 305, 450 | "family": 718, 451 | "legendary": true, 452 | "mythic": false, 453 | "buddy_group_number": 2, 454 | "buddy_distance": 20, 455 | "third_move_stardust": 100000, 456 | "third_move_candy": 100, 457 | "evolutions": [ 458 | { 459 | "pokemon": 718, 460 | "form": 2592 461 | } 462 | ] 463 | }, 464 | "719": { 465 | "name": "Diancie", 466 | "pokedex_id": 719, 467 | "default_form_id": 0, 468 | "types": [ 469 | "Rock", 470 | "Fairy" 471 | ], 472 | "quick_moves": [ 473 | "Tackle", 474 | "Rock Throw" 475 | ], 476 | "charged_moves": [ 477 | "Rock Slide", 478 | "Power Gem", 479 | "Moonblast" 480 | ], 481 | "gen_id": 6, 482 | "generation": "Kalos", 483 | "forms": { 484 | "0": { 485 | "name": "Unset", 486 | "proto": "FORM_UNSET" 487 | } 488 | }, 489 | "attack": 190, 490 | "defense": 285, 491 | "stamina": 137, 492 | "height": 0.7, 493 | "weight": 8.8, 494 | "family": 719, 495 | "legendary": false, 496 | "mythic": true, 497 | "buddy_group_number": 2, 498 | "buddy_distance": 20, 499 | "third_move_stardust": 100000, 500 | "third_move_candy": 100, 501 | "temp_evolutions": { 502 | "1": { 503 | "attack": 342, 504 | "defense": 235, 505 | "stamina": 137, 506 | "height": 1.1, 507 | "weight": 27.8 508 | } 509 | } 510 | } 511 | }, 512 | "types": { 513 | "0": "None", 514 | "1": "Normal" 515 | }, 516 | "moves": { 517 | "0": { 518 | "id": 0, 519 | "name": "Move Unset", 520 | "proto": "MOVE_UNSET" 521 | }, 522 | "21": { 523 | "id": 21, 524 | "name": "Flame Wheel", 525 | "proto": "FLAME_WHEEL", 526 | "type": "Fire", 527 | "power": 55 528 | } 529 | }, 530 | "items": { 531 | "1": { 532 | "name": "Poke Ball", 533 | "proto": "ITEM_POKE_BALL", 534 | "type": "Pokeball", 535 | "category": "Pokeball", 536 | "minTrainerLevel": 1 537 | }, 538 | "503": { 539 | "name": "Troy Disk Mossy", 540 | "proto": "ITEM_TROY_DISK_MOSSY", 541 | "type": "Disk", 542 | "category": "Disk", 543 | "minTrainerLevel": 1 544 | } 545 | }, 546 | "questTypes": { 547 | "0": { 548 | "proto": "QUEST_UNSET", 549 | "formatted": "Unset" 550 | }, 551 | "1": { 552 | "proto": "QUEST_FIRST_CATCH_OF_THE_DAY", 553 | "formatted": "First Catch Of The Day" 554 | } 555 | }, 556 | "questRewardTypes": { 557 | "0": { 558 | "proto": "UNSET", 559 | "formatted": "Unset" 560 | }, 561 | "1": { 562 | "proto": "EXPERIENCE", 563 | "formatted": "Experience" 564 | } 565 | }, 566 | "questConditions": { 567 | "0": { 568 | "proto": "UNSET", 569 | "formatted": "Unset" 570 | }, 571 | "1": { 572 | "proto": "WITH_POKEMON_TYPE", 573 | "formatted": "With Pokemon Type" 574 | } 575 | }, 576 | "invasions": { 577 | "0": { 578 | "type": "Unset", 579 | "gender": 0, 580 | "grunt": "Unset", 581 | "first_reward": false, 582 | "second_reward": false, 583 | "third_reward": false, 584 | "active": false, 585 | "encounters": { 586 | "first": [], 587 | "second": [], 588 | "third": [] 589 | } 590 | } 591 | }, 592 | "weather": { 593 | "0": { 594 | "name": "None", 595 | "types": [] 596 | }, 597 | "1": { 598 | "name": "Clear", 599 | "types": [ 600 | "Ground", 601 | "Fire", 602 | "Grass" 603 | ] 604 | } 605 | }, 606 | "translations": { 607 | "de": { 608 | "gender_1": "Männlich", 609 | "gender_2": "Weiblich", 610 | "item_4": "Meisterball", 611 | "lure_503": "Moos", 612 | "move_94": "Knochenkeule", 613 | "poke_0": "Ersatzpokemon", 614 | "poke_1": "Bisasam", 615 | "form_34": "Angriffsform", 616 | "desc_26_50": "Kräftiges Massieren seiner Backentaschen setzt einen süßen Geruch frei. Dafür nimmt man auch gerne einen leichten Elektroschock in Kauf.", 617 | "grunt_5": "Rüpel (Weiblich)", 618 | "grunt_6": "Käfer - Rüpel (Weiblich)", 619 | "character_category_6": "Giovanni", 620 | "weather_4": "Bedeckt", 621 | "weather_icon_4": "☁️" 622 | }, 623 | "en": { 624 | "gender_1": "Male", 625 | "gender_2": "Female", 626 | "poke_0": "Substitute", 627 | "poke_1": "Bulbasaur", 628 | "lure_503": "Mossy", 629 | "item_4": "Master Ball", 630 | "form_34": "Attack", 631 | "desc_26_50": "When you rub its cheeks, a sweet fragrance comes wafting out. However, you'll also get a light shock!", 632 | "grunt_5": "Grunt (Female)", 633 | "grunt_6": "Bug - Grunt (Female)", 634 | "character_category_6": "Giovanni", 635 | "weather_4": "Cloudy", 636 | "weather_icon_4": "☁️" 637 | } 638 | } 639 | } 640 | -------------------------------------------------------------------------------- /tests/rawValues.json: -------------------------------------------------------------------------------- 1 | { 2 | "pokemon": { 3 | "1": { 4 | "pokedexId": 1, 5 | "pokemonName": "Bulbasaur", 6 | "forms": [ 7 | 163, 8 | 897 9 | ], 10 | "defaultFormId": 163, 11 | "types": [ 12 | 4, 13 | 12 14 | ], 15 | "attack": 118, 16 | "defense": 111, 17 | "stamina": 128, 18 | "height": 0.7, 19 | "weight": 6.9, 20 | "quickMoves": [ 21 | 214, 22 | 221 23 | ], 24 | "chargedMoves": [ 25 | 59, 26 | 90, 27 | 118 28 | ], 29 | "eliteChargedMoves": [], 30 | "eliteQuickMoves": [], 31 | "family": 1, 32 | "legendary": false, 33 | "mythic": false, 34 | "ultraBeast": false, 35 | "buddyGroupNumber": 2, 36 | "buddyDistance": 3, 37 | "buddyMegaEnergy": 15, 38 | "thirdMoveStardust": 10000, 39 | "thirdMoveCandy": 25, 40 | "tradable": true, 41 | "transferable": true, 42 | "gymDefenderEligible": true, 43 | "purificationCandy": 3, 44 | "purificationDust": 3000, 45 | "genId": 1, 46 | "sizeSettings": [ 47 | { 48 | "name": "xxsLowerBound", 49 | "value": 0.343 50 | }, 51 | { 52 | "name": "xsLowerBound", 53 | "value": 0.35 54 | }, 55 | { 56 | "name": "mLowerBound", 57 | "value": 0.525 58 | }, 59 | { 60 | "name": "mUpperBound", 61 | "value": 0.875 62 | }, 63 | { 64 | "name": "xlUpperBound", 65 | "value": 1.05 66 | }, 67 | { 68 | "name": "xxlUpperBound", 69 | "value": 1.225 70 | } 71 | ], 72 | "evolutions": [ 73 | { 74 | "evoId": 2, 75 | "formId": 166, 76 | "candyCost": 25 77 | } 78 | ], 79 | "generation": "Kanto", 80 | "little": true 81 | }, 82 | "6": { 83 | "pokedexId": 6, 84 | "pokemonName": "Charizard", 85 | "forms": [ 86 | 178, 87 | 951 88 | ], 89 | "defaultFormId": 178, 90 | "types": [ 91 | 3, 92 | 10 93 | ], 94 | "attack": 223, 95 | "defense": 173, 96 | "stamina": 186, 97 | "height": 1.7, 98 | "weight": 90.5, 99 | "quickMoves": [ 100 | 255, 101 | 269 102 | ], 103 | "chargedMoves": [ 104 | 83, 105 | 103, 106 | 270 107 | ], 108 | "eliteChargedMoves": [ 109 | 24, 110 | 298 111 | ], 112 | "eliteQuickMoves": [ 113 | 204, 114 | 209, 115 | 210 116 | ], 117 | "family": 4, 118 | "bonusCandyCapture": 7, 119 | "bonusStardustCapture": 400, 120 | "purificationCandy": 3, 121 | "purificationDust": 3000, 122 | "legendary": false, 123 | "sizeSettings": [ 124 | { 125 | "name": "xxsLowerBound", 126 | "value": 0.833 127 | }, 128 | { 129 | "name": "xsLowerBound", 130 | "value": 0.85 131 | }, 132 | { 133 | "name": "mLowerBound", 134 | "value": 1.275 135 | }, 136 | { 137 | "name": "mUpperBound", 138 | "value": 2.125 139 | }, 140 | { 141 | "name": "xlUpperBound", 142 | "value": 2.55 143 | }, 144 | { 145 | "name": "xxlUpperBound", 146 | "value": 2.975 147 | } 148 | ], 149 | "mythic": false, 150 | "ultraBeast": false, 151 | "buddyGroupNumber": 3, 152 | "buddyDistance": 3, 153 | "buddyMegaEnergy": 15, 154 | "thirdMoveStardust": 10000, 155 | "thirdMoveCandy": 25, 156 | "tradable": true, 157 | "transferable": true, 158 | "gymDefenderEligible": true, 159 | "genId": 1, 160 | "tempEvolutions": [ 161 | { 162 | "tempEvoId": 2, 163 | "attack": 273, 164 | "defense": 213, 165 | "stamina": 186, 166 | "weight": 110.5, 167 | "firstEnergyCost": 200, 168 | "subsequentEnergyCost": 40, 169 | "types": [ 170 | 10, 171 | 16 172 | ] 173 | }, 174 | { 175 | "tempEvoId": 3, 176 | "attack": 319, 177 | "defense": 212, 178 | "stamina": 186, 179 | "weight": 100.5, 180 | "firstEnergyCost": 200, 181 | "subsequentEnergyCost": 40 182 | } 183 | ], 184 | "generation": "Kanto" 185 | } 186 | }, 187 | "forms": { 188 | "32": { 189 | "formName": "Snowy", 190 | "proto": "CASTFORM_SNOWY", 191 | "formId": 32, 192 | "quickMoves": [ 193 | 221, 194 | 244 195 | ], 196 | "chargedMoves": [ 197 | 39, 198 | 40, 199 | 293 200 | ], 201 | "types": [ 202 | 15 203 | ] 204 | }, 205 | "2507": { 206 | "formName": "Normal", 207 | "proto": "CURSOLA_NORMAL", 208 | "formId": 2507 209 | }, 210 | "2583": { 211 | "formName": "Galarian", 212 | "proto": "SLOWBRO_GALARIAN", 213 | "formId": 2583, 214 | "attack": 182, 215 | "defense": 156, 216 | "stamina": 216, 217 | "height": 1.6, 218 | "weight": 70.5, 219 | "purificationCandy": 3, 220 | "purificationDust": 3000, 221 | "quickMoves": [ 222 | 224, 223 | 235 224 | ], 225 | "chargedMoves": [ 226 | 90, 227 | 106, 228 | 108, 229 | 247, 230 | 367 231 | ], 232 | "types": [ 233 | 4, 234 | 14 235 | ] 236 | } 237 | }, 238 | "types": { 239 | "0": { 240 | "typeId": 0, 241 | "typeName": "None", 242 | "strengths": [], 243 | "weaknesses": [], 244 | "veryWeakAgainst": [], 245 | "immunes": [], 246 | "weakAgainst": [], 247 | "resistances": [] 248 | }, 249 | "1": { 250 | "typeId": 1, 251 | "typeName": "Normal", 252 | "strengths": [], 253 | "weaknesses": [ 254 | 2 255 | ], 256 | "veryWeakAgainst": [ 257 | 8 258 | ], 259 | "immunes": [ 260 | 8 261 | ], 262 | "weakAgainst": [ 263 | 6, 264 | 9 265 | ], 266 | "resistances": [] 267 | } 268 | }, 269 | "moves": { 270 | "17": { 271 | "moveId": 17, 272 | "moveName": "Smog", 273 | "proto": "SMOG", 274 | "fast": false 275 | }, 276 | "72": { 277 | "moveId": 72, 278 | "moveName": "Magnet Bomb", 279 | "proto": "MAGNET_BOMB", 280 | "fast": false, 281 | "type": 9, 282 | "power": 75, 283 | "durationMs": 3000, 284 | "energyDelta": -33, 285 | "pvpPower": 70, 286 | "pvpEnergyDelta": -45 287 | } 288 | }, 289 | "items": { 290 | "1": { 291 | "itemId": 1, 292 | "itemName": "Poke Ball", 293 | "proto": "ITEM_POKE_BALL", 294 | "type": "Pokeball", 295 | "category": "Pokeball", 296 | "minTrainerLevel": 1 297 | }, 298 | "503": { 299 | "itemId": 503, 300 | "itemName": "Troy Disk Mossy", 301 | "proto": "ITEM_TROY_DISK_MOSSY", 302 | "type": "Disk", 303 | "category": "Disk", 304 | "minTrainerLevel": 1 305 | } 306 | }, 307 | "weather": { 308 | "0": { 309 | "weatherId": 0, 310 | "weatherName": "None", 311 | "proto": "NONE", 312 | "types": [] 313 | }, 314 | "1": { 315 | "weatherId": 1, 316 | "weatherName": "Clear", 317 | "proto": "CLEAR", 318 | "types": [ 319 | 5, 320 | 10, 321 | 12 322 | ] 323 | } 324 | } 325 | } -------------------------------------------------------------------------------- /tests/statCalculating.json: -------------------------------------------------------------------------------- 1 | { 2 | "pokemon": { 3 | "1": { 4 | "pokemonName": "Bulbasaur", 5 | "pokedexId": 1, 6 | "types": [ 7 | "Poison", 8 | "Grass" 9 | ], 10 | "attack": 118, 11 | "defense": 111, 12 | "stamina": 128 13 | }, 14 | "2": { 15 | "pokemonName": "Ivysaur", 16 | "pokedexId": 2, 17 | "types": [ 18 | "Poison", 19 | "Grass" 20 | ], 21 | "attack": 151, 22 | "defense": 143, 23 | "stamina": 155 24 | }, 25 | "3": { 26 | "pokemonName": "Venusaur", 27 | "pokedexId": 3, 28 | "types": [ 29 | "Poison", 30 | "Grass" 31 | ], 32 | "attack": 198, 33 | "defense": 189, 34 | "stamina": 190 35 | }, 36 | "4": { 37 | "pokemonName": "Charmander", 38 | "pokedexId": 4, 39 | "types": [ 40 | "Fire" 41 | ], 42 | "attack": 116, 43 | "defense": 93, 44 | "stamina": 118 45 | }, 46 | "5": { 47 | "pokemonName": "Charmeleon", 48 | "pokedexId": 5, 49 | "types": [ 50 | "Fire" 51 | ], 52 | "attack": 158, 53 | "defense": 126, 54 | "stamina": 151 55 | }, 56 | "6": { 57 | "pokemonName": "Charizard", 58 | "pokedexId": 6, 59 | "types": [ 60 | "Flying", 61 | "Fire" 62 | ], 63 | "attack": 223, 64 | "defense": 173, 65 | "stamina": 186 66 | }, 67 | "7": { 68 | "pokemonName": "Squirtle", 69 | "pokedexId": 7, 70 | "types": [ 71 | "Water" 72 | ], 73 | "attack": 94, 74 | "defense": 121, 75 | "stamina": 127 76 | }, 77 | "8": { 78 | "pokemonName": "Wartortle", 79 | "pokedexId": 8, 80 | "types": [ 81 | "Water" 82 | ], 83 | "attack": 126, 84 | "defense": 155, 85 | "stamina": 153 86 | }, 87 | "9": { 88 | "pokemonName": "Blastoise", 89 | "pokedexId": 9, 90 | "types": [ 91 | "Water" 92 | ], 93 | "attack": 171, 94 | "defense": 207, 95 | "stamina": 188 96 | }, 97 | "24": { 98 | "pokemonName": "Arbok", 99 | "pokedexId": 24, 100 | "types": [ 101 | "Poison" 102 | ], 103 | "attack": 167, 104 | "defense": 153, 105 | "stamina": 155 106 | }, 107 | "51": { 108 | "pokemonName": "Dugtrio", 109 | "pokedexId": 51, 110 | "types": [ 111 | "Ground" 112 | ], 113 | "attack": 167, 114 | "defense": 134, 115 | "stamina": 111 116 | }, 117 | "176": { 118 | "pokemonName": "Togetic", 119 | "pokedexId": 176, 120 | "types": [ 121 | "Flying", 122 | "Fairy" 123 | ], 124 | "attack": 139, 125 | "defense": 181, 126 | "stamina": 146 127 | }, 128 | "226": { 129 | "pokemonName": "Mantine", 130 | "pokedexId": 226, 131 | "types": [ 132 | "Flying", 133 | "Water" 134 | ], 135 | "attack": 148, 136 | "defense": 226, 137 | "stamina": 163 138 | }, 139 | "227": { 140 | "pokemonName": "Skarmory", 141 | "pokedexId": 227, 142 | "types": [ 143 | "Flying", 144 | "Steel" 145 | ], 146 | "attack": 148, 147 | "defense": 226, 148 | "stamina": 163 149 | }, 150 | "292": { 151 | "pokemonName": "Shedinja", 152 | "pokedexId": 292, 153 | "types": [ 154 | "Bug", 155 | "Ghost" 156 | ], 157 | "attack": 153, 158 | "defense": 73, 159 | "stamina": 1 160 | }, 161 | "770": { 162 | "pokemonName": "Palossand", 163 | "pokedexId": 770, 164 | "types": [ 165 | "Ground", 166 | "Ghost" 167 | ], 168 | "attack": 178, 169 | "defense": 178, 170 | "stamina": 198 171 | }, 172 | "893": { 173 | "pokemonName": "Zarude", 174 | "pokedexId": 893, 175 | "types": [ 176 | "Grass", 177 | "Dark" 178 | ], 179 | "attack": 242, 180 | "defense": 215, 181 | "stamina": 233 182 | }, 183 | "894": { 184 | "pokemonName": "Regieleki", 185 | "pokedexId": 894, 186 | "types": [ 187 | "Electric" 188 | ], 189 | "attack": 250, 190 | "defense": 125, 191 | "stamina": 190 192 | }, 193 | "895": { 194 | "pokemonName": "Regidrago", 195 | "pokedexId": 895, 196 | "types": [ 197 | "Dragon" 198 | ], 199 | "attack": 202, 200 | "defense": 101, 201 | "stamina": 400 202 | }, 203 | "896": { 204 | "pokemonName": "Glastrier", 205 | "pokedexId": 896, 206 | "types": [ 207 | "Ice" 208 | ], 209 | "attack": 246, 210 | "defense": 223, 211 | "stamina": 225 212 | }, 213 | "897": { 214 | "pokemonName": "Spectrier", 215 | "pokedexId": 897, 216 | "types": [ 217 | "Ghost" 218 | ], 219 | "attack": 273, 220 | "defense": 146, 221 | "stamina": 205 222 | }, 223 | "898": { 224 | "pokemonName": "Calyrex", 225 | "pokedexId": 898, 226 | "types": [ 227 | "Grass", 228 | "Psychic" 229 | ], 230 | "attack": 162, 231 | "defense": 162, 232 | "stamina": 225 233 | } 234 | } 235 | } -------------------------------------------------------------------------------- /tests/templates.test.js: -------------------------------------------------------------------------------- 1 | const { generate } = require('../dist/index') 2 | const defaultRef = require('./defaultValues.json') 3 | const rawRef = require('./rawValues.json') 4 | const customRef = require('./customValues.json') 5 | const statCalculatingRef = require('./statCalculating.json') 6 | 7 | let data 8 | 9 | jest.setTimeout(30_000) 10 | 11 | const testFn = (refData) => { 12 | Object.keys(refData).forEach(category => { 13 | describe(`Testing ${category}`, () => { 14 | Object.keys(refData[category]).forEach(id => { 15 | const knownValue = refData[category][id] 16 | if (category === 'translations') { 17 | test(`Testing ${id} locale`, () => { 18 | Object.keys(knownValue).forEach(localeKey => { 19 | expect(data[category][id][localeKey]).toBe(knownValue[localeKey]) 20 | }) 21 | }) 22 | } else { 23 | test(`Deep comparing values for ${knownValue.name || knownValue.formatted || knownValue.proto || knownValue.type || knownValue}`, () => { 24 | expect(data[category][id]).toEqual(knownValue) 25 | }) 26 | } 27 | }) 28 | }) 29 | }) 30 | } 31 | 32 | describe('Testing Default Template', () => { 33 | beforeAll(async () => { 34 | data = await generate() 35 | }) 36 | testFn(defaultRef) 37 | }) 38 | 39 | describe('Testing Raw Template', () => { 40 | beforeAll(async () => { 41 | data = await generate({ raw: true }) 42 | }) 43 | testFn(rawRef) 44 | }) 45 | 46 | describe('Testing Custom Template', () => { 47 | beforeAll(async () => { 48 | jest.spyOn(console, 'warn').mockImplementation(() => { }) 49 | data = await generate({ 50 | template: { 51 | globalOptions: { 52 | keyJoiner: '_%_', 53 | genderString: true, 54 | includeProtos: true, 55 | }, 56 | pokemon: { 57 | enabled: true, 58 | options: { 59 | keys: { 60 | main: 'pokedexId formId', 61 | quickMoves: 'moveId', 62 | chargedMoves: 'moveId', 63 | }, 64 | customFields: { 65 | attack: 'baseAttack', 66 | defense: 'baseDefense', 67 | stamina: 'baseStamina', 68 | pokedexId: 'id', 69 | pokemonName: 'name', 70 | formId: 'id', 71 | forms: 'form', 72 | }, 73 | customChildObj: { 74 | attack: 'stats', 75 | defense: 'stats', 76 | stamina: 'stats', 77 | }, 78 | processFormsSeparately: true, 79 | }, 80 | template: { 81 | pokedexId: true, 82 | pokemonName: true, 83 | forms: { 84 | formId: true, 85 | formName: true, 86 | isCostume: true, 87 | }, 88 | attack: true, 89 | defense: true, 90 | stamina: true, 91 | quickMoves: "moveName", 92 | chargedMoves: "moveName", 93 | generation: true, 94 | types: { 95 | typeName: true, 96 | typeId: true, 97 | }, 98 | evolutions: { 99 | evoId: true, 100 | formId: true, 101 | genderRequirement: true, 102 | }, 103 | tempEvolutions: 'tempEvoId', 104 | family: true, 105 | little: true, 106 | } 107 | }, 108 | types: { 109 | enabled: true, 110 | options: { 111 | keys: { 112 | main: 'typeId typeName', 113 | }, 114 | }, 115 | template: { 116 | typeName: true, 117 | }, 118 | }, 119 | moves: { 120 | enabled: true, 121 | options: { 122 | keys: { 123 | main: 'moveId moveName power', 124 | }, 125 | }, 126 | template: { 127 | moveName: true, 128 | proto: true, 129 | type: { 130 | typeId: false, 131 | typeName: true, 132 | }, 133 | }, 134 | }, 135 | items: { 136 | enabled: true, 137 | options: { 138 | keys: { 139 | main: 'itemId', 140 | }, 141 | customFields: { 142 | itemId: 'id', 143 | }, 144 | minTrainerLevel: 100, 145 | }, 146 | template: { 147 | itemName: true, 148 | proto: true, 149 | minTrainerLevel: true, 150 | }, 151 | }, 152 | invasions: { 153 | enabled: true, 154 | options: { 155 | keys: { 156 | main: 'grunt type', 157 | encounters: 'position', 158 | }, 159 | customFields: { 160 | first: 'first', 161 | second: 'second', 162 | third: 'third', 163 | }, 164 | }, 165 | template: { 166 | type: true, 167 | gender: true, 168 | grunt: true, 169 | encounters: { 170 | id: true, 171 | formId: true, 172 | position: false, 173 | }, 174 | }, 175 | }, 176 | weather: { 177 | enabled: true, 178 | options: { 179 | keys: { 180 | keyJoiner: '_', 181 | main: 'weatherId', 182 | }, 183 | }, 184 | template: 'weatherName' 185 | }, 186 | translations: { 187 | enabled: true, 188 | options: { 189 | prefix: { 190 | pokemon: 'pokemon_', 191 | forms: 'forms_', 192 | costumes: 'stupid_costumes_', 193 | alignment: 'alignment_', 194 | evolutions: 'big_boi_evos_', 195 | descriptions: 'pokemon_pokedex_descriptions_', 196 | moves: 'fight_club!_', 197 | items: 'cool_helpers_', 198 | weather: 'weather_is_not_accurate_', 199 | types: 'pokemon%types_', 200 | grunts: 'grunt_', 201 | characterCategories: 'character_category_', 202 | lures: 'amazing_pokemon_spawners_', 203 | throwTypes: 'whatIsAThrowType_', 204 | }, 205 | questVariables: { 206 | prefix: '{{', 207 | suffix: '}}', 208 | }, 209 | masterfileLocale: 'de', 210 | manualTranslations: true, 211 | mergeCategories: true, 212 | }, 213 | locales: { 214 | de: true, 215 | en: true, 216 | fr: true, 217 | }, 218 | template: { 219 | pokemon: { 220 | names: true, 221 | forms: true, 222 | descriptions: true, 223 | }, 224 | moves: true, 225 | items: true, 226 | types: true, 227 | weather: true, 228 | }, 229 | }, 230 | }, 231 | }) 232 | }) 233 | testFn(customRef) 234 | }) 235 | 236 | describe('Testing Stat Calculations', () => { 237 | beforeAll(async () => { 238 | data = await generate({ 239 | template: { 240 | globalOptions: { 241 | keyJoiner: '_', 242 | customChildObj: {}, 243 | customFields: {}, 244 | includeProtos: true, 245 | }, 246 | pokemon: { 247 | enabled: true, 248 | options: { 249 | keys: { 250 | main: 'pokedexId', 251 | }, 252 | includeUnset: true, 253 | allUnset: true, 254 | includeEstimatedPokemon: true, 255 | nonProtoGMStats: true, 256 | pokeApiIds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 24, 51, 176, 226, 227, 292, 770, 893, 895, 896, 897, 898], 257 | }, 258 | template: { 259 | pokedexId: true, 260 | pokemonName: true, 261 | attack: true, 262 | defense: true, 263 | stamina: true, 264 | types: 'typeName', 265 | }, 266 | }, 267 | }, 268 | }) 269 | }) 270 | testFn(statCalculatingRef) 271 | }) 272 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "CommonJS", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "outDir": "./dist", 8 | "rootDir": "./src", 9 | "removeComments": true, 10 | "strict": true, 11 | "strictNullChecks": false, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "esModuleInterop": true, 15 | "skipLibCheck": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "resolveJsonModule": true, 18 | }, 19 | "include": [ 20 | "./src/**/*.ts" 21 | ], 22 | "exclude": [ 23 | "node_modules", 24 | "devWrapper.ts" 25 | ] 26 | } --------------------------------------------------------------------------------