diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index cb5c8a1b6f3..00000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: "[Feature]" -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** - - -**Describe the Feature** - - - -**Additional context** - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..1543236ff37 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,39 @@ +name: Feature Request +description: Suggest an idea for this project +title: "[Feature] " +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request! + - type: textarea + id: relation + attributes: + label: Is your feature request related to a problem? Please describe. + description: Clear and concise description of what the problem is. + placeholder: E.g. "I'm always frustrated when [...]" + validations: + required: true + - type: markdown + attributes: + value: | + --- + - type: textarea + id: description + attributes: + label: Describe the Feature + description: A clear and concise description of what you want to happen. Add a link to the feature if it is an existing move/ability/etc. + validations: + required: true + - type: markdown + attributes: + value: | + --- + - type: textarea + id: additional-context + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. + validations: + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e4f1fa5ebf5..acfe341c075 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,12 +5,12 @@ ## What are the changes? -## Why am I doing these changes? +## Why am I doing these changes the user will see? -## What did change? +## What are the changes from a developer perspective? @@ -30,6 +30,7 @@ - [ ] The PR is self-contained and cannot be split into smaller PRs? - [ ] Have I provided a clear explanation of the changes? - [ ] Have I considered writing automated tests for the issue? +- [ ] If I have text, did I add placeholders for them in locales? - [ ] Have I tested the changes (manually)? - [ ] Are all unit tests still passing? (`npm run test`) - [ ] Are the changes visual? diff --git a/README.md b/README.md index ade4adc2c59..77246fa4402 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to - kyledove - Brumirage - pkmn_realidea (Paid Commissions) + - IceJkai ### 🎨 Trainer Portraits - pkmn_realidea (Paid Commissions) diff --git a/public/audio/bgm/battle_hoenn_champion.mp3 b/public/audio/bgm/battle_hoenn_champion_g5.mp3 similarity index 100% rename from public/audio/bgm/battle_hoenn_champion.mp3 rename to public/audio/bgm/battle_hoenn_champion_g5.mp3 diff --git a/public/audio/bgm/battle_hoenn_champion_g6.mp3 b/public/audio/bgm/battle_hoenn_champion_g6.mp3 new file mode 100644 index 00000000000..16ef20a5bea Binary files /dev/null and b/public/audio/bgm/battle_hoenn_champion_g6.mp3 differ diff --git a/public/fonts/Galmuri11.subset.woff2 b/public/fonts/Galmuri11.subset.woff2 new file mode 100644 index 00000000000..0b626506255 Binary files /dev/null and b/public/fonts/Galmuri11.subset.woff2 differ diff --git a/public/fonts/Galmuri9.subset.woff2 b/public/fonts/Galmuri9.subset.woff2 new file mode 100644 index 00000000000..5475af0cdad Binary files /dev/null and b/public/fonts/Galmuri9.subset.woff2 differ diff --git a/public/fonts/PokePT_Wansung.ttf b/public/fonts/PokePT_Wansung.ttf deleted file mode 100644 index cd846107db4..00000000000 Binary files a/public/fonts/PokePT_Wansung.ttf and /dev/null differ diff --git a/public/fonts/PokePT_Wansung.woff2 b/public/fonts/PokePT_Wansung.woff2 new file mode 100644 index 00000000000..02b299ef7a0 Binary files /dev/null and b/public/fonts/PokePT_Wansung.woff2 differ diff --git a/public/fonts/unifont-15.1.05.otf b/public/fonts/unifont-15.1.05.otf deleted file mode 100644 index 3d0dcd3c1a0..00000000000 Binary files a/public/fonts/unifont-15.1.05.otf and /dev/null differ diff --git a/public/fonts/unifont-15.1.05.subset.woff2 b/public/fonts/unifont-15.1.05.subset.woff2 new file mode 100644 index 00000000000..cde898e35bf Binary files /dev/null and b/public/fonts/unifont-15.1.05.subset.woff2 differ diff --git a/public/images/pokemon/exp/back/911.png b/public/images/pokemon/exp/back/911.png index 04010c67444..a15676954fc 100644 Binary files a/public/images/pokemon/exp/back/911.png and b/public/images/pokemon/exp/back/911.png differ diff --git a/public/images/pokemon/exp/back/shiny/911.png b/public/images/pokemon/exp/back/shiny/911.png index 46b513ba014..948f8a3ae12 100644 Binary files a/public/images/pokemon/exp/back/shiny/911.png and b/public/images/pokemon/exp/back/shiny/911.png differ diff --git a/public/images/pokemon/variant/back/426_3.json b/public/images/pokemon/variant/back/426_3.json deleted file mode 100644 index 72c1bf371ad..00000000000 --- a/public/images/pokemon/variant/back/426_3.json +++ /dev/null @@ -1,1532 +0,0 @@ -{ - "textures": [ - { - "image": "426_3.png", - "format": "RGBA8888", - "size": { - "w": 335, - "h": 335 - }, - "scale": 1, - "frames": [ - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 63, - "h": 57 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 57 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 63, - "h": 57 - }, - "frame": { - "x": 0, - "y": 0, - "w": 63, - "h": 57 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 3, - "w": 63, - "h": 58 - }, - "frame": { - "x": 63, - "y": 0, - "w": 63, - "h": 58 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 3, - "w": 63, - "h": 58 - }, - "frame": { - "x": 63, - "y": 0, - "w": 63, - "h": 58 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 64, - "h": 59 - }, - "frame": { - "x": 126, - "y": 0, - "w": 64, - "h": 59 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 64, - "h": 59 - }, - "frame": { - "x": 126, - "y": 0, - "w": 64, - "h": 59 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 64, - "h": 59 - }, - "frame": { - "x": 126, - "y": 0, - "w": 64, - "h": 59 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 64, - "h": 59 - }, - "frame": { - "x": 126, - "y": 0, - "w": 64, - "h": 59 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 67, - "h": 59 - }, - "frame": { - "x": 190, - "y": 0, - "w": 67, - "h": 59 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 2, - "y": 4, - "w": 67, - "h": 59 - }, - "frame": { - "x": 190, - "y": 0, - "w": 67, - "h": 59 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 67, - "h": 59 - }, - "frame": { - "x": 190, - "y": 0, - "w": 67, - "h": 59 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 67, - "h": 59 - }, - "frame": { - "x": 190, - "y": 0, - "w": 67, - "h": 59 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 64, - "h": 60 - }, - "frame": { - "x": 257, - "y": 0, - "w": 64, - "h": 60 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 64, - "h": 60 - }, - "frame": { - "x": 257, - "y": 0, - "w": 64, - "h": 60 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 1, - "w": 64, - "h": 60 - }, - "frame": { - "x": 257, - "y": 0, - "w": 64, - "h": 60 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 1, - "w": 64, - "h": 60 - }, - "frame": { - "x": 257, - "y": 0, - "w": 64, - "h": 60 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 63, - "h": 60 - }, - "frame": { - "x": 0, - "y": 57, - "w": 63, - "h": 60 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 5, - "y": 7, - "w": 63, - "h": 60 - }, - "frame": { - "x": 0, - "y": 57, - "w": 63, - "h": 60 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 62, - "h": 61 - }, - "frame": { - "x": 63, - "y": 58, - "w": 62, - "h": 61 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 62, - "h": 61 - }, - "frame": { - "x": 63, - "y": 58, - "w": 62, - "h": 61 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 8, - "w": 67, - "h": 60 - }, - "frame": { - "x": 125, - "y": 59, - "w": 67, - "h": 60 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 8, - "w": 67, - "h": 60 - }, - "frame": { - "x": 125, - "y": 59, - "w": 67, - "h": 60 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 6, - "w": 64, - "h": 61 - }, - "frame": { - "x": 192, - "y": 59, - "w": 64, - "h": 61 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 6, - "w": 64, - "h": 61 - }, - "frame": { - "x": 192, - "y": 59, - "w": 64, - "h": 61 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 2, - "w": 64, - "h": 61 - }, - "frame": { - "x": 192, - "y": 59, - "w": 64, - "h": 61 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 2, - "w": 64, - "h": 61 - }, - "frame": { - "x": 192, - "y": 59, - "w": 64, - "h": 61 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 1, - "y": 10, - "w": 69, - "h": 60 - }, - "frame": { - "x": 256, - "y": 60, - "w": 69, - "h": 60 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 1, - "y": 10, - "w": 69, - "h": 60 - }, - "frame": { - "x": 256, - "y": 60, - "w": 69, - "h": 60 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 9, - "w": 63, - "h": 62 - }, - "frame": { - "x": 0, - "y": 117, - "w": 63, - "h": 62 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 9, - "w": 63, - "h": 62 - }, - "frame": { - "x": 0, - "y": 117, - "w": 63, - "h": 62 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 63, - "h": 62 - }, - "frame": { - "x": 0, - "y": 117, - "w": 63, - "h": 62 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 5, - "w": 63, - "h": 62 - }, - "frame": { - "x": 0, - "y": 117, - "w": 63, - "h": 62 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 10, - "w": 70, - "h": 60 - }, - "frame": { - "x": 63, - "y": 119, - "w": 70, - "h": 60 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 10, - "w": 70, - "h": 60 - }, - "frame": { - "x": 63, - "y": 119, - "w": 70, - "h": 60 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 1, - "y": 4, - "w": 69, - "h": 61 - }, - "frame": { - "x": 133, - "y": 120, - "w": 69, - "h": 61 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 1, - "y": 4, - "w": 69, - "h": 61 - }, - "frame": { - "x": 133, - "y": 120, - "w": 69, - "h": 61 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 69, - "h": 61 - }, - "frame": { - "x": 133, - "y": 120, - "w": 69, - "h": 61 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 69, - "h": 61 - }, - "frame": { - "x": 133, - "y": 120, - "w": 69, - "h": 61 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 71, - "h": 61 - }, - "frame": { - "x": 202, - "y": 120, - "w": 71, - "h": 61 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 5, - "w": 71, - "h": 61 - }, - "frame": { - "x": 202, - "y": 120, - "w": 71, - "h": 61 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 71, - "h": 61 - }, - "frame": { - "x": 202, - "y": 120, - "w": 71, - "h": 61 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 1, - "w": 71, - "h": 61 - }, - "frame": { - "x": 202, - "y": 120, - "w": 71, - "h": 61 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 62, - "h": 62 - }, - "frame": { - "x": 273, - "y": 120, - "w": 62, - "h": 62 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 62, - "h": 62 - }, - "frame": { - "x": 273, - "y": 120, - "w": 62, - "h": 62 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 62, - "h": 62 - }, - "frame": { - "x": 273, - "y": 120, - "w": 62, - "h": 62 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 62, - "h": 62 - }, - "frame": { - "x": 273, - "y": 120, - "w": 62, - "h": 62 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 64, - "h": 61 - }, - "frame": { - "x": 0, - "y": 179, - "w": 64, - "h": 61 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 6, - "y": 7, - "w": 64, - "h": 61 - }, - "frame": { - "x": 0, - "y": 179, - "w": 64, - "h": 61 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 1, - "y": 11, - "w": 69, - "h": 61 - }, - "frame": { - "x": 64, - "y": 179, - "w": 69, - "h": 61 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 1, - "y": 11, - "w": 69, - "h": 61 - }, - "frame": { - "x": 64, - "y": 179, - "w": 69, - "h": 61 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 11, - "w": 66, - "h": 61 - }, - "frame": { - "x": 133, - "y": 181, - "w": 66, - "h": 61 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 11, - "w": 66, - "h": 61 - }, - "frame": { - "x": 133, - "y": 181, - "w": 66, - "h": 61 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 64, - "h": 62 - }, - "frame": { - "x": 199, - "y": 181, - "w": 64, - "h": 62 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 7, - "w": 64, - "h": 62 - }, - "frame": { - "x": 199, - "y": 181, - "w": 64, - "h": 62 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 64, - "h": 62 - }, - "frame": { - "x": 199, - "y": 181, - "w": 64, - "h": 62 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 64, - "h": 62 - }, - "frame": { - "x": 199, - "y": 181, - "w": 64, - "h": 62 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 69, - "h": 62 - }, - "frame": { - "x": 263, - "y": 182, - "w": 69, - "h": 62 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 2, - "y": 6, - "w": 69, - "h": 62 - }, - "frame": { - "x": 263, - "y": 182, - "w": 69, - "h": 62 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 69, - "h": 62 - }, - "frame": { - "x": 263, - "y": 182, - "w": 69, - "h": 62 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 2, - "y": 2, - "w": 69, - "h": 62 - }, - "frame": { - "x": 263, - "y": 182, - "w": 69, - "h": 62 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 66, - "h": 62 - }, - "frame": { - "x": 0, - "y": 240, - "w": 66, - "h": 62 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 7, - "w": 66, - "h": 62 - }, - "frame": { - "x": 0, - "y": 240, - "w": 66, - "h": 62 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 3, - "w": 66, - "h": 62 - }, - "frame": { - "x": 0, - "y": 240, - "w": 66, - "h": 62 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 3, - "y": 3, - "w": 66, - "h": 62 - }, - "frame": { - "x": 0, - "y": 240, - "w": 66, - "h": 62 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 64, - "h": 62 - }, - "frame": { - "x": 66, - "y": 240, - "w": 64, - "h": 62 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 64, - "h": 62 - }, - "frame": { - "x": 66, - "y": 240, - "w": 64, - "h": 62 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 63, - "h": 63 - }, - "frame": { - "x": 130, - "y": 242, - "w": 63, - "h": 63 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 5, - "y": 4, - "w": 63, - "h": 63 - }, - "frame": { - "x": 130, - "y": 242, - "w": 63, - "h": 63 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 64, - "h": 63 - }, - "frame": { - "x": 193, - "y": 243, - "w": 64, - "h": 63 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 64, - "h": 63 - }, - "frame": { - "x": 193, - "y": 243, - "w": 64, - "h": 63 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 65, - "h": 63 - }, - "frame": { - "x": 257, - "y": 244, - "w": 65, - "h": 63 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 71, - "h": 72 - }, - "spriteSourceSize": { - "x": 6, - "y": 5, - "w": 65, - "h": 63 - }, - "frame": { - "x": 257, - "y": 244, - "w": 65, - "h": 63 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:5a260d2a6180d3a382a352c076369741:e26f8902d45a6e7b427d7dd3cc82c868:b81a75872f1ff6f03d9cd643388500e3$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/426_3.png b/public/images/pokemon/variant/back/426_3.png deleted file mode 100644 index e994a22f917..00000000000 Binary files a/public/images/pokemon/variant/back/426_3.png and /dev/null differ diff --git a/public/images/trainer/aqua_admin_f.json b/public/images/trainer/aqua_admin_f.json deleted file mode 100644 index 35e6e43edc3..00000000000 --- a/public/images/trainer/aqua_admin_f.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "aqua_admin_f.png", - "format": "RGBA8888", - "size": { - "w": 80, - "h": 80 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - }, - "frame": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:efd07ff3ed1e610150a4b8ca18974343:d9b85b9eb11182e9e4669e2bd8b08694:72b7b50231708a9486d5f315824e4df1$" - } -} diff --git a/public/images/trainer/aqua_admin_m.json b/public/images/trainer/archer.json similarity index 95% rename from public/images/trainer/aqua_admin_m.json rename to public/images/trainer/archer.json index f52412623cc..892a137d61c 100644 --- a/public/images/trainer/aqua_admin_m.json +++ b/public/images/trainer/archer.json @@ -1,7 +1,7 @@ { "textures": [ { - "image": "aqua_admin_m.png", + "image": "archer.png", "format": "RGBA8888", "size": { "w": 80, @@ -38,4 +38,4 @@ "version": "3.0", "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" } -} \ No newline at end of file +} diff --git a/public/images/trainer/rocket_admin_m.png b/public/images/trainer/archer.png similarity index 100% rename from public/images/trainer/rocket_admin_m.png rename to public/images/trainer/archer.png diff --git a/public/images/trainer/rocket_admin_m.json b/public/images/trainer/ariana.json similarity index 95% rename from public/images/trainer/rocket_admin_m.json rename to public/images/trainer/ariana.json index a1ad82dd9a2..d7d24afed6e 100644 --- a/public/images/trainer/rocket_admin_m.json +++ b/public/images/trainer/ariana.json @@ -1,7 +1,7 @@ { "textures": [ { - "image": "rocket_admin_m.png", + "image": "ariana.png", "format": "RGBA8888", "size": { "w": 80, diff --git a/public/images/trainer/rocket_admin_f.png b/public/images/trainer/ariana.png similarity index 100% rename from public/images/trainer/rocket_admin_f.png rename to public/images/trainer/ariana.png diff --git a/public/images/trainer/magma_admin_f.json b/public/images/trainer/bryony.json similarity index 95% rename from public/images/trainer/magma_admin_f.json rename to public/images/trainer/bryony.json index 1065b9f9cdd..7d939fff58f 100644 --- a/public/images/trainer/magma_admin_f.json +++ b/public/images/trainer/bryony.json @@ -1,7 +1,7 @@ { "textures": [ { - "image": "magma_admin_m.png", + "image": "bryony.png", "format": "RGBA8888", "size": { "w": 80, @@ -38,4 +38,4 @@ "version": "3.0", "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" } -} \ No newline at end of file +} diff --git a/public/images/trainer/flare_admin_f.png b/public/images/trainer/bryony.png similarity index 100% rename from public/images/trainer/flare_admin_f.png rename to public/images/trainer/bryony.png diff --git a/public/images/trainer/courtney.json b/public/images/trainer/courtney.json new file mode 100644 index 00000000000..de55e91eb85 --- /dev/null +++ b/public/images/trainer/courtney.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "courtney.png", + "format": "RGBA8888", + "size": { + "w": 52, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 52, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 52, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 52, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/courtney.png b/public/images/trainer/courtney.png new file mode 100644 index 00000000000..991e7ac006f Binary files /dev/null and b/public/images/trainer/courtney.png differ diff --git a/public/images/trainer/flare_admin_m.json b/public/images/trainer/flare_admin_m.json deleted file mode 100644 index 4228fac6af0..00000000000 --- a/public/images/trainer/flare_admin_m.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "flare_admin_m.png", - "format": "RGBA8888", - "size": { - "w": 80, - "h": 80 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - }, - "frame": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:c30bf82452209a923f4becf13d275a9a:a6355b09f92c9c0388d0b919010f587f:0638dbf213f8a974eb5af76eb1e5ddeb$" - } -} diff --git a/public/images/trainer/galactic_admin_f.json b/public/images/trainer/galactic_admin_f.json deleted file mode 100644 index eae1da8fff1..00000000000 --- a/public/images/trainer/galactic_admin_f.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "galactic_admin_f.png", - "format": "RGBA8888", - "size": { - "w": 80, - "h": 80 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - }, - "frame": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" - } -} \ No newline at end of file diff --git a/public/images/trainer/galactic_admin_m.json b/public/images/trainer/galactic_admin_m.json deleted file mode 100644 index f404c2247e9..00000000000 --- a/public/images/trainer/galactic_admin_m.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "galactic_admin_m.png", - "format": "RGBA8888", - "size": { - "w": 80, - "h": 80 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - }, - "frame": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:3012867f03f02c4ee67a8ab3ad5a000e:77a5f60f1adc158664b3b2ee17bf30fe:7e8259b5177c0a76e5d02d6bdc66affe$" - } -} diff --git a/public/images/trainer/flare_admin_m copy.json b/public/images/trainer/jupiter.json similarity index 95% rename from public/images/trainer/flare_admin_m copy.json rename to public/images/trainer/jupiter.json index 1e39a3fcb03..45147497eaa 100644 --- a/public/images/trainer/flare_admin_m copy.json +++ b/public/images/trainer/jupiter.json @@ -1,7 +1,7 @@ { "textures": [ { - "image": "flare_admin_f.png", + "image": "jupiter.png", "format": "RGBA8888", "size": { "w": 80, @@ -38,4 +38,4 @@ "version": "3.0", "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" } -} \ No newline at end of file +} diff --git a/public/images/trainer/jupiter.png b/public/images/trainer/jupiter.png new file mode 100644 index 00000000000..1a460bbfecc Binary files /dev/null and b/public/images/trainer/jupiter.png differ diff --git a/public/images/trainer/magma_admin_f.png b/public/images/trainer/magma_admin_f.png deleted file mode 100644 index 979fe6ae837..00000000000 Binary files a/public/images/trainer/magma_admin_f.png and /dev/null differ diff --git a/public/images/trainer/magma_admin_m.json b/public/images/trainer/magma_admin_m.json deleted file mode 100644 index 9961b8dcd3b..00000000000 --- a/public/images/trainer/magma_admin_m.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "magma_admin_f.png", - "format": "RGBA8888", - "size": { - "w": 80, - "h": 80 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - }, - "frame": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:f63ad48affc076f60fae78992c96a2bf:80928b32710abcb28c07c6fc5a425d99:3b961d8852b62aaf24ceb2030c036515$" - } -} diff --git a/public/images/trainer/mars.json b/public/images/trainer/mars.json new file mode 100644 index 00000000000..0f2dcded0c3 --- /dev/null +++ b/public/images/trainer/mars.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "mars.png", + "format": "RGBA8888", + "size": { + "w": 80, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/galactic_admin_f.png b/public/images/trainer/mars.png similarity index 100% rename from public/images/trainer/galactic_admin_f.png rename to public/images/trainer/mars.png diff --git a/public/images/trainer/matt.json b/public/images/trainer/matt.json new file mode 100644 index 00000000000..b749e740d00 --- /dev/null +++ b/public/images/trainer/matt.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "matt.png", + "format": "RGBA8888", + "size": { + "w": 80, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/aqua_admin_m.png b/public/images/trainer/matt.png similarity index 100% rename from public/images/trainer/aqua_admin_m.png rename to public/images/trainer/matt.png diff --git a/public/images/trainer/petrel.json b/public/images/trainer/petrel.json new file mode 100644 index 00000000000..182a402cae4 --- /dev/null +++ b/public/images/trainer/petrel.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "petrel.png", + "format": "RGBA8888", + "size": { + "w": 80, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/petrel.png b/public/images/trainer/petrel.png new file mode 100644 index 00000000000..d4043735316 Binary files /dev/null and b/public/images/trainer/petrel.png differ diff --git a/public/images/trainer/plasma_sage.json b/public/images/trainer/plasma_sage.json deleted file mode 100644 index 05e75141ec0..00000000000 --- a/public/images/trainer/plasma_sage.json +++ /dev/null @@ -1,2120 +0,0 @@ -{ - "textures": [ - { - "image": "plasma_sage.png", - "format": "RGBA8888", - "size": { - "w": 250, - "h": 250 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 11, - "y": 1, - "w": 56, - "h": 79 - }, - "frame": { - "x": 1, - "y": 1, - "w": 56, - "h": 79 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 11, - "y": 1, - "w": 56, - "h": 79 - }, - "frame": { - "x": 1, - "y": 1, - "w": 56, - "h": 79 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 11, - "y": 1, - "w": 56, - "h": 79 - }, - "frame": { - "x": 1, - "y": 1, - "w": 56, - "h": 79 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 11, - "y": 1, - "w": 56, - "h": 79 - }, - "frame": { - "x": 1, - "y": 1, - "w": 56, - "h": 79 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 13, - "y": 2, - "w": 55, - "h": 78 - }, - "frame": { - "x": 1, - "y": 82, - "w": 55, - "h": 78 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 14, - "y": 2, - "w": 54, - "h": 78 - }, - "frame": { - "x": 59, - "y": 1, - "w": 54, - "h": 78 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 14, - "y": 2, - "w": 54, - "h": 78 - }, - "frame": { - "x": 59, - "y": 1, - "w": 54, - "h": 78 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 16, - "y": 3, - "w": 53, - "h": 77 - }, - "frame": { - "x": 1, - "y": 162, - "w": 53, - "h": 77 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 16, - "y": 4, - "w": 53, - "h": 76 - }, - "frame": { - "x": 115, - "y": 1, - "w": 53, - "h": 76 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 16, - "y": 4, - "w": 53, - "h": 76 - }, - "frame": { - "x": 115, - "y": 1, - "w": 53, - "h": 76 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 16, - "y": 4, - "w": 53, - "h": 76 - }, - "frame": { - "x": 115, - "y": 1, - "w": 53, - "h": 76 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 16, - "y": 4, - "w": 53, - "h": 76 - }, - "frame": { - "x": 115, - "y": 1, - "w": 53, - "h": 76 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 16, - "y": 4, - "w": 53, - "h": 76 - }, - "frame": { - "x": 115, - "y": 1, - "w": 53, - "h": 76 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 16, - "y": 4, - "w": 53, - "h": 76 - }, - "frame": { - "x": 115, - "y": 1, - "w": 53, - "h": 76 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 16, - "y": 4, - "w": 53, - "h": 76 - }, - "frame": { - "x": 115, - "y": 1, - "w": 53, - "h": 76 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 20, - "y": 3, - "w": 49, - "h": 77 - }, - "frame": { - "x": 170, - "y": 1, - "w": 49, - "h": 77 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 20, - "y": 2, - "w": 47, - "h": 78 - }, - "frame": { - "x": 58, - "y": 82, - "w": 47, - "h": 78 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 56, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 56, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 107, - "y": 81, - "w": 46, - "h": 80 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 104, - "y": 163, - "w": 46, - "h": 80 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 155, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 203, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 203, - "y": 80, - "w": 46, - "h": 80 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 0, - "w": 46, - "h": 80 - }, - "frame": { - "x": 152, - "y": 163, - "w": 46, - "h": 80 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 21, - "y": 1, - "w": 46, - "h": 79 - }, - "frame": { - "x": 200, - "y": 162, - "w": 46, - "h": 79 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:98df2be457c75de554b8ce6c2f5ff582:93e2cb242dc211e2485f87e4db93af88:0c38bc008e5ede7b6331c02b8220846f$" - } -} \ No newline at end of file diff --git a/public/images/trainer/plasma_sage.png b/public/images/trainer/plasma_sage.png deleted file mode 100644 index 6c866562989..00000000000 Binary files a/public/images/trainer/plasma_sage.png and /dev/null differ diff --git a/public/images/trainer/proton.json b/public/images/trainer/proton.json new file mode 100644 index 00000000000..87a2e8e2b40 --- /dev/null +++ b/public/images/trainer/proton.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "proton.png", + "format": "RGBA8888", + "size": { + "w": 80, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/proton.png b/public/images/trainer/proton.png new file mode 100644 index 00000000000..6bf48a0c57c Binary files /dev/null and b/public/images/trainer/proton.png differ diff --git a/public/images/trainer/rocket_admin_f.json b/public/images/trainer/rocket_admin_f.json deleted file mode 100644 index 7c154785ba3..00000000000 --- a/public/images/trainer/rocket_admin_f.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "rocket_admin_f.png", - "format": "RGBA8888", - "size": { - "w": 80, - "h": 80 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 80, - "h": 80 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - }, - "frame": { - "x": 0, - "y": 0, - "w": 80, - "h": 80 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" - } -} \ No newline at end of file diff --git a/public/images/trainer/saturn.json b/public/images/trainer/saturn.json new file mode 100644 index 00000000000..29fad6324c9 --- /dev/null +++ b/public/images/trainer/saturn.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "saturn.png", + "format": "RGBA8888", + "size": { + "w": 80, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/galactic_admin_m.png b/public/images/trainer/saturn.png similarity index 100% rename from public/images/trainer/galactic_admin_m.png rename to public/images/trainer/saturn.png diff --git a/public/images/trainer/shelly.json b/public/images/trainer/shelly.json new file mode 100644 index 00000000000..7761779864a --- /dev/null +++ b/public/images/trainer/shelly.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "shelly.png", + "format": "RGBA8888", + "size": { + "w": 80, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/aqua_admin_f.png b/public/images/trainer/shelly.png similarity index 100% rename from public/images/trainer/aqua_admin_f.png rename to public/images/trainer/shelly.png diff --git a/public/images/trainer/tabitha.json b/public/images/trainer/tabitha.json new file mode 100644 index 00000000000..8360f33bffe --- /dev/null +++ b/public/images/trainer/tabitha.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "tabitha.png", + "format": "RGBA8888", + "size": { + "w": 80, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/magma_admin_m.png b/public/images/trainer/tabitha.png similarity index 100% rename from public/images/trainer/magma_admin_m.png rename to public/images/trainer/tabitha.png diff --git a/public/images/trainer/xerosic.json b/public/images/trainer/xerosic.json new file mode 100644 index 00000000000..fd9604637ac --- /dev/null +++ b/public/images/trainer/xerosic.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "xerosic.png", + "format": "RGBA8888", + "size": { + "w": 80, + "h": 80 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 80, + "h": 80 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + }, + "frame": { + "x": 0, + "y": 0, + "w": 80, + "h": 80 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:831f5748dad92911b10a1cb358ee2dae:a3bf81bbaa3b49cad5e0e549cf94563b:bb6befc9383c9c08837183ae2a7a80c1$" + } +} diff --git a/public/images/trainer/flare_admin_m.png b/public/images/trainer/xerosic.png similarity index 100% rename from public/images/trainer/flare_admin_m.png rename to public/images/trainer/xerosic.png diff --git a/public/images/types_ja.json b/public/images/types_ja.json new file mode 100644 index 00000000000..95fd645829e --- /dev/null +++ b/public/images/types_ja.json @@ -0,0 +1,440 @@ +{ + "textures": [ + { + "image": "types_ja.png", + "format": "RGBA8888", + "size": { + "w": 32, + "h": 280 + }, + "scale": 1, + "frames": [ + { + "filename": "unknown", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + } + }, + { + "filename": "bug", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 14, + "w": 32, + "h": 14 + } + }, + { + "filename": "dark", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 28, + "w": 32, + "h": 14 + } + }, + { + "filename": "dragon", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 42, + "w": 32, + "h": 14 + } + }, + { + "filename": "electric", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 56, + "w": 32, + "h": 14 + } + }, + { + "filename": "fairy", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 70, + "w": 32, + "h": 14 + } + }, + { + "filename": "fighting", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 84, + "w": 32, + "h": 14 + } + }, + { + "filename": "fire", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 98, + "w": 32, + "h": 14 + } + }, + { + "filename": "flying", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 112, + "w": 32, + "h": 14 + } + }, + { + "filename": "ghost", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 126, + "w": 32, + "h": 14 + } + }, + { + "filename": "grass", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 140, + "w": 32, + "h": 14 + } + }, + { + "filename": "ground", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 154, + "w": 32, + "h": 14 + } + }, + { + "filename": "ice", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 168, + "w": 32, + "h": 14 + } + }, + { + "filename": "normal", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 182, + "w": 32, + "h": 14 + } + }, + { + "filename": "poison", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 196, + "w": 32, + "h": 14 + } + }, + { + "filename": "psychic", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 210, + "w": 32, + "h": 14 + } + }, + { + "filename": "rock", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 224, + "w": 32, + "h": 14 + } + }, + { + "filename": "steel", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 238, + "w": 32, + "h": 14 + } + }, + { + "filename": "water", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 252, + "w": 32, + "h": 14 + } + }, + { + "filename": "stellar", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 14 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 14 + }, + "frame": { + "x": 0, + "y": 266, + "w": 32, + "h": 14 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$" + } +} \ No newline at end of file diff --git a/public/images/types_ja.png b/public/images/types_ja.png new file mode 100644 index 00000000000..e60d8e071aa Binary files /dev/null and b/public/images/types_ja.png differ diff --git a/public/images/ui/legacy/pbinfo_stat.json b/public/images/ui/legacy/pbinfo_stat.json index b7da47fc192..a956f81150d 100644 --- a/public/images/ui/legacy/pbinfo_stat.json +++ b/public/images/ui/legacy/pbinfo_stat.json @@ -176,6 +176,27 @@ "w": 12, "h": 6 } + }, + { + "filename": "HP", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 9, + "h": 8 + }, + "spriteSourceSize": { + "x": 1, + "y": 2, + "w": 8, + "h": 6 + }, + "frame": { + "x": 112, + "y": 0, + "w": 8, + "h": 6 + } } ] } diff --git a/public/images/ui/legacy/pbinfo_stat.png b/public/images/ui/legacy/pbinfo_stat.png index 62ec3758772..18b0ca314de 100644 Binary files a/public/images/ui/legacy/pbinfo_stat.png and b/public/images/ui/legacy/pbinfo_stat.png differ diff --git a/public/images/ui/legacy/pbinfo_stat_numbers.json b/public/images/ui/legacy/pbinfo_stat_numbers.json index fa7d757990f..c106d1cf41e 100644 --- a/public/images/ui/legacy/pbinfo_stat_numbers.json +++ b/public/images/ui/legacy/pbinfo_stat_numbers.json @@ -281,6 +281,27 @@ "w": 9, "h": 8 } + }, + { + "filename": "empty", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 1, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 1, + "h": 8 + }, + "frame": { + "x": 117, + "y": 0, + "w": 1, + "h": 8 + } } ] } diff --git a/public/images/ui/legacy/pbinfo_stat_numbers.png b/public/images/ui/legacy/pbinfo_stat_numbers.png index ee1453b2107..b02dfbec72f 100644 Binary files a/public/images/ui/legacy/pbinfo_stat_numbers.png and b/public/images/ui/legacy/pbinfo_stat_numbers.png differ diff --git a/public/images/ui/pbinfo_stat.json b/public/images/ui/pbinfo_stat.json index f431e5afafd..4ec46c467b1 100644 --- a/public/images/ui/pbinfo_stat.json +++ b/public/images/ui/pbinfo_stat.json @@ -176,6 +176,27 @@ "w": 13, "h": 7 } + }, + { + "filename": "HP", + "rotated": false, + "trimmed": true, + "sourceSize": { + "w": 9, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 1, + "w": 9, + "h": 7 + }, + "frame": { + "x": 120, + "y": 0, + "w": 9, + "h": 7 + } } ] } diff --git a/public/images/ui/pbinfo_stat.png b/public/images/ui/pbinfo_stat.png index 46169091e7c..1c20ed70e82 100644 Binary files a/public/images/ui/pbinfo_stat.png and b/public/images/ui/pbinfo_stat.png differ diff --git a/public/images/ui/pbinfo_stat_numbers.json b/public/images/ui/pbinfo_stat_numbers.json index ec4f7117bb7..ccd49bbbb79 100644 --- a/public/images/ui/pbinfo_stat_numbers.json +++ b/public/images/ui/pbinfo_stat_numbers.json @@ -281,6 +281,27 @@ "w": 9, "h": 8 } + }, + { + "filename": "empty", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 1, + "h": 8 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 1, + "h": 8 + }, + "frame": { + "x": 117, + "y": 0, + "w": 1, + "h": 8 + } } ] } diff --git a/public/images/ui/pbinfo_stat_numbers.png b/public/images/ui/pbinfo_stat_numbers.png index c778ba99273..1465f8b7a64 100644 Binary files a/public/images/ui/pbinfo_stat_numbers.png and b/public/images/ui/pbinfo_stat_numbers.png differ diff --git a/src/@types/common.ts b/src/@types/common.ts new file mode 100644 index 00000000000..6868d766008 --- /dev/null +++ b/src/@types/common.ts @@ -0,0 +1,3 @@ +import BattleScene from "#app/battle-scene.js"; + +export type ConditionFn = (scene: BattleScene, args?: any[]) => boolean; diff --git a/src/account.ts b/src/account.ts index 7fd1d208496..6682e3e0b7c 100644 --- a/src/account.ts +++ b/src/account.ts @@ -8,7 +8,7 @@ export interface UserInfo { googleId: string; } -export let loggedInUser: UserInfo = null; +export let loggedInUser: UserInfo | null = null; // This is a random string that is used to identify the client session - unique per session (tab or window) so that the game will only save on the one that the server is expecting export const clientSessionId = Utils.randomString(32); @@ -30,11 +30,13 @@ export function updateUserInfo(): Promise<[boolean, integer]> { loggedInUser.lastSessionSlot = lastSessionSlot; // Migrate old data from before the username was appended [ "data", "sessionData", "sessionData1", "sessionData2", "sessionData3", "sessionData4" ].map(d => { - if (localStorage.hasOwnProperty(d)) { - if (localStorage.hasOwnProperty(`${d}_${loggedInUser.username}`)) { - localStorage.setItem(`${d}_${loggedInUser.username}_bak`, localStorage.getItem(`${d}_${loggedInUser.username}`)); + const lsItem = localStorage.getItem(d); + if (lsItem && !!loggedInUser?.username) { + const lsUserItem = localStorage.getItem(`${d}_${loggedInUser.username}`); + if (lsUserItem) { + localStorage.setItem(`${d}_${loggedInUser.username}_bak`, lsUserItem); } - localStorage.setItem(`${d}_${loggedInUser.username}`, localStorage.getItem(d)); + localStorage.setItem(`${d}_${loggedInUser.username}`, lsItem); localStorage.removeItem(d); } }); diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b0967dfebda..06b714cb2f3 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -106,8 +106,8 @@ export default class BattleScene extends SceneBase { public inputController: InputsController; public uiInputs: UiInputs; - public sessionPlayTime: integer = null; - public lastSavePlayTime: integer = null; + public sessionPlayTime: integer | null = null; + public lastSavePlayTime: integer | null = null; public masterVolume: number = 0.5; public bgmVolume: number = 1; public seVolume: number = 1; @@ -122,6 +122,7 @@ export default class BattleScene extends SceneBase { public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1"; public enableMoveInfo: boolean = true; public enableRetries: boolean = false; + public hideIvs: boolean = false; /** * Determines the condition for a notification should be shown for Candy Upgrades * - 0 = 'Off' @@ -192,8 +193,8 @@ export default class BattleScene extends SceneBase { private phaseQueuePrependSpliceIndex: integer; private nextCommandPhaseQueue: Phase[]; - private currentPhase: Phase; - private standbyPhase: Phase; + private currentPhase: Phase | null; + private standbyPhase: Phase | null; public field: Phaser.GameObjects.Container; public fieldUI: Phaser.GameObjects.Container; public charSprite: CharSprite; @@ -213,7 +214,7 @@ export default class BattleScene extends SceneBase { public score: integer; public lockModifierTiers: boolean; public trainer: Phaser.GameObjects.Sprite; - public lastEnemyTrainer: Trainer; + public lastEnemyTrainer: Trainer | null; public currentBattle: Battle; public pokeballCounts: PokeballCounts; public money: integer; @@ -251,7 +252,7 @@ export default class BattleScene extends SceneBase { public spritePipeline: SpritePipeline; private bgm: AnySound; - private bgmResumeTimer: Phaser.Time.TimerEvent; + private bgmResumeTimer: Phaser.Time.TimerEvent | null; private bgmCache: Set = new Set(); private playTimeTimer: Phaser.Time.TimerEvent; @@ -700,7 +701,11 @@ export default class BattleScene extends SceneBase { hasExpSprite(key: string): boolean { const keyMatch = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/g.exec(key); - let k = keyMatch[4]; + if (!keyMatch) { + return false; + } + + let k = keyMatch[4]!; if (keyMatch[2]) { k += "s"; } @@ -723,7 +728,7 @@ export default class BattleScene extends SceneBase { return this.party; } - getPlayerPokemon(): PlayerPokemon { + getPlayerPokemon(): PlayerPokemon | undefined { return this.getPlayerField().find(p => p.isActive()); } @@ -740,7 +745,7 @@ export default class BattleScene extends SceneBase { return this.currentBattle?.enemyParty || []; } - getEnemyPokemon(): EnemyPokemon { + getEnemyPokemon(): EnemyPokemon | undefined { return this.getEnemyField().find(p => p.isActive()); } @@ -764,6 +769,27 @@ export default class BattleScene extends SceneBase { : ret; } + /** + * Used in doubles battles to redirect moves from one pokemon to another when one faints or is removed from the field + * @param removedPokemon {@linkcode Pokemon} the pokemon that is being removed from the field (flee, faint), moves to be redirected FROM + * @param allyPokemon {@linkcode Pokemon} the pokemon that will have the moves be redirected TO + */ + redirectPokemonMoves(removedPokemon: Pokemon, allyPokemon: Pokemon): void { + // failsafe: if not a double battle just return + if (this.currentBattle.double === false) { + return; + } + if (allyPokemon?.isActive(true)) { + let targetingMovePhase: MovePhase; + do { + targetingMovePhase = this.findPhase(mp => mp instanceof MovePhase && mp.targets.length === 1 && mp.targets[0] === removedPokemon.getBattlerIndex() && mp.pokemon.isPlayer() !== allyPokemon.isPlayer()) as MovePhase; + if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) { + targetingMovePhase.targets[0] = allyPokemon.getBattlerIndex(); + } + } while (targetingMovePhase); + } + } + /** * Returns the ModifierBar of this scene, which is declared private and therefore not accessible elsewhere * @returns {ModifierBar} @@ -782,12 +808,12 @@ export default class BattleScene extends SceneBase { return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles; } - getPokemonById(pokemonId: integer): Pokemon { + getPokemonById(pokemonId: integer): Pokemon | null { const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); - return findInParty(this.getParty()) || findInParty(this.getEnemyParty()); + return (findInParty(this.getParty()) || findInParty(this.getEnemyParty())) ?? null; } - addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon { + addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon { const pokemon = new PlayerPokemon(this, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource); if (postProcess) { postProcess(pokemon); @@ -957,7 +983,8 @@ export default class BattleScene extends SceneBase { p.destroy(); } - this.currentBattle = null; + //@ts-ignore - allowing `null` for currentBattle causes a lot of trouble + this.currentBattle = null; // TODO: resolve ts-ignore this.biomeWaveText.setText(startingWave.toString()); this.biomeWaveText.setVisible(false); @@ -1021,14 +1048,14 @@ export default class BattleScene extends SceneBase { } } - newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle { + newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle | null { const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave; const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1); - let newDouble: boolean; + let newDouble: boolean | undefined; let newBattleType: BattleType; - let newTrainer: Trainer; + let newTrainer: Trainer | undefined; - let battleConfig: FixedBattleConfig = null; + let battleConfig: FixedBattleConfig | null = null; this.resetSeed(newWaveIndex); @@ -1038,7 +1065,7 @@ export default class BattleScene extends SceneBase { battleConfig = this.gameMode.getFixedBattle(newWaveIndex); newDouble = battleConfig.double; newBattleType = battleConfig.battleType; - this.executeWithSeedOffset(() => newTrainer = battleConfig.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8); + this.executeWithSeedOffset(() => newTrainer = battleConfig?.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8); if (newTrainer) { this.field.add(newTrainer); } @@ -1078,7 +1105,7 @@ export default class BattleScene extends SceneBase { playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); newDouble = !Utils.randSeedInt(doubleChance.value); } else if (newBattleType === BattleType.TRAINER) { - newDouble = newTrainer.variant === TrainerVariant.DOUBLE; + newDouble = newTrainer?.variant === TrainerVariant.DOUBLE; } } else if (!battleConfig) { newDouble = !!double; @@ -1110,27 +1137,11 @@ export default class BattleScene extends SceneBase { //this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6)); if (!waveIndex && lastBattle) { - let isNewBiome = !(lastBattle.waveIndex % 10) || ((this.gameMode.hasShortBiomes || this.gameMode.isDaily) && (lastBattle.waveIndex % 50) === 49); - if (!isNewBiome && this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 10) < 9) { - let w = lastBattle.waveIndex - ((lastBattle.waveIndex % 10) - 1); - let biomeWaves = 1; - while (w < lastBattle.waveIndex) { - let wasNewBiome = false; - this.executeWithSeedOffset(() => { - wasNewBiome = !Utils.randSeedInt(6 - biomeWaves); - }, w << 4); - if (wasNewBiome) { - biomeWaves = 1; - } else { - biomeWaves++; - } - w++; - } - - this.executeWithSeedOffset(() => { - isNewBiome = !Utils.randSeedInt(6 - biomeWaves); - }, lastBattle.waveIndex << 4); - } + const isWaveIndexMultipleOfTen = !(lastBattle.waveIndex % 10); + const isEndlessOrDaily = this.gameMode.hasShortBiomes || this.gameMode.isDaily; + const isEndlessFifthWave = this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 5) === 0; + const isWaveIndexMultipleOfFiftyMinusOne = (lastBattle.waveIndex % 50) === 49; + const isNewBiome = isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne); const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS; this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy()); this.trySpreadPokerus(); @@ -1309,7 +1320,7 @@ export default class BattleScene extends SceneBase { return 5; } - let isBoss: boolean; + let isBoss: boolean | undefined; if (forceBoss || (species && (species.subLegendary || species.legendary || species.mythical))) { isBoss = true; } else { @@ -1608,7 +1619,7 @@ export default class BattleScene extends SceneBase { randomSpecies(waveIndex: integer, level: integer, fromArenaPool?: boolean, speciesFilter?: PokemonSpeciesFilter, filterAllEvolutions?: boolean): PokemonSpecies { if (fromArenaPool) { - return this.arena.randomSpecies(waveIndex, level,null , getPartyLuckValue(this.party)); + return this.arena.randomSpecies(waveIndex, level, undefined , getPartyLuckValue(this.party)); } const filteredSpecies = speciesFilter ? [...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => { if (!filterAllEvolutions) { @@ -1792,8 +1803,10 @@ export default class BattleScene extends SceneBase { return 13.950; case "battle_johto_champion": //B2W2 Johto Champion Battle return 23.498; - case "battle_hoenn_champion": //B2W2 Hoenn Champion Battle + case "battle_hoenn_champion_g5": //B2W2 Hoenn Champion Battle return 11.328; + case "battle_hoenn_champion_g6": //ORAS Hoenn Champion Battle + return 11.762; case "battle_sinnoh_champion": //B2W2 Sinnoh Champion Battle return 12.235; case "battle_champion_alder": //BW Unova Champion Battle @@ -1964,11 +1977,11 @@ export default class BattleScene extends SceneBase { } /* Phase Functions */ - getCurrentPhase(): Phase { + getCurrentPhase(): Phase | null { return this.currentPhase; } - getStandbyPhase(): Phase { + getStandbyPhase(): Phase | null { return this.standbyPhase; } @@ -2046,7 +2059,10 @@ export default class BattleScene extends SceneBase { } if (this.phaseQueuePrepend.length) { while (this.phaseQueuePrepend.length) { - this.phaseQueue.unshift(this.phaseQueuePrepend.pop()); + const poppedPhase = this.phaseQueuePrepend.pop(); + if (poppedPhase) { + this.phaseQueue.unshift(poppedPhase); + } } } if (!this.phaseQueue.length) { @@ -2054,23 +2070,26 @@ export default class BattleScene extends SceneBase { // Clear the conditionalQueue if there are no phases left in the phaseQueue this.conditionalQueue = []; } - this.currentPhase = this.phaseQueue.shift(); + + this.currentPhase = this.phaseQueue.shift() ?? null; // Check if there are any conditional phases queued if (this.conditionalQueue?.length) { // Retrieve the first conditional phase from the queue const conditionalPhase = this.conditionalQueue.shift(); // Evaluate the condition associated with the phase - if (conditionalPhase[0]()) { + if (conditionalPhase?.[0]()) { // If the condition is met, add the phase to the phase queue this.pushPhase(conditionalPhase[1]); - } else { + } else if (conditionalPhase) { // If the condition is not met, re-add the phase back to the front of the conditional queue this.conditionalQueue.unshift(conditionalPhase); + } else { + console.warn("condition phase is undefined/null!", conditionalPhase); } } - this.currentPhase.start(); + this.currentPhase?.start(); } overridePhase(phase: Phase): boolean { @@ -2085,7 +2104,7 @@ export default class BattleScene extends SceneBase { return true; } - findPhase(phaseFilter: (phase: Phase) => boolean): Phase { + findPhase(phaseFilter: (phase: Phase) => boolean): Phase | undefined { return this.phaseQueue.find(phaseFilter); } @@ -2144,7 +2163,7 @@ export default class BattleScene extends SceneBase { * @param promptDelay optional param for MessagePhase constructor * @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue */ - queueMessage(message: string, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer, defer?: boolean) { + queueMessage(message: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null, defer?: boolean | null) { const phase = new MessagePhase(this, message, callbackDelay, prompt, promptDelay); if (!defer) { // adds to the end of PhaseQueuePrepend @@ -2180,7 +2199,10 @@ export default class BattleScene extends SceneBase { return Math.floor(moneyValue / 10) * 10; } - addModifier(modifier: Modifier, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean): Promise { + addModifier(modifier: Modifier | null, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean): Promise { + if (!modifier) { + return Promise.resolve(false); + } return new Promise(resolve => { let success = false; const soundName = modifier.type.soundName; @@ -2200,7 +2222,7 @@ export default class BattleScene extends SceneBase { } } else if (!virtual) { const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier); - this.queueMessage(`The stack for this item is full.\n You will receive ${defaultModifierType.name} instead.`, null, true); + this.queueMessage(`The stack for this item is full.\n You will receive ${defaultModifierType.name} instead.`, undefined, true); return this.addModifier(defaultModifierType.newModifier(), ignoreUpdate, playSound, false, instant).then(success => resolve(success)); } @@ -2300,7 +2322,7 @@ export default class BattleScene extends SceneBase { return new Promise(resolve => { const source = itemModifier.pokemonId ? itemModifier.getPokemon(target.scene) : null; const cancelled = new Utils.BooleanHolder(false); - Utils.executeIf(source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source, cancelled)).then(() => { + Utils.executeIf(!!source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source! /* checked in condition*/, cancelled)).then(() => { if (cancelled.value) { return resolve(false); } @@ -2381,7 +2403,7 @@ export default class BattleScene extends SceneBase { } party.forEach((enemyPokemon: EnemyPokemon, i: integer) => { - const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer.config.isBoss); + const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer?.config.isBoss); let upgradeChance = 32; if (isBoss) { upgradeChance /= 2; @@ -2389,9 +2411,9 @@ export default class BattleScene extends SceneBase { if (isFinalBoss) { upgradeChance /= 8; } - const modifierChance = this.gameMode.getEnemyModifierChance(isBoss); + const modifierChance = this.gameMode.getEnemyModifierChance(isBoss!); // TODO: is this bang correct? let pokemonModifierChance = modifierChance; - if (this.currentBattle.battleType === BattleType.TRAINER) + if (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer) pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); // eslint-disable-line let count = 0; for (let c = 0; c < chances; c++) { @@ -2510,7 +2532,7 @@ export default class BattleScene extends SceneBase { return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m)); } - findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier { + findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier | undefined { return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m)); } @@ -2546,7 +2568,7 @@ export default class BattleScene extends SceneBase { return appliedModifiers; } - applyModifier(modifierType: Constructor, player: boolean = true, ...args: any[]): PersistentModifier { + applyModifier(modifierType: Constructor, player: boolean = true, ...args: any[]): PersistentModifier | null { const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); for (const modifier of modifiers) { if (modifier.apply(args)) { @@ -2633,7 +2655,7 @@ export default class BattleScene extends SceneBase { initFinalBossPhaseTwo(pokemon: Pokemon): void { if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) { this.fadeOutBgm(Utils.fixedInt(2000), false); - this.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, null, () => { + this.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, undefined, () => { this.addEnemyModifier(getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as PersistentModifier, false, true); pokemon.generateAndPopulateMoveset(1); this.setFieldScale(0.75); diff --git a/src/battle.ts b/src/battle.ts index e8a1323fc4c..0b3b256f3df 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -39,7 +39,7 @@ export interface TurnCommand { } interface TurnCommands { - [key: integer]: TurnCommand + [key: integer]: TurnCommand | null } export default class Battle { @@ -47,8 +47,8 @@ export default class Battle { public waveIndex: integer; public battleType: BattleType; public battleSpec: BattleSpec; - public trainer: Trainer; - public enemyLevels: integer[]; + public trainer: Trainer | null; + public enemyLevels: integer[] | undefined; public enemyParty: EnemyPokemon[]; public seenEnemyPartyMemberIds: Set; public double: boolean; @@ -62,26 +62,26 @@ export default class Battle { public escapeAttempts: integer; public lastMove: Moves; public battleSeed: string; - private battleSeedState: string; + private battleSeedState: string | null; public moneyScattered: number; - public lastUsedPokeball: PokeballType; + public lastUsedPokeball: PokeballType | null; public playerFaints: number; // The amount of times pokemon on the players side have fainted public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted private rngCounter: integer = 0; - constructor(gameMode: GameMode, waveIndex: integer, battleType: BattleType, trainer: Trainer, double: boolean) { + constructor(gameMode: GameMode, waveIndex: integer, battleType: BattleType, trainer?: Trainer, double?: boolean) { this.gameMode = gameMode; this.waveIndex = waveIndex; this.battleType = battleType; - this.trainer = trainer; + this.trainer = trainer!; //TODO: is this bang correct? this.initBattleSpec(); this.enemyLevels = battleType !== BattleType.TRAINER ? new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave()) - : trainer.getPartyLevels(this.waveIndex); + : trainer?.getPartyLevels(this.waveIndex); this.enemyParty = []; this.seenEnemyPartyMemberIds = new Set(); - this.double = double; + this.double = double!; //TODO: is this bang correct? this.enemySwitchCounter = 0; this.turn = 0; this.playerParticipantIds = new Set(); @@ -159,6 +159,7 @@ export default class Battle { addPostBattleLoot(enemyPokemon: EnemyPokemon): void { this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferrable, false).map(i => { const ret = i as PokemonHeldItemModifier; + //@ts-ignore - this is awful to fix/change ret.pokemonId = null; return ret; })); @@ -177,7 +178,7 @@ export default class Battle { const userLocale = navigator.language || "en-US"; const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale); const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount }); - scene.queueMessage(message, null, true); + scene.queueMessage(message, undefined, true); scene.currentBattle.moneyScattered = 0; } @@ -200,16 +201,16 @@ export default class Battle { scene.updateScoreText(); } - getBgmOverride(scene: BattleScene): string { + getBgmOverride(scene: BattleScene): string | null { const battlers = this.enemyParty.slice(0, this.getBattlerCount()); if (this.battleType === BattleType.TRAINER) { - if (!this.started && this.trainer.config.encounterBgm && this.trainer.getEncounterMessages()?.length) { - return `encounter_${this.trainer.getEncounterBgm()}`; + if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) { + return `encounter_${this.trainer?.getEncounterBgm()}`; } if (scene.musicPreference === 0) { - return this.trainer.getBattleBgm(); + return this.trainer?.getBattleBgm()!; // TODO: is this bang correct? } else { - return this.trainer.getMixedBattleBgm(); + return this.trainer?.getMixedBattleBgm()!; // TODO: is this bang correct? } } else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) { return "end_summit"; @@ -382,7 +383,7 @@ export default class Battle { export class FixedBattle extends Battle { constructor(scene: BattleScene, waveIndex: integer, config: FixedBattleConfig) { - super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : null, config.double); + super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : undefined, config.double); if (config.getEnemyParty) { this.enemyParty = config.getEnemyParty(scene); } @@ -425,22 +426,28 @@ export class FixedBattleConfig { } } + /** * Helper function to generate a random trainer for evil team trainers and the elite 4/champion * @param trainerPool The TrainerType or list of TrainerTypes that can possibly be generated * @param randomGender whether or not to randomly (50%) generate a female trainer (for use with evil team grunts) + * @param seedOffset the seed offset to use for the random generation of the trainer * @returns the generated trainer */ -function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], randomGender: boolean = false): GetTrainerFunc { +function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], randomGender: boolean = false, seedOffset: number = 0): GetTrainerFunc { return (scene: BattleScene) => { const rand = Utils.randSeedInt(trainerPool.length); const trainerTypes: TrainerType[] = []; - for (const trainerPoolEntry of trainerPool) { - const trainerType = Array.isArray(trainerPoolEntry) - ? Utils.randSeedItem(trainerPoolEntry) - : trainerPoolEntry; - trainerTypes.push(trainerType); - } + + scene.executeWithSeedOffset(() => { + for (const trainerPoolEntry of trainerPool) { + const trainerType = Array.isArray(trainerPoolEntry) + ? Utils.randSeedItem(trainerPoolEntry) + : trainerPoolEntry; + trainerTypes.push(trainerType); + } + }, seedOffset); + let trainerGender = TrainerVariant.DEFAULT; if (randomGender) { trainerGender = (Utils.randInt(2) === 0) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT; @@ -486,13 +493,13 @@ export const classicFixedBattles: FixedBattleConfigs = { [64]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT ], true)), [66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_ADMIN, TrainerType.MAGMA_ADMIN, TrainerType.AQUA_ADMIN, TrainerType.GALACTIC_ADMIN, TrainerType.PLASMA_SAGE, TrainerType.FLARE_ADMIN ], true)), + .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ] ], true)), [95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) .setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), [112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT ], true)), [114]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) - .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_ADMIN, TrainerType.MAGMA_ADMIN, TrainerType.AQUA_ADMIN, TrainerType.GALACTIC_ADMIN, TrainerType.PLASMA_SAGE, TrainerType.FLARE_ADMIN ], true)), + .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ] ], true,1)), [115]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE ])), [145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) diff --git a/src/configs/inputs/configHandler.ts b/src/configs/inputs/configHandler.ts index a67c45fd413..45fb033e9fa 100644 --- a/src/configs/inputs/configHandler.ts +++ b/src/configs/inputs/configHandler.ts @@ -20,7 +20,7 @@ export function getKeyWithKeycode(config, keycode) { */ export function getSettingNameWithKeycode(config, keycode) { const key = getKeyWithKeycode(config, keycode); - return config.custom[key]; + return key ? config.custom[key] : null; } /** @@ -32,7 +32,7 @@ export function getSettingNameWithKeycode(config, keycode) { */ export function getIconWithKeycode(config, keycode) { const key = getKeyWithKeycode(config, keycode); - return config.icons[key]; + return key ? config.icons[key] : null; } /** @@ -122,15 +122,21 @@ export function assign(config, settingNameTarget, keycode): boolean { // if it was already bound, we delete the bind if (previousSettingName) { const previousKey = getKeyWithSettingName(config, previousSettingName); - config.custom[previousKey] = -1; + if (previousKey) { + config.custom[previousKey] = -1; + } } // then, we need to delete the current key for this settingName const currentKey = getKeyWithSettingName(config, settingNameTarget); - config.custom[currentKey] = -1; + if (currentKey) { + config.custom[currentKey] = -1; + } // then, the new key is assigned to the new settingName const newKey = getKeyWithKeycode(config, keycode); - config.custom[newKey] = settingNameTarget; + if (newKey) { + config.custom[newKey] = settingNameTarget; + } return true; } @@ -145,8 +151,12 @@ export function swap(config, settingNameTarget, keycode) { const new_key = getKeyWithKeycode(config, keycode); const new_settingName = getSettingNameWithKey(config, new_key); - config.custom[prev_key] = new_settingName; - config.custom[new_key] = prev_settingName; + if (prev_key) { + config.custom[prev_key] = new_settingName; + } + if (new_key) { + config.custom[new_key] = prev_settingName; + } return true; } @@ -161,7 +171,9 @@ export function deleteBind(config, settingName) { if (config.blacklist.includes(key)) { return false; } - config.custom[key] = -1; + if (key) { + config.custom[key] = -1; + } return true; } diff --git a/src/data/ability.ts b/src/data/ability.ts index f3e8270f17b..018c11548be 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -6,7 +6,7 @@ import { BattleStat, getBattleStatName } from "./battle-stat"; import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; import { getPokemonNameWithAffix } from "../messages"; import { Weather, WeatherType } from "./weather"; -import { BattlerTag, GroundedTag } from "./battler-tags"; +import { BattlerTag, GroundedTag, GulpMissileTag } from "./battler-tags"; import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { Gender } from "./gender"; import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit } from "./move"; @@ -120,7 +120,7 @@ export class Ability implements Localizable { type AbAttrApplyFunc = (attr: TAttr, passive: boolean) => boolean | Promise; type AbAttrCondition = (pokemon: Pokemon) => boolean; -type PokemonAttackCondition = (user: Pokemon, target: Pokemon, move: Move) => boolean; +type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; type PokemonStatChangeCondition = (target: Pokemon, statsChanged: BattleStat[], levels: integer) => boolean; @@ -132,11 +132,11 @@ export abstract class AbAttr { this.showAbility = showAbility; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { return null; } @@ -226,7 +226,7 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { } for (const statChangePhase of statChangePhases) { - if (!this.selfTarget && !statChangePhase.getPokemon().summonData) { + if (!this.selfTarget && !statChangePhase.getPokemon()?.summonData) { pokemon.scene.pushPhase(statChangePhase); } else { // TODO: This causes the ability bar to be shown at the wrong time pokemon.scene.unshiftPhase(statChangePhase); @@ -240,7 +240,7 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean; export class PreDefendAbAttr extends AbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { return false; } } @@ -352,14 +352,14 @@ export class PreDefendMoveDamageToOneAbAttr extends ReceivedMoveDamageMultiplier * @see {@linkcode getCondition} */ export class TypeImmunityAbAttr extends PreDefendAbAttr { - private immuneType: Type; - private condition: AbAttrCondition; + private immuneType: Type | null; + private condition: AbAttrCondition | null; - constructor(immuneType: Type, condition?: AbAttrCondition) { + constructor(immuneType: Type | null, condition?: AbAttrCondition) { super(); this.immuneType = immuneType; - this.condition = condition; + this.condition = condition!; // TODO: is this bang correct? } /** @@ -386,7 +386,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { return false; } - getCondition(): AbAttrCondition { + override getCondition(): AbAttrCondition | null { return this.condition; } } @@ -491,11 +491,54 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } export class PostDefendAbAttr extends AbAttr { - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; } } +/** + * Applies the effects of Gulp Missile when the user is hit by an attack. + * @extends PostDefendAbAttr + */ +export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { + constructor() { + super(true); + } + + /** + * Damages the attacker and triggers the secondary effect based on the form or the BattlerTagType. + * @param {Pokemon} pokemon - The defending Pokemon. + * @param passive - n/a + * @param {Pokemon} attacker - The attacking Pokemon. + * @param {Move} move - The move being used. + * @param {HitResult} hitResult - n/a + * @param {any[]} args - n/a + * @returns Whether the effects of the ability are applied. + */ + applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + const battlerTag = pokemon.getTag(GulpMissileTag); + if (!battlerTag || move.category === MoveCategory.STATUS) { + return false; + } + + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled); + + if (!cancelled.value) { + attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), HitResult.OTHER); + } + + if (battlerTag.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ BattleStat.DEF ], -1)); + } else { + attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon); + } + + pokemon.removeTag(battlerTag.tagType); + return true; + } +} + export class PostDefendDisguiseAbAttr extends PostDefendAbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { @@ -838,7 +881,7 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { private chance: integer; private tagType: BattlerTagType; - private turnCount: integer; + private turnCount: integer | undefined; constructor(chance: integer, tagType: BattlerTagType, turnCount?: integer) { super(); @@ -875,7 +918,7 @@ export class PostDefendCritStatChangeAbAttr extends PostDefendAbAttr { } getCondition(): AbAttrCondition { - return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; + return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length !== 0 && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; } } @@ -941,14 +984,19 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { private weatherType: WeatherType; + protected condition: PokemonDefendCondition | null; - constructor(weatherType: WeatherType) { + constructor(weatherType: WeatherType, condition?: PokemonDefendCondition) { super(); this.weatherType = weatherType; + this.condition = condition ?? null; } applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + if (this.condition !== null && !this.condition(pokemon, attacker, move)) { + return false; + } if (!pokemon.scene.arena.weather?.isImmutable()) { return pokemon.scene.arena.trySetWeather(this.weatherType, true); } @@ -1061,7 +1109,7 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { } export class PreAttackAbAttr extends AbAttr { - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean | Promise { + applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean | Promise { return false; } } @@ -1074,7 +1122,7 @@ export class PreAttackAbAttr extends AbAttr { export class MoveEffectChanceMultiplierAbAttr extends AbAttr { private chanceMultiplier: number; - constructor(chanceMultiplier?: number) { + constructor(chanceMultiplier: number) { super(true); this.chanceMultiplier = chanceMultiplier; } @@ -1455,7 +1503,7 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { this.powerMultiplier = powerMultiplier; } - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, defender: Pokemon | null, move: Move, args: any[]): boolean { if (this.condition(pokemon, defender, move)) { (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; @@ -1509,14 +1557,14 @@ export class AllyMoveCategoryPowerBoostAbAttr extends FieldMovePowerBoostAbAttr export class BattleStatMultiplierAbAttr extends AbAttr { private battleStat: BattleStat; private multiplier: number; - private condition: PokemonAttackCondition; + private condition: PokemonAttackCondition | null; constructor(battleStat: BattleStat, multiplier: number, condition?: PokemonAttackCondition) { super(false); this.battleStat = battleStat; this.multiplier = multiplier; - this.condition = condition; + this.condition = condition!; // TODO: is this bang correct? } applyBattleStat(pokemon: Pokemon, passive: boolean, battleStat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise { @@ -1545,7 +1593,7 @@ export class PostAttackAbAttr extends AbAttr { * applying the effect of any inherited class. This can be changed by providing a different {@link attackCondition} to the constructor. See {@link ConfusionOnStatusEffectAbAttr} * for an example of an effect that does not require a damaging move. */ - applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. // If attackRequired is false, we always defer to the secondary requirements. if (this.attackCondition(pokemon, defender, move)) { @@ -1558,18 +1606,18 @@ export class PostAttackAbAttr extends AbAttr { /** * This method is only called after {@link applyPostAttack} has already been applied. Use this for handling checks specific to the ability in question. */ - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; } } export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { - private stealCondition: PokemonAttackCondition; + private stealCondition: PokemonAttackCondition | null; constructor(stealCondition?: PokemonAttackCondition) { super(); - this.stealCondition = stealCondition; + this.stealCondition = stealCondition!; // TODO: is this bang correct? } applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { @@ -1653,12 +1701,12 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { } export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { - private condition: PokemonDefendCondition; + private condition: PokemonDefendCondition | null; constructor(condition?: PokemonDefendCondition) { super(); - this.condition = condition; + this.condition = condition!; // TODO: is this bang correct? } applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { @@ -2153,17 +2201,17 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { } if ( - target.getAbility().hasAttr(UncopiableAbilityAbAttr) && + target!.getAbility().hasAttr(UncopiableAbilityAbAttr) && // Wonder Guard is normally uncopiable so has the attribute, but Trace specifically can copy it - !(pokemon.hasAbility(Abilities.TRACE) && target.getAbility().id === Abilities.WONDER_GUARD) + !(pokemon.hasAbility(Abilities.TRACE) && target!.getAbility().id === Abilities.WONDER_GUARD) ) { return false; } - this.target = target; - this.targetAbilityName = allAbilities[target.getAbility().id].name; - pokemon.summonData.ability = target.getAbility().id; - setAbilityRevealed(target); + this.target = target!; + this.targetAbilityName = allAbilities[target!.getAbility().id].name; + pokemon.summonData.ability = target!.getAbility().id; + setAbilityRevealed(target!); pokemon.updateInfo(); return true; @@ -2210,7 +2258,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt } for (const pokemon of allowedParty) { - if (this.statusEffect.includes(pokemon.status?.effect)) { + if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { pokemon.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); pokemon.resetStatus(false); pokemon.updateInfo(); @@ -2259,17 +2307,18 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { return false; } - let target: Pokemon; + let target: Pokemon = targets[0]; if (targets.length > 1) { pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex); - } else { + } else if (targets.length === 1) { target = targets[0]; + } else { + return false; } if (target.illusion.active) { return false; } - pokemon.summonData.speciesForm = target.getSpeciesForm(); pokemon.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); pokemon.summonData.ability = target.getAbility().id; @@ -2277,7 +2326,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { pokemon.summonData.fusionGender = target.getFusionGender(); pokemon.summonData.stats = [ pokemon.stats[Stat.HP] ].concat(target.stats.slice(1)); pokemon.summonData.battleStats = target.summonData.battleStats.slice(0); - pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m.moveId, m.ppUsed, m.ppUp)); + pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m!.moveId, m!.ppUsed, m!.ppUp)); // TODO: are those bangs correct? pokemon.summonData.types = target.getTypes(); pokemon.scene.playSound("PRSFX- Transform"); @@ -2324,7 +2373,7 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { * @returns {boolean} Returns true if the weather clears, otherwise false. */ applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { - const weatherType = pokemon.scene.arena.weather.weatherType; + const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; // Clear weather only if user's ability matches the weather and no other pokemon has the ability. @@ -2405,18 +2454,18 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { } export class PreStatChangeAbAttr extends AbAttr { - applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreStatChange(pokemon: Pokemon | null, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } export class ProtectStatAbAttr extends PreStatChangeAbAttr { - private protectedStat: BattleStat; + private protectedStat: BattleStat | null; constructor(protectedStat?: BattleStat) { super(); - this.protectedStat = protectedStat; + this.protectedStat = protectedStat!; // TODO: is this bang correct? } applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { @@ -2432,7 +2481,7 @@ export class ProtectStatAbAttr extends PreStatChangeAbAttr { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - statName: this.protectedStat !== undefined ? getBattleStatName(this.protectedStat) : i18next.t("battle:stats") + statName: this.protectedStat ? getBattleStatName(this.protectedStat) : i18next.t("battle:stats") }); } } @@ -2472,7 +2521,7 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { } export class PreSetStatusAbAttr extends AbAttr { - applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2683,7 +2732,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { * @returns Returns true if status damage is blocked */ apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.effects.includes(pokemon.status?.effect)) { + if (pokemon.status && this.effects.includes(pokemon.status.effect)) { cancelled.value = true; return true; } @@ -2722,7 +2771,7 @@ export class IncrementMovePriorityAbAttr extends AbAttr { export class IgnoreContactAbAttr extends AbAttr { } export class PreWeatherEffectAbAttr extends AbAttr { - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather | null, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2753,7 +2802,7 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { constructor(affectsImmutable?: boolean) { super(); - this.affectsImmutable = affectsImmutable; + this.affectsImmutable = affectsImmutable!; // TODO: is this bang correct? } applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { @@ -2804,7 +2853,7 @@ function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition { return false; } const weatherType = pokemon.scene.arena.weather?.weatherType; - return weatherType && weatherTypes.indexOf(weatherType) > -1; + return !!weatherType && weatherTypes.indexOf(weatherType) > -1; }; } @@ -2813,15 +2862,15 @@ function getAnticipationCondition(): AbAttrCondition { for (const opponent of pokemon.getOpponents()) { for (const move of opponent.moveset) { // move is super effective - if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true) >= 2) { + if (move!.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move!.getMove().type, opponent, true) >= 2) { // TODO: is this bang correct? return true; } // move is a OHKO - if (move.getMove().hasAttr(OneHitKOAttr)) { + if (move!.getMove().hasAttr(OneHitKOAttr)) { // TODO: is this bang correct? return true; } // edge case for hidden power, type is computed - if (move.getMove().id === Moves.HIDDEN_POWER) { + if (move!.getMove().id === Moves.HIDDEN_POWER) { // TODO: is this bang correct? const iv_val = Math.floor(((opponent.ivs[Stat.HP] & 1) +(opponent.ivs[Stat.ATK] & 1) * 2 +(opponent.ivs[Stat.DEF] & 1) * 4 @@ -2869,21 +2918,21 @@ export class ForewarnAbAttr extends PostSummonAbAttr { let movePower = 0; for (const opponent of pokemon.getOpponents()) { for (const move of opponent.moveset) { - if (move.getMove() instanceof StatusMove) { + if (move!.getMove() instanceof StatusMove) { // TODO: is this bang correct? movePower = 1; - } else if (move.getMove().hasAttr(OneHitKOAttr)) { + } else if (move!.getMove().hasAttr(OneHitKOAttr)) { // TODO: is this bang correct? movePower = 150; - } else if (move.getMove().id === Moves.COUNTER || move.getMove().id === Moves.MIRROR_COAT || move.getMove().id === Moves.METAL_BURST) { + } else if (move!.getMove().id === Moves.COUNTER || move!.getMove().id === Moves.MIRROR_COAT || move!.getMove().id === Moves.METAL_BURST) { // TODO: are those bangs correct? movePower = 120; - } else if (move.getMove().power === -1) { + } else if (move!.getMove().power === -1) { // TODO: is this bang correct? movePower = 80; } else { - movePower = move.getMove().power; + movePower = move!.getMove().power; // TODO: is this bang correct? } if (movePower > maxPowerSeen) { maxPowerSeen = movePower; - maxMove = move.getName(); + maxMove = move!.getName(); // TODO: is this bang correct? } } } @@ -2944,7 +2993,7 @@ export class PostWeatherLapseAbAttr extends AbAttr { this.weatherTypes = weatherTypes; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather, args: any[]): boolean | Promise { + applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather | null, args: any[]): boolean | Promise { return false; } @@ -3027,7 +3076,7 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { return (pokemon: Pokemon) => { const terrainType = pokemon.scene.arena.terrain?.terrainType; - return terrainType && terrainTypes.indexOf(terrainType) > -1; + return !!terrainType && terrainTypes.indexOf(terrainType) > -1; }; } @@ -3059,7 +3108,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { * @returns Returns true if healed from status, false if not */ applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { - if (this.effects.includes(pokemon.status?.effect)) { + if (pokemon.status && this.effects.includes(pokemon.status.effect)) { if (!pokemon.isFullHp()) { const scene = pokemon.scene; const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; @@ -3295,7 +3344,7 @@ export class FetchBallAbAttr extends PostTurnAbAttr { */ applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { const lastUsed = pokemon.scene.currentBattle.lastUsedPokeball; - if (lastUsed !== null && pokemon.isPlayer) { + if (lastUsed !== null && !!pokemon.isPlayer) { pokemon.scene.pokeballCounts[lastUsed]++; pokemon.scene.currentBattle.lastUsedPokeball = null; pokemon.scene.queueMessage(i18next.t("abilityTriggers:fetchBall", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokeballName: getPokeballName(lastUsed) })); @@ -3576,7 +3625,8 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot; if (postBattleLoot.length) { const randItem = Utils.randSeedItem(postBattleLoot); - if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) { + //@ts-ignore - TODO see below + if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) { // TODO: fix. This is a promise!? postBattleLoot.splice(postBattleLoot.indexOf(randItem), 1); pokemon.scene.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: randItem.type.name })); return true; @@ -3608,7 +3658,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr { * @returns {boolean} Returns true if the weather clears, otherwise false. */ applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const weatherType = pokemon.scene.arena.weather.weatherType; + const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; // Clear weather only if user's ability matches the weather and no other pokemon has the ability. @@ -4171,7 +4221,7 @@ export class BypassSpeedChanceAbAttr extends AbAttr { const turnCommand = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; - const move = allMoves[turnCommand.move?.move]; + const move = turnCommand?.move?.move ?allMoves[turnCommand.move.move] : null; const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; if (isCommandFight && isDamageMove) { @@ -4190,14 +4240,14 @@ export class BypassSpeedChanceAbAttr extends AbAttr { async function applyAbAttrsInternal( attrType: Constructor, - pokemon: Pokemon, + pokemon: Pokemon | null, applyFunc: AbAttrApplyFunc, args: any[], showAbilityInstant: boolean = false, quiet: boolean = false, ) { for (const passive of [false, true]) { - if (!pokemon.canApplyAbility(passive)) { + if (!pokemon?.canApplyAbility(passive)) { continue; } @@ -4245,7 +4295,7 @@ async function applyAbAttrsInternal( } } -export function applyAbAttrs(attrType: Constructor, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { +export function applyAbAttrs(attrType: Constructor, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, cancelled, args), args); } @@ -4255,13 +4305,13 @@ export function applyPostBattleInitAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + pokemon: Pokemon, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise { const simulated = args.length > 1 && args[1]; return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, simulated); } export function applyPostDefendAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise { + pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args); } @@ -4291,12 +4341,12 @@ export function applyFieldBattleStatMultiplierAbAttrs(attrType: Constructor, - pokemon: Pokemon, defender: Pokemon, move: Move, ...args: any[]): Promise { + pokemon: Pokemon, defender: Pokemon | null, move: Move, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args); } export function applyPostAttackAbAttrs(attrType: Constructor, - pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise { + pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, defender, move, hitResult, args), args); } @@ -4326,7 +4376,7 @@ export function applyPreSwitchOutAbAttrs(attrType: Constructor, - pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args); } @@ -4336,7 +4386,7 @@ export function applyPostStatChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + pokemon: Pokemon, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { const simulated = args.length > 1 && args[1]; return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, effect, cancelled, args), args, false, !simulated); } @@ -4347,7 +4397,7 @@ export function applyPreApplyBattlerTagAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + pokemon: Pokemon, weather: Weather | null, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, weather, cancelled, args), args, true); } @@ -4362,7 +4412,7 @@ export function applyPostWeatherChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: Weather, ...args: any[]): Promise { + pokemon: Pokemon, weather: Weather | null, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherLapse(pokemon, passive, weather, args), args); } @@ -4651,8 +4701,8 @@ export function initAbilities() { .attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPD, 1) .ignorable(), new Ability(Abilities.RIVALRY, 4) - .attr(MovePowerBoostAbAttr, (user, target, move) => user.gender !== Gender.GENDERLESS && target.gender !== Gender.GENDERLESS && user.gender === target.gender, 1.25, true) - .attr(MovePowerBoostAbAttr, (user, target, move) => user.gender !== Gender.GENDERLESS && target.gender !== Gender.GENDERLESS && user.gender !== target.gender, 0.75), + .attr(MovePowerBoostAbAttr, (user, target, move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender === target?.gender, 1.25, true) + .attr(MovePowerBoostAbAttr, (user, target, move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender !== target?.gender, 0.75), new Ability(Abilities.STEADFAST, 4) .attr(FlinchStatChangeAbAttr, BattleStat.SPD, 1), new Ability(Abilities.SNOW_CLOAK, 4) @@ -4742,7 +4792,8 @@ export function initAbilities() { .attr(IgnoreOpponentStatChangesAbAttr) .ignorable(), new Ability(Abilities.TINTED_LENS, 4) - .attr(DamageBoostAbAttr, 2, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) <= 0.5), + //@ts-ignore + .attr(DamageBoostAbAttr, 2, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) <= 0.5), // TODO: fix TS issues new Ability(Abilities.FILTER, 4) .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75) .ignorable(), @@ -4824,9 +4875,9 @@ export function initAbilities() { .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.isFullHp(), 0.5) .ignorable(), new Ability(Abilities.TOXIC_BOOST, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.PHYSICAL && (user.status?.effect === StatusEffect.POISON || user.status?.effect === StatusEffect.TOXIC), 1.5), + .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.PHYSICAL && (user?.status?.effect === StatusEffect.POISON || user?.status?.effect === StatusEffect.TOXIC), 1.5), new Ability(Abilities.FLARE_BOOST, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.SPECIAL && user.status?.effect === StatusEffect.BURN, 1.5), + .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.SPECIAL && user?.status?.effect === StatusEffect.BURN, 1.5), new Ability(Abilities.HARVEST, 5) .attr( PostTurnLootAbAttr, @@ -4859,7 +4910,8 @@ export function initAbilities() { .attr(WonderSkinAbAttr) .ignorable(), new Ability(Abilities.ANALYTIC, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => !!target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command !== Command.FIGHT, 1.3), + //@ts-ignore + .attr(MovePowerBoostAbAttr, (user, target, move) => !!target?.getLastXMoves(1).find(m => m.turn === target?.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command !== Command.FIGHT, 1.3), // TODO fix TS issues new Ability(Abilities.ILLUSION, 5) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) @@ -5017,7 +5069,7 @@ export function initAbilities() { new Ability(Abilities.WATER_COMPACTION, 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, BattleStat.DEF, 2), new Ability(Abilities.MERCILESS, 7) - .attr(ConditionalCritAbAttr, (user, target, move) => target.status?.effect === StatusEffect.TOXIC || target.status?.effect === StatusEffect.POISON), + .attr(ConditionalCritAbAttr, (user, target, move) => target?.status?.effect === StatusEffect.TOXIC || target?.status?.effect === StatusEffect.POISON), new Ability(Abilities.SHIELDS_DOWN, 7) .attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) @@ -5029,7 +5081,8 @@ export function initAbilities() { .bypassFaint() .partial(), new Ability(Abilities.STAKEOUT, 7) - .attr(MovePowerBoostAbAttr, (user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.POKEMON, 2), + //@ts-ignore + .attr(MovePowerBoostAbAttr, (user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.POKEMON, 2), // TODO: fix TS issues new Ability(Abilities.WATER_BUBBLE, 7) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5) .attr(MoveTypePowerBoostAbAttr, Type.WATER, 2) @@ -5169,7 +5222,8 @@ export function initAbilities() { new Ability(Abilities.PRISM_ARMOR, 7) .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75), new Ability(Abilities.NEUROFORCE, 7) - .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 1.25), + //@ts-ignore + .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 1.25), // TODO: fix TS issues new Ability(Abilities.INTREPID_SWORD, 8) .attr(PostSummonStatChangeAbAttr, BattleStat.ATK, 1, true) .condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)), @@ -5194,7 +5248,11 @@ export function initAbilities() { .attr(UnsuppressableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .attr(NoFusionAbilityAbAttr) - .unimplemented(), + .attr(UncopiableAbilityAbAttr) + .attr(UnswappableAbilityAbAttr) + .attr(PostDefendGulpMissileAbAttr) + // Does not transform when Surf/Dive misses/is protected + .partial(), new Ability(Abilities.STALWART, 8) .attr(BlockRedirectAbAttr), new Ability(Abilities.STEAM_ENGINE, 8) @@ -5204,7 +5262,7 @@ export function initAbilities() { .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5) .ignorable(), new Ability(Abilities.SAND_SPIT, 8) - .attr(PostDefendWeatherChangeAbAttr, WeatherType.SANDSTORM), + .attr(PostDefendWeatherChangeAbAttr, WeatherType.SANDSTORM, (target, user, move) => move.category !== MoveCategory.STATUS), new Ability(Abilities.ICE_SCALES, 8) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.category === MoveCategory.SPECIAL, 0.5) .ignorable(), @@ -5252,8 +5310,10 @@ export function initAbilities() { .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .ignorable(), new Ability(Abilities.HUNGER_SWITCH, 8) - .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1) - .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0) + //@ts-ignore + .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1) // TODO: fix ts-ignore + //@ts-ignore + .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0) // TODO: fix ts-ignore .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index fd72ab21026..7c67271b0dc 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -25,12 +25,12 @@ export enum ArenaTagSide { export abstract class ArenaTag { public tagType: ArenaTagType; public turnCount: integer; - public sourceMove: Moves; - public sourceId: integer; + public sourceMove?: Moves; + public sourceId?: integer; public side: ArenaTagSide; - constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) { + constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) { this.tagType = tagType; this.turnCount = turnCount; this.sourceMove = sourceMove; @@ -56,7 +56,7 @@ export abstract class ArenaTag { return this.turnCount < 1 || !!(--this.turnCount); } - getMoveName(): string { + getMoveName(): string | null { return this.sourceMove ? allMoves[this.sourceMove].name : null; @@ -75,9 +75,14 @@ export class MistTag extends ArenaTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { - arena.scene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + if (this.sourceId) { + const source = arena.scene.getPokemonById(this.sourceId); + + if (!quiet && source) { + arena.scene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + } else if (!quiet) { + console.warn("Failed to get source for MistTag onAdd"); + } } } @@ -280,8 +285,14 @@ class MatBlockTag extends ConditionalProtectTag { } onAdd(arena: Arena) { - const source = arena.scene.getPokemonById(this.sourceId); - arena.scene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + if (this.sourceId) { + const source = arena.scene.getPokemonById(this.sourceId); + if (source) { + arena.scene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + } else { + console.warn("Failed to get source for MatBlockTag onAdd"); + } + } } } @@ -303,6 +314,39 @@ class CraftyShieldTag extends ConditionalProtectTag { } } +/** + * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Lucky_Chant_(move) Lucky Chant}. + * Prevents critical hits against the tag's side. + */ +export class NoCritTag extends ArenaTag { + /** + * Constructor method for the NoCritTag class + * @param turnCount `integer` the number of turns this effect lasts + * @param sourceMove {@linkcode Moves} the move that created this effect + * @param sourceId `integer` the ID of the {@linkcode Pokemon} that created this effect + * @param side {@linkcode ArenaTagSide} the side to which this effect belongs + */ + constructor(turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide) { + super(ArenaTagType.NO_CRIT, turnCount, sourceMove, sourceId, side); + } + + /** Queues a message upon adding this effect to the field */ + onAdd(arena: Arena): void { + arena.scene.queueMessage(i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, { + moveName: this.getMoveName() + })); + } + + /** Queues a message upon removing this effect from the field */ + onRemove(arena: Arena): void { + const source = arena.scene.getPokemonById(this.sourceId!); // TODO: is this bang correct? + arena.scene.queueMessage(i18next.t("arenaTag:noCritOnRemove", { + pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined), + moveName: this.getMoveName() + })); + } +} + /** * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Wish_(move) Wish}. * Heals the Pokémon in the user's position the turn after Wish is used. @@ -317,10 +361,16 @@ class WishTag extends ArenaTag { } onAdd(arena: Arena): void { - const user = arena.scene.getPokemonById(this.sourceId); - this.battlerIndex = user.getBattlerIndex(); - this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); - this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1); + if (this.sourceId) { + const user = arena.scene.getPokemonById(this.sourceId); + if (user) { + this.battlerIndex = user.getBattlerIndex(); + this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); + this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1); + } else { + console.warn("Failed to get source for WishTag onAdd"); + } + } } onRemove(arena: Arena): void { @@ -461,8 +511,8 @@ class SpikesTag extends ArenaTrapTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (!quiet && source) { arena.scene.queueMessage(i18next.t("arenaTag:spikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); } } @@ -506,8 +556,8 @@ class ToxicSpikesTag extends ArenaTrapTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (!quiet && source) { arena.scene.queueMessage(i18next.t("arenaTag:toxicSpikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); } } @@ -556,7 +606,7 @@ class ToxicSpikesTag extends ArenaTrapTag { class DelayedAttackTag extends ArenaTag { public targetIndex: BattlerIndex; - constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, targetIndex: BattlerIndex) { + constructor(tagType: ArenaTagType, sourceMove: Moves | undefined, sourceId: integer, targetIndex: BattlerIndex) { super(tagType, 3, sourceMove, sourceId); this.targetIndex = targetIndex; @@ -566,7 +616,7 @@ class DelayedAttackTag extends ArenaTag { const ret = super.lapse(arena); if (!ret) { - arena.scene.unshiftPhase(new MoveEffectPhase(arena.scene, this.sourceId, [ this.targetIndex ], new PokemonMove(this.sourceMove, 0, 0, true))); + arena.scene.unshiftPhase(new MoveEffectPhase(arena.scene, this.sourceId!, [ this.targetIndex ], new PokemonMove(this.sourceMove!, 0, 0, true))); // TODO: are those bangs correct? } return ret; @@ -588,8 +638,8 @@ class StealthRockTag extends ArenaTrapTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (!quiet && source) { arena.scene.queueMessage(i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor() })); } } @@ -597,7 +647,7 @@ class StealthRockTag extends ArenaTrapTag { getDamageHpRatio(pokemon: Pokemon): number { const effectiveness = pokemon.getAttackTypeEffectiveness(Type.ROCK, undefined, true); - let damageHpRatio: number; + let damageHpRatio: number = 0; switch (effectiveness) { case 0: @@ -663,8 +713,8 @@ class StickyWebTag extends ArenaTrapTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (!quiet && source) { arena.scene.queueMessage(i18next.t("arenaTag:stickyWebOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); } } @@ -702,7 +752,10 @@ export class TrickRoomTag extends ArenaTag { } onAdd(arena: Arena): void { - arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(arena.scene.getPokemonById(this.sourceId)) })); + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (source) { + arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + } } onRemove(arena: Arena): void { @@ -749,8 +802,8 @@ class TailwindTag extends ArenaTag { arena.scene.queueMessage(i18next.t(`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); } - const source = arena.scene.getPokemonById(this.sourceId); - const party = source.isPlayer() ? source.scene.getPlayerField() : source.scene.getEnemyField(); + const source = arena.scene.getPokemonById(this.sourceId!); //TODO: this bang is questionable! + const party = (source?.isPlayer() ? source.scene.getPlayerField() : source?.scene.getEnemyField()) ?? []; for (const pokemon of party) { // Apply the CHARGED tag to party members with the WIND_POWER ability @@ -791,7 +844,7 @@ class HappyHourTag extends ArenaTag { } } -export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag { +export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { switch (tagType) { case ArenaTagType.MIST: return new MistTag(turnCount, sourceId, side); @@ -803,6 +856,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new MatBlockTag(sourceId, side); case ArenaTagType.CRAFTY_SHIELD: return new CraftyShieldTag(sourceId, side); + case ArenaTagType.NO_CRIT: + return new NoCritTag(turnCount, sourceMove!, sourceId, side); // TODO: is this bang correct? case ArenaTagType.MUD_SPORT: return new MudSportTag(turnCount, sourceId); case ArenaTagType.WATER_SPORT: @@ -813,7 +868,7 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new ToxicSpikesTag(sourceId, side); case ArenaTagType.FUTURE_SIGHT: case ArenaTagType.DOOM_DESIRE: - return new DelayedAttackTag(tagType, sourceMove, sourceId, targetIndex); + return new DelayedAttackTag(tagType, sourceMove, sourceId, targetIndex!); // TODO:questionable bang case ArenaTagType.WISH: return new WishTag(turnCount, sourceId, side); case ArenaTagType.STEALTH_ROCK: @@ -834,5 +889,7 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new TailwindTag(turnCount, sourceId, side); case ArenaTagType.HAPPY_HOUR: return new HappyHourTag(turnCount, sourceId, side); + default: + return null; } } diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index d4dbb8ec350..d9fc87c67c7 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -128,7 +128,7 @@ export class AnimConfig { for (const fte of Object.keys(frameTimedEvents)) { const timedEvents: AnimTimedEvent[] = []; for (const te of frameTimedEvents[fte]) { - let timedEvent: AnimTimedEvent; + let timedEvent: AnimTimedEvent | undefined; switch (te.eventType) { case "AnimTimedSoundEvent": timedEvent = new AnimTimedSoundEvent(te.frameIndex, te.resourceName, te); @@ -140,7 +140,8 @@ export class AnimConfig { timedEvent = new AnimTimedUpdateBgEvent(te.frameIndex, te.resourceName, te); break; } - timedEvents.push(timedEvent); + + timedEvent && timedEvents.push(timedEvent); } this.frameTimedEvents.set(parseInt(fte), timedEvents); } @@ -330,7 +331,7 @@ class AnimTimedSoundEvent extends AnimTimedEvent { } return Math.ceil((scene.sound.get(this.resourceName).totalDuration * 1000) / 33.33); } else { - return Math.ceil((battleAnim.user.cry(soundConfig).totalDuration * 1000) / 33.33); + return Math.ceil((battleAnim.user!.cry(soundConfig).totalDuration * 1000) / 33.33); // TODO: is the bang behind user correct? } } @@ -441,15 +442,15 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent { } } -export const moveAnims = new Map(); -export const chargeAnims = new Map(); +export const moveAnims = new Map(); +export const chargeAnims = new Map(); export const commonAnims = new Map(); export function initCommonAnims(scene: BattleScene): Promise { return new Promise(resolve => { const commonAnimNames = Utils.getEnumKeys(CommonAnim); const commonAnimIds = Utils.getEnumValues(CommonAnim); - const commonAnimFetches = []; + const commonAnimFetches: Promise>[] = []; for (let ca = 0; ca < commonAnimIds.length; ca++) { const commonAnimId = commonAnimIds[ca]; commonAnimFetches.push(scene.cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`) @@ -572,7 +573,7 @@ export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLo const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr)[0] || allMoves[moveId].getAttrs(DelayedAttackAttr)[0]; if (chargeAttr) { const moveChargeAnims = chargeAnims.get(chargeAttr.chargeAnim); - moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims[0]); + moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims![0]); // TODO: is the bang correct? if (Array.isArray(moveChargeAnims)) { moveAnimations.push(moveChargeAnims[1]); } @@ -668,21 +669,21 @@ interface SpriteCache { } export abstract class BattleAnim { - public user: Pokemon; - public target: Pokemon; + public user: Pokemon | null; + public target: Pokemon | null; public sprites: Phaser.GameObjects.Sprite[]; public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle; private srcLine: number[]; private dstLine: number[]; - constructor(user: Pokemon, target: Pokemon) { - this.user = user; - this.target = target; + constructor(user?: Pokemon, target?: Pokemon) { + this.user = user!; // TODO: is this bang correct? + this.target = target!; // TODO: is this bang correct? this.sprites = []; } - abstract getAnim(): AnimConfig; + abstract getAnim(): AnimConfig | null; abstract isOppAnim(): boolean; @@ -705,12 +706,12 @@ export abstract class BattleAnim { const user = !isOppAnim ? this.user : this.target; const target = !isOppAnim ? this.target : this.user; - const userInitialX = user.x; - const userInitialY = user.y; - const userHalfHeight = user.getSprite().displayHeight / 2; - const targetInitialX = target.x; - const targetInitialY = target.y; - const targetHalfHeight = target.getSprite().displayHeight / 2; + const userInitialX = user!.x; // TODO: is this bang correct? + const userInitialY = user!.y; // TODO: is this bang correct? + const userHalfHeight = user!.getSprite().displayHeight! / 2; // TODO: is this bang correct? + const targetInitialX = target!.x; // TODO: is this bang correct? + const targetInitialY = target!.y; // TODO: is this bang correct? + const targetHalfHeight = target!.getSprite().displayHeight! / 2; // TODO: is this bang correct? let g = 0; let u = 0; @@ -742,7 +743,7 @@ export abstract class BattleAnim { } const angle = -frame.angle; const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; - ret.get(frame.target).set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); + ret.get(frame.target)!.set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); // TODO: is the bang correct? } return ret; @@ -750,10 +751,10 @@ export abstract class BattleAnim { play(scene: BattleScene, callback?: Function) { const isOppAnim = this.isOppAnim(); - const user = !isOppAnim ? this.user : this.target; + const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct? const target = !isOppAnim ? this.target : this.user; - if (!target.isOnField()) { + if (!target?.isOnField()) { if (callback) { callback(); } @@ -781,7 +782,7 @@ export abstract class BattleAnim { targetSprite.setAlpha(1); targetSprite.pipelineData["tone"] = [ 0.0, 0.0, 0.0, 0.0 ]; targetSprite.setAngle(0); - if (!this.isHideUser()) { + if (!this.isHideUser() && userSprite) { userSprite.setVisible(true); } if (!this.isHideTarget() && (targetSprite !== userSprite || !this.isHideUser())) { @@ -814,20 +815,20 @@ export abstract class BattleAnim { this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ]; this.dstLine = [ userInitialX, userInitialY, targetInitialX, targetInitialY ]; - let r = anim.frames.length; + let r = anim!.frames.length; // TODO: is this bang correct? let f = 0; scene.tweens.addCounter({ duration: Utils.getFrameMs(3), - repeat: anim.frames.length, + repeat: anim!.frames.length, // TODO: is this bang correct? onRepeat: () => { if (!f) { userSprite.setVisible(false); targetSprite.setVisible(false); } - const spriteFrames = anim.frames[f]; - const frameData = this.getGraphicFrameData(scene, anim.frames[f]); + const spriteFrames = anim!.frames[f]; // TODO: is the bang correcT? + const frameData = this.getGraphicFrameData(scene, anim!.frames[f]); // TODO: is the bang correct? let u = 0; let t = 0; let g = 0; @@ -840,9 +841,9 @@ export abstract class BattleAnim { const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET]; const spriteSource = isUser ? userSprite : targetSprite; if ((isUser ? u : t) === sprites.length) { - const sprite = scene.addPokemonSprite(isUser ? user : target, 0, 0, spriteSource.texture, spriteSource.frame.name, true); - [ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user : target).getSprite().pipelineData[k]); - sprite.setPipelineData("spriteKey", (isUser ? user : target).getBattleSpriteKey()); + const sprite = scene.addPokemonSprite(isUser ? user! : target, 0, 0, spriteSource!.texture, spriteSource!.frame.name, true); // TODO: are those bangs correct? + [ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user! : target).getSprite().pipelineData[k]); // TODO: are those bangs correct? + sprite.setPipelineData("spriteKey", (isUser ? user! : target).getBattleSpriteKey()); sprite.setPipelineData("shiny", (isUser ? user : target).shiny); sprite.setPipelineData("variant", (isUser ? user : target).variant); sprite.setPipelineData("ignoreFieldPos", true); @@ -853,7 +854,7 @@ export abstract class BattleAnim { const spriteIndex = isUser ? u++ : t++; const pokemonSprite = sprites[spriteIndex]; - const graphicFrameData = frameData.get(frame.target).get(spriteIndex); + const graphicFrameData = frameData.get(frame.target)!.get(spriteIndex)!; // TODO: are the bangs correct? pokemonSprite.setPosition(graphicFrameData.x, graphicFrameData.y - ((spriteSource.height / 2) * (spriteSource.parentContainer.scale - 1))); pokemonSprite.setAngle(graphicFrameData.angle); @@ -868,7 +869,7 @@ export abstract class BattleAnim { } else { const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; if (g === sprites.length) { - const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim.graphic, 1); + const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim!.graphic, 1); // TODO: is the bang correct? sprites.push(newSprite); scene.field.add(newSprite); spritePriorities.push(1); @@ -881,7 +882,7 @@ export abstract class BattleAnim { const setSpritePriority = (priority: integer) => { switch (priority) { case 0: - scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon() || scene.getPlayerPokemon()); + scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon() || scene.getPlayerPokemon()!); // TODO: is this bang correct? break; case 1: scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); @@ -892,11 +893,11 @@ export abstract class BattleAnim { if (this.bgSprite) { scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite); } else { - scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user); + scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? } break; case AnimFocus.TARGET: - scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target); + scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? break; default: setSpritePriority(1); @@ -906,10 +907,10 @@ export abstract class BattleAnim { case 3: switch (frame.focus) { case AnimFocus.USER: - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user); + scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? break; case AnimFocus.TARGET: - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target); + scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? break; default: setSpritePriority(1); @@ -925,7 +926,7 @@ export abstract class BattleAnim { moveSprite.setFrame(frame.graphicFrame); //console.log(AnimFocus[frame.focus]); - const graphicFrameData = frameData.get(frame.target).get(graphicIndex); + const graphicFrameData = frameData.get(frame.target)!.get(graphicIndex)!; // TODO: are those bangs correct? moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y); moveSprite.setAngle(graphicFrameData.angle); moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY); @@ -935,8 +936,8 @@ export abstract class BattleAnim { moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE); } } - if (anim.frameTimedEvents.has(f)) { - for (const event of anim.frameTimedEvents.get(f)) { + if (anim?.frameTimedEvents.has(f)) { + for (const event of anim.frameTimedEvents.get(f)!) { // TODO: is this bang correct? r = Math.max((anim.frames.length - f) + event.execute(scene, this), r); } } @@ -980,16 +981,16 @@ export abstract class BattleAnim { } export class CommonBattleAnim extends BattleAnim { - public commonAnim: CommonAnim; + public commonAnim: CommonAnim | null; - constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon) { + constructor(commonAnim: CommonAnim | null, user: Pokemon, target?: Pokemon) { super(user, target || user); this.commonAnim = commonAnim; } - getAnim(): AnimConfig { - return commonAnims.get(this.commonAnim); + getAnim(): AnimConfig | null { + return this.commonAnim ? commonAnims.get(this.commonAnim)! : null; // TODO: is this bang correct? } isOppAnim(): boolean { @@ -1009,11 +1010,11 @@ export class MoveAnim extends BattleAnim { getAnim(): AnimConfig { return moveAnims.get(this.move) instanceof AnimConfig ? moveAnims.get(this.move) as AnimConfig - : moveAnims.get(this.move)[this.user.isPlayer() ? 0 : 1] as AnimConfig; + : moveAnims.get(this.move)![this.user?.isPlayer() ? 0 : 1] as AnimConfig; // TODO: is this bang correct? } isOppAnim(): boolean { - return !this.user.isPlayer() && Array.isArray(moveAnims.get(this.move)); + return !this.user?.isPlayer() && Array.isArray(moveAnims.get(this.move)); } protected isHideUser(): boolean { @@ -1035,13 +1036,13 @@ export class MoveChargeAnim extends MoveAnim { } isOppAnim(): boolean { - return !this.user.isPlayer() && Array.isArray(chargeAnims.get(this.chargeAnim)); + return !this.user?.isPlayer() && Array.isArray(chargeAnims.get(this.chargeAnim)); } getAnim(): AnimConfig { return chargeAnims.get(this.chargeAnim) instanceof AnimConfig ? chargeAnims.get(this.chargeAnim) as AnimConfig - : chargeAnims.get(this.chargeAnim)[this.user.isPlayer() ? 0 : 1] as AnimConfig; + : chargeAnims.get(this.chargeAnim)![this.user?.isPlayer() ? 0 : 1] as AnimConfig; // TODO: is this bang correct? } } @@ -1059,19 +1060,19 @@ export async function populateAnims() { moveNameToId[moveName] = move; } - const seNames = [];//(await fs.readdir('./public/audio/se/battle_anims/')).map(se => se.toString()); + const seNames: string[] = [];//(await fs.readdir('./public/audio/se/battle_anims/')).map(se => se.toString()); - const animsData = [];//battleAnimRawData.split('!ruby/array:PBAnimation').slice(1); + const animsData : any[] = [];//battleAnimRawData.split('!ruby/array:PBAnimation').slice(1); // TODO: add a proper type for (let a = 0; a < animsData.length; a++) { const fields = animsData[a].split("@").slice(1); const nameField = fields.find(f => f.startsWith("name: ")); - let isOppMove: boolean; - let commonAnimId: CommonAnim; - let chargeAnimId: ChargeAnim; + let isOppMove: boolean | undefined; + let commonAnimId: CommonAnim | undefined; + let chargeAnimId: ChargeAnim | undefined; if (!nameField.startsWith("name: Move:") && !(isOppMove = nameField.startsWith("name: OppMove:"))) { - const nameMatch = commonNamePattern.exec(nameField); + const nameMatch = commonNamePattern.exec(nameField)!; // TODO: is this bang correct? const name = nameMatch[2].toLowerCase(); if (commonAnimMatchNames.indexOf(name) > -1) { commonAnimId = commonAnimIds[commonAnimMatchNames.indexOf(name)]; @@ -1128,14 +1129,14 @@ export async function populateAnims() { for (let t = 0; t < timingEntries.length; t++) { const timingData = timingEntries[t].replace(/\n/g, " ").replace(/[ ]{2,}/g, " ").replace(/[a-z]+: ! '', /ig, "").replace(/name: (.*?),/, "name: \"$1\",") .replace(/flashColor: !ruby\/object:Color { alpha: ([\d\.]+), blue: ([\d\.]+), green: ([\d\.]+), red: ([\d\.]+)}/, "flashRed: $4, flashGreen: $3, flashBlue: $2, flashAlpha: $1"); - const frameIndex = parseInt(/frame: (\d+)/.exec(timingData)[1]); - let resourceName = /name: "(.*?)"/.exec(timingData)[1].replace("''", ""); - const timingType = parseInt(/timingType: (\d)/.exec(timingData)[1]); - let timedEvent: AnimTimedEvent; + const frameIndex = parseInt(/frame: (\d+)/.exec(timingData)![1]); // TODO: is the bang correct? + let resourceName = /name: "(.*?)"/.exec(timingData)![1].replace("''", ""); // TODO: is the bang correct? + const timingType = parseInt(/timingType: (\d)/.exec(timingData)![1]); // TODO: is the bang correct? + let timedEvent: AnimTimedEvent | undefined; switch (timingType) { case 0: if (resourceName && resourceName.indexOf(".") === -1) { - let ext: string; + let ext: string | undefined; [ "wav", "mp3", "m4a" ].every(e => { if (seNames.indexOf(`${resourceName}.${e}`) > -1) { ext = e; @@ -1162,7 +1163,7 @@ export async function populateAnims() { } const propPattern = /([a-z]+): (.*?)(?:,|\})/ig; let propMatch: RegExpExecArray; - while ((propMatch = propPattern.exec(timingData))) { + while ((propMatch = propPattern.exec(timingData)!)) { // TODO: is this bang correct? const prop = propMatch[1]; let value: any = propMatch[2]; switch (prop) { @@ -1194,7 +1195,7 @@ export async function populateAnims() { if (!anim.frameTimedEvents.has(frameIndex)) { anim.frameTimedEvents.set(frameIndex, []); } - anim.frameTimedEvents.get(frameIndex).push(timedEvent); + anim.frameTimedEvents.get(frameIndex)!.push(timedEvent); // TODO: is this bang correct? } break; case "position": diff --git a/src/data/battle-stat.ts b/src/data/battle-stat.ts index d70e6655f8c..a0cb7ca88e1 100644 --- a/src/data/battle-stat.ts +++ b/src/data/battle-stat.ts @@ -8,7 +8,8 @@ export enum BattleStat { SPD, ACC, EVA, - RAND + RAND, + HP } export function getBattleStatName(stat: BattleStat) { @@ -27,6 +28,8 @@ export function getBattleStatName(stat: BattleStat) { return i18next.t("pokemonInfo:Stat.ACC"); case BattleStat.EVA: return i18next.t("pokemonInfo:Stat.EVA"); + case BattleStat.HP: + return i18next.t("pokemonInfo:Stat.HPStat"); default: return "???"; } diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index ce70240df8c..c25407d0599 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -36,11 +36,11 @@ export class BattlerTag { public sourceMove: Moves; public sourceId?: number; - constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove: Moves, sourceId?: number) { + constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number) { this.tagType = tagType; this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ]; this.turnCount = turnCount; - this.sourceMove = sourceMove; + this.sourceMove = sourceMove!; // TODO: is this bang correct? this.sourceId = sourceId; } @@ -66,7 +66,7 @@ export class BattlerTag { return false; } - getMoveName(): string { + getMoveName(): string | null { return this.sourceMove ? allMoves[this.sourceMove].name : null; @@ -299,12 +299,12 @@ export class DestinyBondTag extends BattlerTag { if (lapseType !== BattlerTagLapseType.CUSTOM) { return super.lapse(pokemon, lapseType); } - const source = pokemon.scene.getPokemonById(this.sourceId); - if (!source.isFainted()) { + const source = this.sourceId ? pokemon.scene.getPokemonById(this.sourceId) : null; + if (!source?.isFainted()) { return true; } - if (source.getAlly() === pokemon) { + if (source?.getAlly() === pokemon) { return false; } @@ -330,7 +330,19 @@ export class InfatuatedTag extends BattlerTag { } canAdd(pokemon: Pokemon): boolean { - return pokemon.isOppositeGender(pokemon.scene.getPokemonById(this.sourceId)); + if (this.sourceId) { + const pkm = pokemon.scene.getPokemonById(this.sourceId); + + if (pkm) { + return pokemon.isOppositeGender(pkm); + } else { + console.warn("canAdd: this.sourceId is not a valid pokemon id!", this.sourceId); + return false; + } + } else { + console.warn("canAdd: this.sourceId is undefined"); + return false; + } } onAdd(pokemon: Pokemon): void { @@ -339,7 +351,7 @@ export class InfatuatedTag extends BattlerTag { pokemon.scene.queueMessage( i18next.t("battle:battlerTagsInfatuatedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined) // TODO: is that bang correct? }) ); } @@ -357,7 +369,7 @@ export class InfatuatedTag extends BattlerTag { pokemon.scene.queueMessage( i18next.t("battle:battlerTagsInfatuatedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined) // TODO: is that bang correct? }) ); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT)); @@ -410,7 +422,7 @@ export class SeedTag extends BattlerTag { super.onAdd(pokemon); pokemon.scene.queueMessage(i18next.t("battle:battlerTagsSeededOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); - this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId).getBattlerIndex(); + this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -556,11 +568,11 @@ export class EncoreTag extends BattlerTag { const movePhase = pokemon.scene.findPhase(m => m instanceof MovePhase && m.pokemon === pokemon); if (movePhase) { - const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId); + const movesetMove = pokemon.getMoveset().find(m => m!.moveId === this.moveId); // TODO: is this bang correct? if (movesetMove) { const lastMove = pokemon.getLastXMoves(1)[0]; pokemon.scene.tryReplacePhase((m => m instanceof MovePhase && m.pokemon === pokemon), - new MovePhase(pokemon.scene, pokemon, lastMove.targets, movesetMove)); + new MovePhase(pokemon.scene, pokemon, lastMove.targets!, movesetMove)); // TODO: is this bang correct? } } } @@ -580,7 +592,7 @@ export class HelpingHandTag extends BattlerTag { onAdd(pokemon: Pokemon): void { pokemon.scene.queueMessage( i18next.t("battle:battlerTagsHelpingHandOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)), + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? pokemonName: getPokemonNameWithAffix(pokemon) }) ); @@ -800,7 +812,7 @@ export class BindTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsBindOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)), + sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? moveName: this.getMoveName() }); } @@ -814,7 +826,7 @@ export class WrapTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsWrapOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? }); } } @@ -848,7 +860,7 @@ export class ClampTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsClampOnTrap", { - sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)), + sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? pokemonName: getPokemonNameWithAffix(pokemon), }); } @@ -895,7 +907,7 @@ export class ThunderCageTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsThunderCageOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? }); } } @@ -908,7 +920,7 @@ export class InfestationTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsInfestationOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? }); } } @@ -1242,6 +1254,7 @@ export class HighestStatBoostTag extends AbilityBattlerTag { return highestValue; }, 0); + highestStat = highestStat!; // tell TS compiler it's defined! this.stat = highestStat; switch (this.stat) { @@ -1427,7 +1440,7 @@ export class SaltCuredTag extends BattlerTag { super.onAdd(pokemon); pokemon.scene.queueMessage(i18next.t("battle:battlerTagsSaltCuredOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); - this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId).getBattlerIndex(); + this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -1474,7 +1487,7 @@ export class CursedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId).getBattlerIndex(); + this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -1647,6 +1660,80 @@ export class StockpilingTag extends BattlerTag { } } +/** + * Battler tag for Gulp Missile used by Cramorant. + * @extends BattlerTag + */ +export class GulpMissileTag extends BattlerTag { + constructor(tagType: BattlerTagType, sourceMove: Moves) { + super(tagType, BattlerTagLapseType.CUSTOM, 0, sourceMove); + } + + /** + * Gulp Missile's initial form changes are triggered by using Surf and Dive. + * @param {Pokemon} pokemon The Pokemon with Gulp Missile ability. + * @returns Whether the BattlerTag can be added. + */ + canAdd(pokemon: Pokemon): boolean { + const isSurfOrDive = [ Moves.SURF, Moves.DIVE ].includes(this.sourceMove); + const isNormalForm = pokemon.formIndex === 0 && !pokemon.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA) && !pokemon.getTag(BattlerTagType.GULP_MISSILE_PIKACHU); + const isCramorant = pokemon.species.speciesId === Species.CRAMORANT; + + return isSurfOrDive && isNormalForm && isCramorant; + } + + onAdd(pokemon: Pokemon): void { + super.onAdd(pokemon); + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger); + } + + onRemove(pokemon: Pokemon): void { + super.onRemove(pokemon); + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger); + } +} + +/** + * Tag that makes the target drop all of it type immunities + * and all accuracy checks ignore its evasiveness stat. + * + * Applied by moves: {@linkcode Moves.ODOR_SLEUTH | Odor Sleuth}, + * {@linkcode Moves.MIRACLE_EYE | Miracle Eye} and {@linkcode Moves.FORESIGHT | Foresight}. + * + * @extends BattlerTag + * @see {@linkcode ignoreImmunity} + */ +export class ExposedTag extends BattlerTag { + private defenderType: Type; + private allowedTypes: Type[]; + + constructor(tagType: BattlerTagType, sourceMove: Moves, defenderType: Type, allowedTypes: Type[]) { + super(tagType, BattlerTagLapseType.CUSTOM, 1, sourceMove); + this.defenderType = defenderType; + this.allowedTypes = allowedTypes; + } + + /** + * When given a battler tag or json representing one, load the data for it. + * @param {BattlerTag | any} source A battler tag + */ + loadTag(source: BattlerTag | any): void { + super.loadTag(source); + this.defenderType = source.defenderType as Type; + this.allowedTypes = source.allowedTypes as Type[]; + } + + /** + * @param types {@linkcode Type} of the defending Pokemon + * @param moveType {@linkcode Type} of the move targetting it + * @returns `true` if the move should be allowed to target the defender. + */ + ignoreImmunity(type: Type, moveType: Type): boolean { + return type === this.defenderType && this.allowedTypes.includes(moveType); + } +} + + export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag { switch (tagType) { case BattlerTagType.RECHARGING: @@ -1741,13 +1828,11 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source case BattlerTagType.ALWAYS_CRIT: case BattlerTagType.IGNORE_ACCURACY: return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove); - case BattlerTagType.NO_CRIT: - return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount, sourceMove); case BattlerTagType.ALWAYS_GET_HIT: case BattlerTagType.RECEIVE_DOUBLE_DAMAGE: return new BattlerTag(tagType, BattlerTagLapseType.PRE_MOVE, 1, sourceMove); case BattlerTagType.BYPASS_SLEEP: - return new BattlerTag(BattlerTagType.BYPASS_SLEEP, BattlerTagLapseType.TURN_END, turnCount, sourceMove); + return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceMove); case BattlerTagType.IGNORE_FLYING: return new GroundedTag(tagType, BattlerTagLapseType.CUSTOM, sourceMove); case BattlerTagType.ROOSTED: @@ -1770,6 +1855,13 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new StockpilingTag(sourceMove); case BattlerTagType.OCTOLOCK: return new OctolockTag(sourceId); + case BattlerTagType.IGNORE_GHOST: + return new ExposedTag(tagType, sourceMove, Type.GHOST, [Type.NORMAL, Type.FIGHTING]); + case BattlerTagType.IGNORE_DARK: + return new ExposedTag(tagType, sourceMove, Type.DARK, [Type.PSYCHIC]); + case BattlerTagType.GULP_MISSILE_ARROKUDA: + case BattlerTagType.GULP_MISSILE_PIKACHU: + return new GulpMissileTag(tagType, sourceMove); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/data/berry.ts b/src/data/berry.ts index 5ca64544544..30b89848452 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -54,7 +54,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); - return !!pokemon.getMoveset().find(m => !m.getPpRatio()); + return !!pokemon.getMoveset().find(m => !m?.getPpRatio()); }; } } @@ -120,10 +120,10 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { if (pokemon.battleData) { pokemon.battleData.berriesEaten.push(berryType); } - const ppRestoreMove = pokemon.getMoveset().find(m => !m.getPpRatio()) ? pokemon.getMoveset().find(m => !m.getPpRatio()) : pokemon.getMoveset().find(m => m.getPpRatio() < 1); + const ppRestoreMove = pokemon.getMoveset().find(m => !m?.getPpRatio()) ? pokemon.getMoveset().find(m => !m?.getPpRatio()) : pokemon.getMoveset().find(m => m!.getPpRatio() < 1); // TODO: is this bang correct? if (ppRestoreMove !== undefined) { - ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); - pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove.getName(), berryName: getBerryName(berryType) })); + ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0); + pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) })); } }; } diff --git a/src/data/biomes.ts b/src/data/biomes.ts index 479e0994cb6..ed579112249 100644 --- a/src/data/biomes.ts +++ b/src/data/biomes.ts @@ -7705,7 +7705,7 @@ export function initBiomes() { ? pokemonEvolutions[speciesId] : []; - if (!biomeEntries.filter(b => b[0] !== Biome.END).length && !speciesEvolutions.filter(es => !!((pokemonBiomes.find(p => p[0] === es.speciesId))[3] as any[]).filter(b => b[0] !== Biome.END).length).length) { + if (!biomeEntries.filter(b => b[0] !== Biome.END).length && !speciesEvolutions.filter(es => !!((pokemonBiomes.find(p => p[0] === es.speciesId)!)[3] as any[]).filter(b => b[0] !== Biome.END).length).length) { // TODO: is the bang on the `find()` correct? uncatchableSpecies.push(speciesId); } diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 7bea68e36c7..5f17fdedb78 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -412,7 +412,7 @@ export class SingleGenerationChallenge extends Challenge { const speciesToCheck = [pokemon.speciesId]; while (speciesToCheck.length) { const checking = speciesToCheck.pop(); - if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { + if (checking && pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { pokemonEvolutions[checking].forEach(e => { speciesToCheck.push(e.speciesId); generations.push(getPokemonSpecies(e.speciesId).generation); @@ -430,7 +430,7 @@ export class SingleGenerationChallenge extends Challenge { applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { const baseGeneration = pokemon.species.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.species.speciesId).generation; - const fusionGeneration = pokemon.isFusion() ? pokemon.fusionSpecies.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.fusionSpecies.speciesId).generation : 0; + const fusionGeneration = pokemon.isFusion() ? pokemon.fusionSpecies?.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0; // TODO: is the bang on fusionSpecies correct? if (pokemon.isPlayer() && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))) { valid.value = false; return true; @@ -542,13 +542,13 @@ export class SingleTypeChallenge extends Challenge { const speciesToCheck = [pokemon.speciesId]; while (speciesToCheck.length) { const checking = speciesToCheck.pop(); - if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { + if (checking && pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { pokemonEvolutions[checking].forEach(e => { speciesToCheck.push(e.speciesId); types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2); }); } - if (pokemonFormChanges.hasOwnProperty(checking) && checkPokemonForms) { + if (checking && pokemonFormChanges.hasOwnProperty(checking) && checkPokemonForms) { pokemonFormChanges[checking].forEach(f1 => { getPokemonSpecies(checking).forms.forEach(f2 => { if (f1.formKey === f2.formKey) { @@ -568,10 +568,11 @@ export class SingleTypeChallenge extends Challenge { applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { if (pokemon.isPlayer() && !pokemon.isOfType(this.value - 1, false, false, true) - && !SingleTypeChallenge.TYPE_OVERRIDES.some(o => o.type === (this.value - 1) && (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies : pokemon.species).speciesId === o.species)) { + && !SingleTypeChallenge.TYPE_OVERRIDES.some(o => o.type === (this.value - 1) && (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies! : pokemon.species).speciesId === o.species)) { // TODO: is the bang on fusionSpecies correct? valid.value = false; return true; } + return false; } /** diff --git a/src/data/daily-run.ts b/src/data/daily-run.ts index 4b741b6f473..b7e03b4b189 100644 --- a/src/data/daily-run.ts +++ b/src/data/daily-run.ts @@ -11,15 +11,15 @@ export interface DailyRunConfig { starters: Starter; } -export function fetchDailyRunSeed(): Promise { - return new Promise((resolve, reject) => { +export function fetchDailyRunSeed(): Promise { + return new Promise((resolve, reject) => { Utils.apiFetch("daily/seed").then(response => { if (!response.ok) { resolve(null); return; } return response.text(); - }).then(seed => resolve(seed)) + }).then(seed => resolve(seed!)) // TODO: is this bang correct? .catch(err => reject(err)); }); } diff --git a/src/data/dialogue.ts b/src/data/dialogue.ts index 4386515c953..3e48d81ed12 100644 --- a/src/data/dialogue.ts +++ b/src/data/dialogue.ts @@ -452,144 +452,304 @@ export const trainerTypeDialogue: TrainerTypeDialogue = { [TrainerType.ROCKET_GRUNT]: [ { encounter: [ - "dialogue:rocket_grunt.encounter.1" + "dialogue:rocket_grunt.encounter.1", + "dialogue:rocket_grunt.encounter.2", + "dialogue:rocket_grunt.encounter.3", + "dialogue:rocket_grunt.encounter.4", + "dialogue:rocket_grunt.encounter.5", ], victory: [ - "dialogue:rocket_grunt.victory.1" + "dialogue:rocket_grunt.victory.1", + "dialogue:rocket_grunt.victory.2", + "dialogue:rocket_grunt.victory.3", + "dialogue:rocket_grunt.victory.4", + "dialogue:rocket_grunt.victory.5", ] } ], - [TrainerType.ROCKET_ADMIN]: [ + [TrainerType.ARCHER]: [ { encounter: [ - "dialogue:rocket_admin.encounter.1", - "dialogue:rocket_admin.encounter.2", - "dialogue:rocket_admin.encounter.3", + "dialogue:archer.encounter.1", + "dialogue:archer.encounter.2", + "dialogue:archer.encounter.3", ], victory: [ - "dialogue:rocket_admin.victory.1", - "dialogue:rocket_admin.victory.2", - "dialogue:rocket_admin.victory.3", + "dialogue:archer.victory.1", + "dialogue:archer.victory.2", + "dialogue:archer.victory.3", + ] + } + ], + [TrainerType.ARIANA]: [ + { + encounter: [ + "dialogue:ariana.encounter.1", + "dialogue:ariana.encounter.2", + "dialogue:ariana.encounter.3", + ], + victory: [ + "dialogue:ariana.victory.1", + "dialogue:ariana.victory.2", + "dialogue:ariana.victory.3", + ] + } + ], + [TrainerType.PROTON]: [ + { + encounter: [ + "dialogue:proton.encounter.1", + "dialogue:proton.encounter.2", + "dialogue:proton.encounter.3", + ], + victory: [ + "dialogue:proton.victory.1", + "dialogue:proton.victory.2", + "dialogue:proton.victory.3", + ] + } + ], + [TrainerType.PETREL]: [ + { + encounter: [ + "dialogue:petrel.encounter.1", + "dialogue:petrel.encounter.2", + "dialogue:petrel.encounter.3", + ], + victory: [ + "dialogue:petrel.victory.1", + "dialogue:petrel.victory.2", + "dialogue:petrel.victory.3", ] } ], [TrainerType.MAGMA_GRUNT]: [ { encounter: [ - "dialogue:magma_grunt.encounter.1" + "dialogue:magma_grunt.encounter.1", + "dialogue:magma_grunt.encounter.2", + "dialogue:magma_grunt.encounter.3", + "dialogue:magma_grunt.encounter.4", + "dialogue:magma_grunt.encounter.5", ], victory: [ - "dialogue:magma_grunt.victory.1" + "dialogue:magma_grunt.victory.1", + "dialogue:magma_grunt.victory.2", + "dialogue:magma_grunt.victory.3", + "dialogue:magma_grunt.victory.4", + "dialogue:magma_grunt.victory.5", ] } ], - [TrainerType.MAGMA_ADMIN]: [ + [TrainerType.TABITHA]: [ { encounter: [ - "dialogue:magma_admin.encounter.1", - "dialogue:magma_admin.encounter.2", - "dialogue:magma_admin.encounter.3", + "dialogue:tabitha.encounter.1", + "dialogue:tabitha.encounter.2", + "dialogue:tabitha.encounter.3", ], victory: [ - "dialogue:magma_admin.victory.1", - "dialogue:magma_admin.victory.2", - "dialogue:magma_admin.victory.3", + "dialogue:tabitha.victory.1", + "dialogue:tabitha.victory.2", + "dialogue:tabitha.victory.3", + ] + } + ], + [TrainerType.COURTNEY]: [ + { + encounter: [ + "dialogue:courtney.encounter.1", + "dialogue:courtney.encounter.2", + "dialogue:courtney.encounter.3", + ], + victory: [ + "dialogue:courtney.victory.1", + "dialogue:courtney.victory.2", + "dialogue:courtney.victory.3", ] } ], [TrainerType.AQUA_GRUNT]: [ { encounter: [ - "dialogue:aqua_grunt.encounter.1" + "dialogue:aqua_grunt.encounter.1", + "dialogue:aqua_grunt.encounter.2", + "dialogue:aqua_grunt.encounter.3", + "dialogue:aqua_grunt.encounter.4", + "dialogue:aqua_grunt.encounter.5", ], victory: [ - "dialogue:aqua_grunt.victory.1" + "dialogue:aqua_grunt.victory.1", + "dialogue:aqua_grunt.victory.2", + "dialogue:aqua_grunt.victory.3", + "dialogue:aqua_grunt.victory.4", + "dialogue:aqua_grunt.victory.5", ] } ], - [TrainerType.AQUA_ADMIN]: [ + [TrainerType.MATT]: [ { encounter: [ - "dialogue:aqua_admin.encounter.1", - "dialogue:aqua_admin.encounter.2", - "dialogue:aqua_admin.encounter.3", + "dialogue:matt.encounter.1", + "dialogue:matt.encounter.2", + "dialogue:matt.encounter.3", ], victory: [ - "dialogue:aqua_admin.victory.1", - "dialogue:aqua_admin.victory.2", - "dialogue:aqua_admin.victory.3", + "dialogue:matt.victory.1", + "dialogue:matt.victory.2", + "dialogue:matt.victory.3", + ] + } + ], + [TrainerType.SHELLY]: [ + { + encounter: [ + "dialogue:shelly.encounter.1", + "dialogue:shelly.encounter.2", + "dialogue:shelly.encounter.3", + ], + victory: [ + "dialogue:shelly.victory.1", + "dialogue:shelly.victory.2", + "dialogue:shelly.victory.3", ] } ], [TrainerType.GALACTIC_GRUNT]: [ { encounter: [ - "dialogue:galactic_grunt.encounter.1" + "dialogue:galactic_grunt.encounter.1", + "dialogue:galactic_grunt.encounter.2", + "dialogue:galactic_grunt.encounter.3", + "dialogue:galactic_grunt.encounter.4", + "dialogue:galactic_grunt.encounter.5", ], victory: [ - "dialogue:galactic_grunt.victory.1" + "dialogue:galactic_grunt.victory.1", + "dialogue:galactic_grunt.victory.2", + "dialogue:galactic_grunt.victory.3", + "dialogue:galactic_grunt.victory.4", + "dialogue:galactic_grunt.victory.5", ] } ], - [TrainerType.GALACTIC_ADMIN]: [ + [TrainerType.JUPITER]: [ { encounter: [ - "dialogue:galactic_admin.encounter.1", - "dialogue:galactic_admin.encounter.2", - "dialogue:galactic_admin.encounter.3", + "dialogue:jupiter.encounter.1", + "dialogue:jupiter.encounter.2", + "dialogue:jupiter.encounter.3", ], victory: [ - "dialogue:galactic_admin.victory.1", - "dialogue:galactic_admin.victory.2", - "dialogue:galactic_admin.victory.3", + "dialogue:jupiter.victory.1", + "dialogue:jupiter.victory.2", + "dialogue:jupiter.victory.3", + ] + } + ], + [TrainerType.MARS]: [ + { + encounter: [ + "dialogue:mars.encounter.1", + "dialogue:mars.encounter.2", + "dialogue:mars.encounter.3", + ], + victory: [ + "dialogue:mars.victory.1", + "dialogue:mars.victory.2", + "dialogue:mars.victory.3", + ] + } + ], + [TrainerType.SATURN]: [ + { + encounter: [ + "dialogue:saturn.encounter.1", + "dialogue:saturn.encounter.2", + "dialogue:saturn.encounter.3", + ], + victory: [ + "dialogue:saturn.victory.1", + "dialogue:saturn.victory.2", + "dialogue:saturn.victory.3", ] } ], [TrainerType.PLASMA_GRUNT]: [ { encounter: [ - "dialogue:plasma_grunt.encounter.1" + "dialogue:plasma_grunt.encounter.1", + "dialogue:plasma_grunt.encounter.2", + "dialogue:plasma_grunt.encounter.3", + "dialogue:plasma_grunt.encounter.4", + "dialogue:plasma_grunt.encounter.5", ], victory: [ - "dialogue:plasma_grunt.victory.1" + "dialogue:plasma_grunt.victory.1", + "dialogue:plasma_grunt.victory.2", + "dialogue:plasma_grunt.victory.3", + "dialogue:plasma_grunt.victory.4", + "dialogue:plasma_grunt.victory.5", ] } ], - [TrainerType.PLASMA_SAGE]: [ + [TrainerType.ZINZOLIN]: [ { encounter: [ - "dialogue:plasma_sage.encounter.1", - "dialogue:plasma_sage.encounter.2", - "dialogue:plasma_sage.encounter.3", + "dialogue:zinzolin.encounter.1", + "dialogue:zinzolin.encounter.2", + "dialogue:zinzolin.encounter.3", ], victory: [ - "dialogue:plasma_sage.victory.1", - "dialogue:plasma_sage.victory.2", - "dialogue:plasma_sage.victory.3", + "dialogue:zinzolin.victory.1", + "dialogue:zinzolin.victory.2", + "dialogue:zinzolin.victory.3", ] } ], [TrainerType.FLARE_GRUNT]: [ { encounter: [ - "dialogue:flare_grunt.encounter.1" + "dialogue:flare_grunt.encounter.1", + "dialogue:flare_grunt.encounter.2", + "dialogue:flare_grunt.encounter.3", + "dialogue:flare_grunt.encounter.4", + "dialogue:flare_grunt.encounter.5", ], victory: [ - "dialogue:flare_grunt.victory.1" + "dialogue:flare_grunt.victory.1", + "dialogue:flare_grunt.victory.2", + "dialogue:flare_grunt.victory.3", + "dialogue:flare_grunt.victory.4", + "dialogue:flare_grunt.victory.5", ] } ], - [TrainerType.FLARE_ADMIN]: [ + [TrainerType.BRYONY]: [ { encounter: [ - "dialogue:flare_admin.encounter.1", - "dialogue:flare_admin.encounter.2", - "dialogue:flare_admin.encounter.3", + "dialogue:bryony.encounter.1", + "dialogue:bryony.encounter.2", + "dialogue:bryony.encounter.3", ], victory: [ - "dialogue:flare_admin.victory.1", - "dialogue:flare_admin.victory.2", - "dialogue:flare_admin.victory.3", + "dialogue:bryony.victory.1", + "dialogue:bryony.victory.2", + "dialogue:bryony.victory.3", + ] + } + ], + [TrainerType.XEROSIC]: [ + { + encounter: [ + "dialogue:xerosic.encounter.1", + "dialogue:xerosic.encounter.2", + "dialogue:xerosic.encounter.3", + ], + victory: [ + "dialogue:xerosic.victory.1", + "dialogue:xerosic.victory.2", + "dialogue:xerosic.victory.3", ] } ], diff --git a/src/data/egg.ts b/src/data/egg.ts index a7a1b167238..9c76591f01b 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -140,30 +140,30 @@ export class Egg { constructor(eggOptions?: IEggOptions) { //if (eggOptions.tier && eggOptions.species) throw Error("Error egg can't have species and tier as option. only choose one of them.") - this._sourceType = eggOptions.sourceType ?? undefined; + this._sourceType = eggOptions?.sourceType!; // TODO: is this bang correct? // Ensure _sourceType is defined before invoking rollEggTier(), as it is referenced - this._tier = eggOptions.tier ?? (Overrides.EGG_TIER_OVERRIDE ?? this.rollEggTier()); + this._tier = eggOptions?.tier ?? (Overrides.EGG_TIER_OVERRIDE ?? this.rollEggTier()); // If egg was pulled, check if egg pity needs to override the egg tier - if (eggOptions.pulled) { + if (eggOptions?.pulled) { // Needs this._tier and this._sourceType to work - this.checkForPityTierOverrides(eggOptions.scene); + this.checkForPityTierOverrides(eggOptions.scene!); // TODO: is this bang correct? } - this._id = eggOptions.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); + this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); - this._sourceType = eggOptions.sourceType ?? undefined; - this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); - this._timestamp = eggOptions.timestamp ?? new Date().getTime(); + this._sourceType = eggOptions?.sourceType ?? undefined; + this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); + this._timestamp = eggOptions?.timestamp ?? new Date().getTime(); // First roll shiny and variant so we can filter if species with an variant exist - this._isShiny = eggOptions.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); - this._variantTier = eggOptions.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); - this._species = eggOptions.species ?? this.rollSpecies(eggOptions.scene); + this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); + this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); + this._species = eggOptions?.species ?? this.rollSpecies(eggOptions!.scene!)!; // TODO: Are those bangs correct? - this._overrideHiddenAbility = eggOptions.overrideHiddenAbility ?? false; + this._overrideHiddenAbility = eggOptions?.overrideHiddenAbility ?? false; // Override egg tier and hatchwaves if species was given - if (eggOptions.species) { + if (eggOptions?.species) { this._tier = this.getEggTierFromSpeciesStarterValue(); this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); } @@ -174,10 +174,10 @@ export class Egg { this._variantTier = VariantTier.COMMON; } // Needs this._tier so it needs to be generated afer the tier override if bought from same species - this._eggMoveIndex = eggOptions.eggMoveIndex ?? this.rollEggMoveIndex(); - if (eggOptions.pulled) { - this.increasePullStatistic(eggOptions.scene); - this.addEggToGameData(eggOptions.scene); + this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex(); + if (eggOptions?.pulled) { + this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct? + this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct? } } @@ -202,14 +202,18 @@ export class Egg { // Legacy egg wants to hatch. Generate missing properties if (!this._species) { this._isShiny = this.rollShiny(); - this._species = this.rollSpecies(scene); + this._species = this.rollSpecies(scene!)!; // TODO: are these bangs correct? } - const pokemonSpecies = getPokemonSpecies(this._species); + let pokemonSpecies = getPokemonSpecies(this._species); + // Special condition to have Phione eggs also have a chance of generating Manaphy + if (this._species === Species.PHIONE) { + pokemonSpecies = getPokemonSpecies(Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? Species.PHIONE : Species.MANAPHY); + } // Sets the hidden ability if a hidden ability exists and the override is set // or if the same species egg hits the chance - let abilityIndex = undefined; + let abilityIndex: number | undefined = undefined; if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility || (this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE)))) { abilityIndex = 2; @@ -273,6 +277,9 @@ export class Egg { return i18next.t("egg:gachaTypeShiny"); case EggSourceType.GACHA_MOVE: return i18next.t("egg:gachaTypeMove"); + default: + console.warn("getEggTypeDescriptor case not defined. Returning default empty string"); + return ""; } } @@ -322,9 +329,9 @@ export class Egg { return tierValue >= 52 + tierValueOffset ? EggTier.COMMON : tierValue >= 8 + tierValueOffset ? EggTier.GREAT : tierValue >= 1 + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER; } - private rollSpecies(scene: BattleScene): Species { + private rollSpecies(scene: BattleScene): Species | null { if (!scene) { - return undefined; + return null; } /** * Manaphy eggs have a 1/8 chance of being Manaphy and 7/8 chance of being Phione @@ -396,7 +403,7 @@ export class Egg { * and being the same each time */ let totalWeight = 0; - const speciesWeights = []; + const speciesWeights : number[] = []; for (const speciesId of speciesPool) { let weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); const species = getPokemonSpecies(speciesId); @@ -416,6 +423,7 @@ export class Egg { break; } } + species = species!; // tell TS compiled it's defined now! if (!!scene.gameData.dexData[species].caughtAttr || scene.gameData.eggs.some(e => e.species === species)) { scene.gameData.unlockPity[this.tier] = Math.min(scene.gameData.unlockPity[this.tier] + 1, 10); @@ -513,6 +521,8 @@ export class Egg { if (speciesStartValue >= 8) { return EggTier.MASTER; } + + return EggTier.COMMON; } //// @@ -537,6 +547,7 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta scene.executeWithSeedOffset(() => { ret = Phaser.Math.RND.shuffle(legendarySpecies)[index]; }, offset, EGG_SEED.toString()); + ret = ret!; // tell TS compiler it's return ret; } diff --git a/src/data/move.ts b/src/data/move.ts index 4ee3ae29789..b0fe0f8c015 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,7 +1,7 @@ import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; -import { BattleEndPhase, MoveEndPhase, MovePhase, NewBattlePhase, PartyStatusCurePhase, PokemonHealPhase, StatChangePhase, SwitchSummonPhase } from "../phases"; +import { BattleEndPhase, MoveEndPhase, MovePhase, NewBattlePhase, PartyStatusCurePhase, PokemonHealPhase, StatChangePhase, SwitchPhase, SwitchSummonPhase } from "../phases"; import { BattleStat, getBattleStatName } from "./battle-stat"; -import { EncoreTag, HelpingHandTag, SemiInvulnerableTag, StockpilingTag, TypeBoostTag } from "./battler-tags"; +import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, StockpilingTag, TypeBoostTag } from "./battler-tags"; import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon"; import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects} from "./status-effect"; @@ -10,7 +10,7 @@ import { Constructor } from "#app/utils"; import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag"; -import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr, IgnoreMoveEffectsAbAttr, applyPreDefendAbAttrs, MoveEffectChanceMultiplierAbAttr, WonderSkinAbAttr, applyPreAttackAbAttrs, MoveTypeChangeAttr, UserFieldMoveTypePowerBoostAbAttr, FieldMoveTypePowerBoostAbAttr, AllyMoveCategoryPowerBoostAbAttr, VariableMovePowerAbAttr } from "./ability"; +import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr, IgnoreMoveEffectsAbAttr, applyPreDefendAbAttrs, MoveEffectChanceMultiplierAbAttr, WonderSkinAbAttr, applyPreAttackAbAttrs, MoveTypeChangeAttr, UserFieldMoveTypePowerBoostAbAttr, FieldMoveTypePowerBoostAbAttr, AllyMoveCategoryPowerBoostAbAttr, VariableMovePowerAbAttr } from "./ability"; import { allAbilities } from "./ability"; import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier, PokemonMoveAccuracyBoosterModifier, AttackTypeBoosterModifier, PokemonMultiHitModifier } from "../modifier/modifier"; import { BattlerIndex, BattleType } from "../battle"; @@ -27,6 +27,7 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { MoveUsedEvent } from "#app/events/battle-scene.js"; export enum MoveCategory { PHYSICAL, @@ -179,7 +180,7 @@ export default class Move implements Localizable { * @returns the first {@linkcode MoveAttr} element in attrs that makes the input function return true */ findAttr(attrPredicate: (attr: MoveAttr) => boolean): MoveAttr { - return this.attrs.find(attrPredicate); + return this.attrs.find(attrPredicate)!; // TODO: is the bang correct? } /** @@ -360,7 +361,7 @@ export default class Move implements Localizable { * @param makesContact The value (boolean) to set the flag to * @returns The {@linkcode Move} that called this function */ - makesContact(makesContact?: boolean): this { + makesContact(makesContact: boolean = true): this { // TODO: is true the correct default? this.setFlag(MoveFlags.MAKES_CONTACT, makesContact); return this; } @@ -371,7 +372,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.CURSE} * @returns The {@linkcode Move} that called this function */ - ignoresProtect(ignoresProtect?: boolean): this { + ignoresProtect(ignoresProtect: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.IGNORE_PROTECT, ignoresProtect); return this; } @@ -382,7 +383,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.NATURE_POWER} * @returns The {@linkcode Move} that called this function */ - ignoresVirtual(ignoresVirtual?: boolean): this { + ignoresVirtual(ignoresVirtual: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.IGNORE_VIRTUAL, ignoresVirtual); return this; } @@ -393,7 +394,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.UPROAR} * @returns The {@linkcode Move} that called this function */ - soundBased(soundBased?: boolean): this { + soundBased(soundBased: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.SOUND_BASED, soundBased); return this; } @@ -404,7 +405,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.TELEPORT} * @returns The {@linkcode Move} that called this function */ - hidesUser(hidesUser?: boolean): this { + hidesUser(hidesUser: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.HIDE_USER, hidesUser); return this; } @@ -415,7 +416,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.WHIRLWIND} * @returns The {@linkcode Move} that called this function */ - hidesTarget(hidesTarget?: boolean): this { + hidesTarget(hidesTarget: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.HIDE_TARGET, hidesTarget); return this; } @@ -426,7 +427,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.BITE} * @returns The {@linkcode Move} that called this function */ - bitingMove(bitingMove?: boolean): this { + bitingMove(bitingMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.BITING_MOVE, bitingMove); return this; } @@ -437,7 +438,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.WATER_PULSE} * @returns The {@linkcode Move} that called this function */ - pulseMove(pulseMove?: boolean): this { + pulseMove(pulseMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.PULSE_MOVE, pulseMove); return this; } @@ -448,7 +449,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.DRAIN_PUNCH} * @returns The {@linkcode Move} that called this function */ - punchingMove(punchingMove?: boolean): this { + punchingMove(punchingMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.PUNCHING_MOVE, punchingMove); return this; } @@ -459,7 +460,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.X_SCISSOR} * @returns The {@linkcode Move} that called this function */ - slicingMove(slicingMove?: boolean): this { + slicingMove(slicingMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.SLICING_MOVE, slicingMove); return this; } @@ -470,7 +471,7 @@ export default class Move implements Localizable { * @param recklessMove The value to set the flag to * @returns The {@linkcode Move} that called this function */ - recklessMove(recklessMove?: boolean): this { + recklessMove(recklessMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.RECKLESS_MOVE, recklessMove); return this; } @@ -481,7 +482,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.ELECTRO_BALL} * @returns The {@linkcode Move} that called this function */ - ballBombMove(ballBombMove?: boolean): this { + ballBombMove(ballBombMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.BALLBOMB_MOVE, ballBombMove); return this; } @@ -492,7 +493,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.STUN_SPORE} * @returns The {@linkcode Move} that called this function */ - powderMove(powderMove?: boolean): this { + powderMove(powderMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.POWDER_MOVE, powderMove); return this; } @@ -503,7 +504,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.PETAL_DANCE} * @returns The {@linkcode Move} that called this function */ - danceMove(danceMove?: boolean): this { + danceMove(danceMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.DANCE_MOVE, danceMove); return this; } @@ -514,7 +515,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.HURRICANE} * @returns The {@linkcode Move} that called this function */ - windMove(windMove?: boolean): this { + windMove(windMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.WIND_MOVE, windMove); return this; } @@ -525,7 +526,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.ABSORB} * @returns The {@linkcode Move} that called this function */ - triageMove(triageMove?: boolean): this { + triageMove(triageMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.TRIAGE_MOVE, triageMove); return this; } @@ -536,7 +537,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.SUNSTEEL_STRIKE} * @returns The {@linkcode Move} that called this function */ - ignoresAbilities(ignoresAbilities?: boolean): this { + ignoresAbilities(ignoresAbilities: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.IGNORE_ABILITIES, ignoresAbilities); return this; } @@ -547,7 +548,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.TRIPLE_AXEL} * @returns The {@linkcode Move} that called this function */ - checkAllHits(checkAllHits?: boolean): this { + checkAllHits(checkAllHits: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits); return this; } @@ -558,7 +559,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.METAL_BURST} * @returns The {@linkcode Move} that called this function */ - redirectCounter(redirectCounter?: boolean): this { + redirectCounter(redirectCounter: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.REDIRECT_COUNTER, redirectCounter); return this; } @@ -570,7 +571,7 @@ export default class Move implements Localizable { * @param target {@linkcode Pokemon} the Pokemon receiving the move * @returns boolean */ - checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean { + checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon | null): boolean { // special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact switch (flag) { case MoveFlags.MAKES_CONTACT: @@ -869,7 +870,7 @@ export abstract class MoveAttr { * @param args Set of unique arguments needed by this attribute * @returns true if application of the ability succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { + apply(user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): boolean | Promise { return true; } @@ -877,7 +878,7 @@ export abstract class MoveAttr { * @virtual * @returns the {@linkcode MoveCondition} or {@linkcode MoveConditionFunc} for this {@linkcode Move} */ - getCondition(): MoveCondition | MoveConditionFunc { + getCondition(): MoveCondition | MoveConditionFunc | null { return null; } @@ -953,14 +954,14 @@ export class MoveEffectAttr extends MoveAttr { * @param args Set of unique arguments needed by this attribute * @returns true if basic application of the ability attribute should be possible */ - canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) { + canApply(user: Pokemon, target: Pokemon, move: Move, args?: any[]) { return !! (this.selfTarget ? user.hp && !user.getTag(BattlerTagType.FRENZY) : target.hp) && (this.selfTarget || !target.getTag(BattlerTagType.PROTECTED) || move.checkFlag(MoveFlags.IGNORE_PROTECT, user, target)); } /** Applies move effects so long as they are able based on {@linkcode canApply} */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { + apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise { return this.canApply(user, target, move, args); } @@ -983,6 +984,43 @@ export class MoveEffectAttr extends MoveAttr { } } +/** + * Base class defining all Move Header attributes. + * Move Header effects apply at the beginning of a turn before any moves are resolved. + * They can be used to apply effects to the field (e.g. queueing a message) or to the user + * (e.g. adding a battler tag). + */ +export class MoveHeaderAttr extends MoveAttr { + constructor() { + super(true); + } +} + +/** + * Header attribute to queue a message at the beginning of a turn. + * @see {@link MoveHeaderAttr} + */ +export class MessageHeaderAttr extends MoveHeaderAttr { + private message: string | ((user: Pokemon, move: Move) => string); + + constructor(message: string | ((user: Pokemon, move: Move) => string)) { + super(); + this.message = message; + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const message = typeof this.message === "string" + ? this.message + : this.message(user, move); + + if (message) { + user.scene.queueMessage(message); + return true; + } + return false; + } +} + export class PreMoveMessageAttr extends MoveAttr { private message: string | ((user: Pokemon, target: Pokemon, move: Move) => string); @@ -1407,10 +1445,10 @@ export class PartyStatusCureAttr extends MoveEffectAttr { /** Skips mons with this ability, ie. Soundproof */ private abilityCondition: Abilities; - constructor(message: string, abilityCondition: Abilities) { + constructor(message: string | null, abilityCondition: Abilities) { super(); - this.message = message; + this.message = message!; // TODO: is this bang correct? this.abilityCondition = abilityCondition; } @@ -1427,6 +1465,7 @@ export class PartyStatusCureAttr extends MoveEffectAttr { return false; } this.addPartyCurePhase(user); + return true; } addPartyCurePhase(user: Pokemon) { @@ -1582,16 +1621,16 @@ export class SandHealAttr extends WeatherHealAttr { */ export class BoostHealAttr extends HealAttr { /** Healing received when {@linkcode condition} is false */ - private normalHealRatio?: number; + private normalHealRatio: number; /** Healing received when {@linkcode condition} is true */ - private boostedHealRatio?: number; + private boostedHealRatio: number; /** The lambda expression to check against when boosting the healing value */ private condition?: MoveConditionFunc; constructor(normalHealRatio?: number, boostedHealRatio?: number, showAnim?: boolean, selfTarget?: boolean, condition?: MoveConditionFunc) { super(normalHealRatio, showAnim, selfTarget); - this.normalHealRatio = normalHealRatio; - this.boostedHealRatio = boostedHealRatio; + this.normalHealRatio = normalHealRatio!; // TODO: is this bang correct? + this.boostedHealRatio = boostedHealRatio!; // TODO: is this bang correct? this.condition = condition; } @@ -1603,7 +1642,7 @@ export class BoostHealAttr extends HealAttr { * @returns true if the move was successful */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const healRatio = this.condition(user, target, move) ? this.boostedHealRatio : this.normalHealRatio; + const healRatio: number = (this.condition ? this.condition(user, target, move) : false) ? this.boostedHealRatio : this.normalHealRatio; this.addHealPhase(target, healRatio); return true; } @@ -1642,13 +1681,13 @@ export class HealOnAllyAttr extends HealAttr { export class HitHealAttr extends MoveEffectAttr { private healRatio: number; private message: string; - private healStat: Stat; + private healStat: Stat | null; - constructor(healRatio?: number, healStat?: Stat) { + constructor(healRatio?: number | null, healStat?: Stat) { super(true, MoveEffectTrigger.HIT); - this.healRatio = healRatio || 0.5; - this.healStat = healStat || null; + this.healRatio = healRatio ?? 0.5; + this.healStat = healStat ?? null; } /** * Heals the user the determined amount and possibly displays a message about regaining health. @@ -1664,7 +1703,7 @@ export class HitHealAttr extends MoveEffectAttr { let healAmount = 0; let message = ""; const reverseDrain = target.hasAbilityWithAttr(ReverseDrainAbAttr, false); - if (this.healStat) { + if (this.healStat !== null) { // Strength Sap formula healAmount = target.getBattleStat(this.healStat); message = i18next.t("battle:drainMessage", {pokemonName: getPokemonNameWithAffix(target)}); @@ -1676,11 +1715,11 @@ export class HitHealAttr extends MoveEffectAttr { if (reverseDrain) { if (user.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { healAmount = 0; - message = null; + message = ""; } else { user.turnData.damageTaken += healAmount; healAmount = healAmount * -1; - message = null; + message = ""; } } user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(), healAmount, message, false, true)); @@ -1849,14 +1888,14 @@ export class WaterShurikenMultiHitTypeAttr extends ChangeMultiHitTypeAttr { export class StatusEffectAttr extends MoveEffectAttr { public effect: StatusEffect; - public cureTurn: integer; + public cureTurn: integer | null; public overrideStatus: boolean; constructor(effect: StatusEffect, selfTarget?: boolean, cureTurn?: integer, overrideStatus?: boolean) { super(selfTarget, MoveEffectTrigger.HIT); this.effect = effect; - this.cureTurn = cureTurn; + this.cureTurn = cureTurn!; // TODO: is this bang correct? this.overrideStatus = !!overrideStatus; } @@ -1874,7 +1913,7 @@ export class StatusEffectAttr extends MoveEffectAttr { } if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0)) && pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) { - applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null,this.effect); + applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, this.effect); return true; } } @@ -1913,12 +1952,13 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const statusToApply: StatusEffect = user.status?.effect ?? (user.hasAbility(Abilities.COMATOSE) ? StatusEffect.SLEEP : undefined); + const statusToApply: StatusEffect | undefined = user.status?.effect ?? (user.hasAbility(Abilities.COMATOSE) ? StatusEffect.SLEEP : undefined); if (target.status) { return false; } - if (!target.status || (target.status.effect === statusToApply && move.chance < 0)) { + //@ts-ignore - how can target.status.effect be checked when we return `false` before when it's defined? + if (!target.status || (target.status.effect === statusToApply && move.chance < 0)) { // TODO: resolve ts-ignore const statusAfflictResult = target.trySetStatus(statusToApply, true, user); if (statusAfflictResult) { if (user.status) { @@ -1959,7 +1999,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferrable); if (heldItems.length) { const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD; - const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier, highestTier), 0); + const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct? const tierHeldItems = heldItems.filter(m => m.type.getOrInferTier(poolType) === highestItemTier); const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)]; user.scene.tryTransferHeldItemModifier(stolenItem, user, false).then(success => { @@ -2072,10 +2112,9 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { * Attribute that causes targets of the move to eat a berry. Used for Teatime, Stuff Cheeks */ export class EatBerryAttr extends MoveEffectAttr { - protected chosenBerry: BerryModifier; + protected chosenBerry: BerryModifier | undefined; constructor() { super(true, MoveEffectTrigger.HIT); - this.chosenBerry = undefined; } /** * Causes the target to eat a berry. @@ -2110,16 +2149,16 @@ export class EatBerryAttr extends MoveEffectAttr { } reduceBerryModifier(target: Pokemon) { - if (this.chosenBerry.stackCount === 1) { + if (this.chosenBerry?.stackCount === 1) { target.scene.removeModifier(this.chosenBerry, !target.isPlayer()); - } else { + } else if (this.chosenBerry !== undefined && this.chosenBerry.stackCount > 1) { this.chosenBerry.stackCount--; } target.scene.updateModifiers(target.isPlayer()); } eatBerry(consumer: Pokemon) { - getBerryEffectFunc(this.chosenBerry.berryType)(consumer); // consumer eats the berry + getBerryEffectFunc(this.chosenBerry!.berryType)(consumer); // consumer eats the berry applyAbAttrs(HealFromBerryUseAbAttr, consumer, new Utils.BooleanHolder(false)); } } @@ -2350,20 +2389,20 @@ export class OverrideMoveEffectAttr extends MoveAttr { export class ChargeAttr extends OverrideMoveEffectAttr { public chargeAnim: ChargeAnim; private chargeText: string; - private tagType: BattlerTagType; + private tagType: BattlerTagType | null; private chargeEffect: boolean; public sameTurn: boolean; - public followUpPriority: integer; + public followUpPriority: integer | null; - constructor(chargeAnim: ChargeAnim, chargeText: string, tagType?: BattlerTagType, chargeEffect: boolean = false, sameTurn: boolean = false, followUpPriority?: integer) { + constructor(chargeAnim: ChargeAnim, chargeText: string, tagType?: BattlerTagType | null, chargeEffect: boolean = false, sameTurn: boolean = false, followUpPriority?: integer) { super(); this.chargeAnim = chargeAnim; this.chargeText = chargeText; - this.tagType = tagType; + this.tagType = tagType!; // TODO: is this bang correct? this.chargeEffect = chargeEffect; this.sameTurn = sameTurn; - this.followUpPriority = followUpPriority; + this.followUpPriority = followUpPriority!; // TODO: is this bang correct? } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { @@ -2382,11 +2421,11 @@ export class ChargeAttr extends OverrideMoveEffectAttr { user.pushMoveHistory({ move: move.id, targets: [ target.getBattlerIndex() ], result: MoveResult.OTHER }); user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true }); if (this.sameTurn) { - let movesetMove = user.moveset.find(m => m.moveId === move.id); + let movesetMove = user.moveset.find(m => m?.moveId === move.id); if (!movesetMove) { // account for any move that calls a ChargeAttr move when the ChargeAttr move does not exist in moveset movesetMove = new PokemonMove(move.id, 0, 0, true); } - user.scene.pushMovePhase(new MovePhase(user.scene, user, [ target.getBattlerIndex() ], movesetMove, true), this.followUpPriority); + user.scene.pushMovePhase(new MovePhase(user.scene, user, [ target.getBattlerIndex() ], movesetMove, true), this.followUpPriority!); // TODO: is this bang correct? } user.addTag(BattlerTagType.CHARGING, 1, move.id, user.id); resolve(true); @@ -2398,7 +2437,7 @@ export class ChargeAttr extends OverrideMoveEffectAttr { }); } - usedChargeEffect(user: Pokemon, target: Pokemon, move: Move): boolean { + usedChargeEffect(user: Pokemon, target: Pokemon | null, move: Move): boolean { if (!this.chargeEffect) { return false; } @@ -2487,7 +2526,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { resolve(true); }); } else { - user.scene.ui.showText(i18next.t("moveTriggers:tookMoveAttack", {pokemonName: getPokemonNameWithAffix(user.scene.getPokemonById(target.id)), moveName: move.name}), null, () => resolve(true)); + user.scene.ui.showText(i18next.t("moveTriggers:tookMoveAttack", {pokemonName: getPokemonNameWithAffix(user.scene.getPokemonById(target.id) ?? undefined), moveName: move.name}), null, () => resolve(true)); } }); } @@ -2496,20 +2535,20 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { export class StatChangeAttr extends MoveEffectAttr { public stats: BattleStat[]; public levels: integer; - private condition: MoveConditionFunc; + private condition: MoveConditionFunc | null; private showMessage: boolean; - constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false) { + constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false) { super(selfTarget, moveEffectTrigger, firstHitOnly, false, firstTargetOnly); this.stats = typeof(stats) === "number" ? [ stats as BattleStat ] : stats as BattleStat[]; this.levels = levels; - this.condition = condition || null; + this.condition = condition!; // TODO: is this bang correct? this.showMessage = showMessage; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { + apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise { if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move))) { return false; } @@ -2573,7 +2612,7 @@ export class StatChangeAttr extends MoveEffectAttr { export class PostVictoryStatChangeAttr extends MoveAttr { private stats: BattleStat[]; private levels: integer; - private condition: MoveConditionFunc; + private condition: MoveConditionFunc | null; private showMessage: boolean; constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) { @@ -2582,7 +2621,7 @@ export class PostVictoryStatChangeAttr extends MoveAttr { ? [ stats as BattleStat ] : stats as BattleStat[]; this.levels = levels; - this.condition = condition || null; + this.condition = condition!; // TODO: is this bang correct? this.showMessage = showMessage; } applyPostVictory(user: Pokemon, target: Pokemon, move: Move): void { @@ -2590,7 +2629,7 @@ export class PostVictoryStatChangeAttr extends MoveAttr { return; } const statChangeAttr = new StatChangeAttr(this.stats, this.levels, this.showMessage); - statChangeAttr.apply(user, target, move, undefined); + statChangeAttr.apply(user, target, move); } } @@ -2750,7 +2789,7 @@ export class HpSplitAttr extends MoveEffectAttr { return resolve(false); } - const infoUpdates = []; + const infoUpdates: Promise[] = []; const hpValue = Math.floor((target.hp + user.hp) / 2); if (user.hp < hpValue) { @@ -2802,7 +2841,7 @@ export class LessPPMorePowerAttr extends VariablePowerAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const ppMax = move.pp; - const ppUsed = user.moveset.find((m) => m.moveId === move.id).ppUsed; + const ppUsed = user.moveset.find((m) => m?.moveId === move.id)?.ppUsed!; // TODO: is the bang correct? let ppRemains = ppMax - ppUsed; /** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */ @@ -2869,6 +2908,7 @@ const beatUpFunc = (user: Pokemon, allyIndex: number): number => { } return (pokemon.species.getBaseStat(Stat.ATK) / 10) + 5; } + return 0; }; export class BeatUpAttr extends VariablePowerAttr { @@ -2895,7 +2935,7 @@ export class BeatUpAttr extends VariablePowerAttr { } const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { - let message: string = null; + let message: string = ""; user.scene.executeWithSeedOffset(() => { const rand = Utils.randSeedInt(100); if (rand < move.chance) { @@ -2909,7 +2949,7 @@ export class DoublePowerChanceAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { let rand: integer; user.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100), user.scene.currentBattle.turn << 6, user.scene.waveSeed); - if (rand < move.chance) { + if (rand! < move.chance) { const power = args[0] as Utils.NumberHolder; power.value *= 2; return true; @@ -2925,9 +2965,9 @@ export abstract class ConsecutiveUsePowerMultiplierAttr extends MovePowerMultipl const moveHistory = user.getLastXMoves(limit + 1).slice(1); let count = 0; - let turnMove: TurnMove; + let turnMove: TurnMove | undefined; - while (((turnMove = moveHistory.shift())?.move === move.id || (comboMoves.length && comboMoves.includes(turnMove?.move))) && (!resetOnFail || turnMove.result === MoveResult.SUCCESS)) { + while (((turnMove = moveHistory.shift())?.move === move.id || (comboMoves.length && comboMoves.includes(turnMove?.move!))) && (!resetOnFail || turnMove?.result === MoveResult.SUCCESS)) { // TODO: is this bang correct? if (count < (limit - 1)) { count++; } else if (resetOnLimit) { @@ -3180,7 +3220,7 @@ const magnitudeMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { message = i18next.t("moveTriggers:magnitudeMessage", {magnitude: m + 4}); }, user.scene.currentBattle.turn << 6, user.scene.waveSeed); - return message; + return message!; }; export class MagnitudePowerAttr extends VariablePowerAttr { @@ -3196,7 +3236,7 @@ export class MagnitudePowerAttr extends VariablePowerAttr { let m = 0; for (; m < magnitudeThresholds.length; m++) { - if (rand < magnitudeThresholds[m]) { + if (rand! < magnitudeThresholds[m]) { break; } } @@ -3359,7 +3399,7 @@ export class SpitUpPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const stockpilingTag = user.getTag(StockpilingTag); - if (stockpilingTag?.stockpiledCount > 0) { + if (stockpilingTag !== null && stockpilingTag.stockpiledCount > 0) { const power = args[0] as Utils.IntegerHolder; power.value = this.multiplier * stockpilingTag.stockpiledCount; return true; @@ -3377,7 +3417,7 @@ export class SwallowHealAttr extends HealAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const stockpilingTag = user.getTag(StockpilingTag); - if (stockpilingTag?.stockpiledCount > 0) { + if (stockpilingTag !== null && stockpilingTag?.stockpiledCount > 0) { const stockpiled = stockpilingTag.stockpiledCount; let healRatio: number; @@ -3399,7 +3439,10 @@ export class SwallowHealAttr extends HealAttr { } } -const hasStockpileStacksCondition: MoveConditionFunc = (user) => user.getTag(StockpilingTag)?.stockpiledCount > 0; +const hasStockpileStacksCondition: MoveConditionFunc = (user) => { + const hasStockpilingTag = user.getTag(StockpilingTag); + return !!hasStockpilingTag && hasStockpilingTag.stockpiledCount > 0; +}; /** * Attribute used for multi-hit moves that increase power in increments of the @@ -3470,13 +3513,13 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { const enemy = user.getOpponent(0); const pokemonActed: Pokemon[] = []; - if (enemy.turnData.acted) { + if (enemy?.turnData.acted) { pokemonActed.push(enemy); } if (user.scene.currentBattle.double) { const userAlly = user.getAlly(); - const enemyAlly = enemy.getAlly(); + const enemyAlly = enemy?.getAlly(); if (userAlly && userAlly.turnData.acted) { pokemonActed.push(userAlly); @@ -3565,6 +3608,9 @@ export class VariableAccuracyAttr extends MoveAttr { } } +/** + * Attribute used for Thunder and Hurricane that sets accuracy to 50 in sun and never miss in rain + */ export class ThunderAccuracyAttr extends VariableAccuracyAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) { @@ -3572,7 +3618,6 @@ export class ThunderAccuracyAttr extends VariableAccuracyAttr { const weatherType = user.scene.arena.weather?.weatherType || WeatherType.NONE; switch (weatherType) { case WeatherType.SUNNY: - case WeatherType.SANDSTORM: case WeatherType.HARSH_SUN: accuracy.value = 50; return true; @@ -3587,6 +3632,28 @@ export class ThunderAccuracyAttr extends VariableAccuracyAttr { } } +/** + * Attribute used for Bleakwind Storm, Wildbolt Storm, and Sandsear Storm that sets accuracy to never + * miss in rain + * Springtide Storm does NOT have this property + */ +export class StormAccuracyAttr extends VariableAccuracyAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) { + const accuracy = args[0] as Utils.NumberHolder; + const weatherType = user.scene.arena.weather?.weatherType || WeatherType.NONE; + switch (weatherType) { + case WeatherType.RAIN: + case WeatherType.HEAVY_RAIN: + accuracy.value = -1; + return true; + } + } + + return false; + } +} + /** * Attribute used for moves which never miss * against Pokemon with the {@linkcode BattlerTagType.MINIMIZED} @@ -3726,7 +3793,7 @@ export class VariableMoveTypeAttr extends MoveAttr { export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.ARCEUS) || [user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.SILVALLY)) { - const form = user.species.speciesId === Species.ARCEUS || user.species.speciesId === Species.SILVALLY ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.ARCEUS || user.species.speciesId === Species.SILVALLY ? user.formIndex : user.fusionSpecies?.formIndex!; // TODO: is this bang correct? move.type = Type[Type[form]]; return true; @@ -3739,7 +3806,7 @@ export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { export class TechnoBlastTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.GENESECT)) { - const form = user.species.speciesId === Species.GENESECT ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.GENESECT ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { case 1: // Shock Drive @@ -3768,7 +3835,7 @@ export class TechnoBlastTypeAttr extends VariableMoveTypeAttr { export class AuraWheelTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)) { - const form = user.species.speciesId === Species.MORPEKO ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.MORPEKO ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { case 1: // Hangry Mode @@ -3788,7 +3855,7 @@ export class AuraWheelTypeAttr extends VariableMoveTypeAttr { export class RagingBullTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.PALDEA_TAUROS)) { - const form = user.species.speciesId === Species.PALDEA_TAUROS ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.PALDEA_TAUROS ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { case 1: // Blaze breed @@ -3811,7 +3878,7 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr { export class IvyCudgelTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.OGERPON)) { - const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { case 1: // Wellspring Mask @@ -4100,23 +4167,23 @@ export class DisableMoveAttr extends MoveEffectAttr { } const moveQueue = target.getLastXMoves(); - let turnMove: TurnMove; + let turnMove: TurnMove | undefined; while (moveQueue.length) { turnMove = moveQueue.shift(); - if (turnMove.virtual) { + if (turnMove?.virtual) { continue; } - const moveIndex = target.getMoveset().findIndex(m => m.moveId === turnMove.move); + const moveIndex = target.getMoveset().findIndex(m => m?.moveId === turnMove?.move); if (moveIndex === -1) { return false; } const disabledMove = target.getMoveset()[moveIndex]; - target.summonData.disabledMove = disabledMove.moveId; + target.summonData.disabledMove = disabledMove?.moveId!; // TODO: is this bang correct? target.summonData.disabledTurns = 4; - user.scene.queueMessage(i18next.t("abilityTriggers:postDefendMoveDisable", { pokemonNameWithAffix: getPokemonNameWithAffix(target), moveName: disabledMove.getName()})); + user.scene.queueMessage(i18next.t("abilityTriggers:postDefendMoveDisable", { pokemonNameWithAffix: getPokemonNameWithAffix(target), moveName: disabledMove?.getName()})); return true; } @@ -4125,26 +4192,28 @@ export class DisableMoveAttr extends MoveEffectAttr { } getCondition(): MoveConditionFunc { - return (user, target, move) => { + return (user, target, move): boolean => { // TODO: Not sure what to do here if (target.summonData.disabledMove || target.isMax()) { return false; } const moveQueue = target.getLastXMoves(); - let turnMove: TurnMove; + let turnMove: TurnMove | undefined; while (moveQueue.length) { turnMove = moveQueue.shift(); - if (turnMove.virtual) { + if (turnMove?.virtual) { continue; } - const move = target.getMoveset().find(m => m.moveId === turnMove.move); + const move = target.getMoveset().find(m => m?.moveId === turnMove?.move); if (!move) { continue; } return true; } + + return false; }; } @@ -4217,13 +4286,13 @@ export class AddBattlerTagAttr extends MoveEffectAttr { return false; } - getCondition(): MoveConditionFunc { + getCondition(): MoveConditionFunc | null { return this.failOnOverlap ? (user, target, move) => !(this.selfTarget ? user : target).getTag(this.tagType) : null; } - getTagTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { + getTagTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer | void { switch (this.tagType) { case BattlerTagType.RECHARGING: case BattlerTagType.PERISH_SONG: @@ -4233,7 +4302,6 @@ export class AddBattlerTagAttr extends MoveEffectAttr { case BattlerTagType.INFATUATED: case BattlerTagType.NIGHTMARE: case BattlerTagType.DROWSY: - case BattlerTagType.NO_CRIT: return -5; case BattlerTagType.SEEDED: case BattlerTagType.SALT_CURED: @@ -4272,7 +4340,47 @@ export class AddBattlerTagAttr extends MoveEffectAttr { if (moveChance < 0) { moveChance = 100; } - return Math.floor(this.getTagTargetBenefitScore(user, target, move) * (moveChance / 100)); + return Math.floor(this.getTagTargetBenefitScore(user, target, move)! * (moveChance / 100)); // TODO: is the bang correct? + } +} + +/** + * Adds the appropriate battler tag for Gulp Missile when Surf or Dive is used. + * @extends MoveEffectAttr + */ +export class GulpMissileTagAttr extends MoveEffectAttr { + constructor() { + super(true, MoveEffectTrigger.POST_APPLY); + } + + /** + * Adds BattlerTagType from GulpMissileTag based on the Pokemon's HP ratio. + * @param {Pokemon} user The Pokemon using the move. + * @param {Pokemon} target The Pokemon being targeted by the move. + * @param {Move} move The move being used. + * @param {any[]} args Additional arguments, if any. + * @returns Whether the BattlerTag is applied. + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { + if (!super.apply(user, target, move, args)) { + return false; + } + + if (user.hasAbility(Abilities.GULP_MISSILE) && user.species.speciesId === Species.CRAMORANT) { + if (user.getHpRatio() >= .5) { + user.addTag(BattlerTagType.GULP_MISSILE_ARROKUDA, 0, move.id); + } else { + user.addTag(BattlerTagType.GULP_MISSILE_PIKACHU, 0, move.id); + } + return true; + } + + return false; + } + + getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { + const isCramorant = user.hasAbility(Abilities.GULP_MISSILE) && user.species.speciesId === Species.CRAMORANT; + return isCramorant && !user.getTag(GulpMissileTag) ? 10 : 0; } } @@ -4380,11 +4488,11 @@ export class ProtectAttr extends AddBattlerTagAttr { return ((user, target, move): boolean => { let timesUsed = 0; const moveHistory = user.getLastXMoves(); - let turnMove: TurnMove; + let turnMove: TurnMove | undefined; while (moveHistory.length) { turnMove = moveHistory.shift(); - if (!allMoves[turnMove.move].hasAttr(ProtectAttr) || turnMove.result !== MoveResult.SUCCESS) { + if (!allMoves[turnMove?.move!].hasAttr(ProtectAttr) || turnMove?.result !== MoveResult.SUCCESS) { // TODO: is the bang correct? break; } timesUsed++; @@ -4457,11 +4565,11 @@ export class AddArenaTagAttr extends MoveEffectAttr { private failOnOverlap: boolean; public selfSideTarget: boolean; - constructor(tagType: ArenaTagType, turnCount?: integer, failOnOverlap: boolean = false, selfSideTarget: boolean = false) { + constructor(tagType: ArenaTagType, turnCount?: integer | null, failOnOverlap: boolean = false, selfSideTarget: boolean = false) { super(true, MoveEffectTrigger.POST_APPLY); this.tagType = tagType; - this.turnCount = turnCount; + this.turnCount = turnCount!; // TODO: is the bang correct? this.failOnOverlap = failOnOverlap; this.selfSideTarget = selfSideTarget; } @@ -4479,7 +4587,7 @@ export class AddArenaTagAttr extends MoveEffectAttr { return false; } - getCondition(): MoveConditionFunc { + getCondition(): MoveConditionFunc | null { return this.failOnOverlap ? (user, target, move) => !user.scene.arena.getTagOnSide(this.tagType, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY) : null; @@ -4652,13 +4760,13 @@ export class SwapArenaTagsAttr extends MoveEffectAttr { if (tagPlayerTemp) { for (const swapTagsType of tagPlayerTemp) { user.scene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.PLAYER, true); - user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId, ArenaTagSide.ENEMY, true); + user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId!, ArenaTagSide.ENEMY, true); // TODO: is the bang correct? } } if (tagEnemyTemp) { for (const swapTagsType of tagEnemyTemp) { user.scene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.ENEMY, true); - user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId, ArenaTagSide.PLAYER, true); + user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId!, ArenaTagSide.PLAYER, true); // TODO: is the bang correct? } } @@ -4741,13 +4849,15 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { this.batonPass = !!batonPass; } + isBatonPass() { + return this.batonPass; + } + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { // Check if the move category is not STATUS or if the switch out condition is not met if (!this.getSwitchOutCondition()(user, target, move)) { - //Apply effects before switch out i.e. poison point, flame body, etc - applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, move, null); return resolve(false); } @@ -4755,10 +4865,11 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { // This ensures that the switch out only happens when the conditions are met const switchOutTarget = this.user ? user : target; if (switchOutTarget instanceof PlayerPokemon) { + switchOutTarget.leaveField(!this.batonPass); + if (switchOutTarget.hp > 0) { - applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, switchOutTarget); - // switchOut below sets the UI to select party(this is not a separate Phase), then adds a SwitchSummonPhase with selected 'mon - (switchOutTarget as PlayerPokemon).switchOut(this.batonPass).then(() => resolve(true)); + user.scene.prependToPhase(new SwitchPhase(user.scene, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); + resolve(true); } else { resolve(false); } @@ -4769,16 +4880,21 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (switchOutTarget.hp > 0) { // for opponent switching out - user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(), user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot), false, this.batonPass, false), MoveEndPhase); + user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(), (user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), false, this.batonPass, false), MoveEndPhase); } } else { - // Switch out logic for everything else - switchOutTarget.setVisible(false); + // Switch out logic for everything else (eg: WILD battles) + switchOutTarget.leaveField(false); if (switchOutTarget.hp) { - switchOutTarget.hideInfo().then(() => switchOutTarget.destroy()); - switchOutTarget.scene.field.remove(switchOutTarget); + switchOutTarget.setWildFlee(true); user.scene.queueMessage(i18next.t("moveTriggers:fled", {pokemonName: getPokemonNameWithAffix(switchOutTarget)}), null, true, 500); + + // in double battles redirect potential moves off fled pokemon + if (switchOutTarget.scene.currentBattle.double) { + const allyPokemon = switchOutTarget.getAlly(); + switchOutTarget.scene.redirectPokemonMoves(switchOutTarget, allyPokemon); + } } if (!switchOutTarget.getAlly()?.isActive(true)) { @@ -4859,7 +4975,7 @@ export class RemoveTypeAttr extends MoveEffectAttr { return false; } - if (user.isTerastallized && user.getTeraType() === this.removedType) { // active tera types cannot be removed + if (user.isTerastallized() && user.getTeraType() === this.removedType) { // active tera types cannot be removed return false; } @@ -4981,7 +5097,7 @@ export class FirstMoveTypeAttr extends MoveEffectAttr { return false; } - const firstMoveType = target.getMoveset()[0].getMove().type; + const firstMoveType = target.getMoveset()[0]?.getMove().type!; // TODO: is this bang correct? user.summonData.types = [ firstMoveType ]; user.scene.queueMessage(i18next.t("battle:transformedIntoType", {pokemonName: getPokemonNameWithAffix(user), type: i18next.t(`pokemonInfo:Type.${Type[firstMoveType]}`)})); @@ -4990,21 +5106,21 @@ export class FirstMoveTypeAttr extends MoveEffectAttr { } export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr { - private enemyMoveset: boolean; + private enemyMoveset: boolean | null; constructor(enemyMoveset?: boolean) { super(); - this.enemyMoveset = enemyMoveset; + this.enemyMoveset = enemyMoveset!; // TODO: is this bang correct? } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveset = (!this.enemyMoveset ? user : target).getMoveset(); - const moves = moveset.filter(m => !m.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL)); + const moves = moveset.filter(m => !m?.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL)); if (moves.length) { const move = moves[user.randSeedInt(moves.length)]; - const moveIndex = moveset.findIndex(m => m.moveId === move.moveId); - const moveTargets = getMoveTargets(user, move.moveId); + const moveIndex = moveset.findIndex(m => m?.moveId === move?.moveId); + const moveTargets = getMoveTargets(user, move?.moveId!); // TODO: is this bang correct? if (!moveTargets.targets.length) { return false; } @@ -5025,8 +5141,8 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr { } } const targets = selectTargets; - user.getMoveQueue().push({ move: move.moveId, targets: targets, ignorePP: true }); - user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, moveset[moveIndex], true)); + user.getMoveQueue().push({ move: move?.moveId!, targets: targets, ignorePP: true }); // TODO: is this bang correct? + user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, moveset[moveIndex]!, true)); // There's a PR to re-do the move(s) that use this Attr, gonna put `!` for now return true; } @@ -5268,12 +5384,12 @@ export class ReducePpMoveAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { // Null checks can be skipped due to condition function const lastMove = target.getLastXMoves().find(() => true); - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move); - const lastPpUsed = movesetMove.ppUsed; - movesetMove.ppUsed = Math.min(movesetMove.ppUsed + this.reduction, movesetMove.getMovePp()); - - const message = i18next.t("battle:ppReduced", {targetName: getPokemonNameWithAffix(target), moveName: movesetMove.getName(), reduction: movesetMove.ppUsed - lastPpUsed}); + const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove?.move); + const lastPpUsed = movesetMove?.ppUsed!; // TODO: is the bang correct? + movesetMove!.ppUsed = Math.min((movesetMove?.ppUsed!) + this.reduction, movesetMove?.getMovePp()!); // TODO: is the bang correct? + const message = i18next.t("battle:ppReduced", {targetName: getPokemonNameWithAffix(target), moveName: movesetMove?.getName(), reduction: (movesetMove?.ppUsed!) - lastPpUsed}); // TODO: is the bang correct? + user.scene.eventTarget.dispatchEvent(new MoveUsedEvent(target?.id, movesetMove?.getMove()!, movesetMove?.ppUsed!)); // TODO: are these bangs correct? user.scene.queueMessage(message); return true; @@ -5283,7 +5399,7 @@ export class ReducePpMoveAttr extends MoveEffectAttr { return (user, target, move) => { const lastMove = target.getLastXMoves().find(() => true); if (lastMove) { - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move); + const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move); return !!movesetMove?.getPpRatio(); } return false; @@ -5293,7 +5409,7 @@ export class ReducePpMoveAttr extends MoveEffectAttr { getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { const lastMove = target.getLastXMoves().find(() => true); if (lastMove) { - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move); + const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move); if (movesetMove) { const maxPp = movesetMove.getMovePp(); const ppLeft = maxPp - movesetMove.ppUsed; @@ -5330,7 +5446,7 @@ export class AttackReducePpMoveAttr extends ReducePpMoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const lastMove = target.getLastXMoves().find(() => true); if (lastMove) { - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move); + const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move); if (Boolean(movesetMove?.getPpRatio())) { super.apply(user, target, move, args); } @@ -5376,7 +5492,7 @@ export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr { const copiedMove = allMoves[targetMoves[0].move]; - const thisMoveIndex = user.getMoveset().findIndex(m => m.moveId === move.id); + const thisMoveIndex = user.getMoveset().findIndex(m => m?.moveId === move.id); if (thisMoveIndex === -1) { return false; @@ -5395,10 +5511,26 @@ export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr { } } +/** + * Attribute for {@linkcode Moves.SKETCH} that causes the user to copy the opponent's last used move + * This move copies the last used non-virtual move + * e.g. if Metronome is used, it copies Metronome itself, not the virtual move called by Metronome + * Fails if the opponent has not yet used a move. + * Fails if used on an uncopiable move, listed in unsketchableMoves in getCondition + * Fails if the move is already in the user's moveset + */ export class SketchAttr extends MoveEffectAttr { constructor() { super(true); } + /** + * User copies the opponent's last used move, if possible + * @param {Pokemon} user Pokemon that used the move and will replace Sketch with the copied move + * @param {Pokemon} target Pokemon that the user wants to copy a move from + * @param {Move} move Move being used + * @param {any[]} args Unused + * @returns {boolean} true if the function succeeds, otherwise false + */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!super.apply(user, target, move, args)) { @@ -5411,7 +5543,7 @@ export class SketchAttr extends MoveEffectAttr { } const sketchedMove = allMoves[targetMove.move]; - const sketchIndex = user.getMoveset().findIndex(m => m.moveId === move.id); + const sketchIndex = user.getMoveset().findIndex(m => m?.moveId === move.id); if (sketchIndex === -1) { return false; } @@ -5429,14 +5561,28 @@ export class SketchAttr extends MoveEffectAttr { return false; } - const targetMoves = target.getMoveHistory().filter(m => !m.virtual); - if (!targetMoves.length) { + const targetMove = target.getMoveHistory().filter(m => !m.virtual).at(-1); + if (!targetMove) { return false; } - const sketchableMove = targetMoves[0]; + const unsketchableMoves = [ + Moves.CHATTER, + Moves.MIRROR_MOVE, + Moves.SLEEP_TALK, + Moves.STRUGGLE, + Moves.SKETCH, + Moves.REVIVAL_BLESSING, + Moves.TERA_STARSTORM, + Moves.BREAKNECK_BLITZ__PHYSICAL, + Moves.BREAKNECK_BLITZ__SPECIAL + ]; - if (user.getMoveset().find(m => m.moveId === sketchableMove.move)) { + if (unsketchableMoves.includes(targetMove.move)) { + return false; + } + + if (user.getMoveset().find(m => m?.moveId === targetMove.move)) { return false; } @@ -5628,7 +5774,7 @@ export class TransformAttr extends MoveEffectAttr { user.summonData.fusionGender = target.getFusionGender(); user.summonData.stats = [ user.stats[Stat.HP] ].concat(target.stats.slice(1)); user.summonData.battleStats = target.summonData.battleStats.slice(0); - user.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m.moveId, m.ppUsed, m.ppUp)); + user.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m?.moveId!, m?.ppUsed, m?.ppUp)); // TODO: is this bang correct? user.summonData.types = target.getTypes(); user.scene.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", {pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target)})); @@ -5696,7 +5842,7 @@ export class LastResortAttr extends MoveAttr { getCondition(): MoveConditionFunc { return (user: Pokemon, target: Pokemon, move: Move) => { const uniqueUsedMoveIds = new Set(); - const movesetMoveIds = user.getMoveset().map(m => m.moveId); + const movesetMoveIds = user.getMoveset().map(m => m?.moveId); user.getMoveHistory().map(m => { if (m.move !== move.id && movesetMoveIds.find(mm => mm === m.move)) { uniqueUsedMoveIds.add(m.move); @@ -5770,7 +5916,7 @@ const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target export type MoveAttrFilter = (attr: MoveAttr) => boolean; -function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { +function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): Promise { return new Promise(resolve => { const attrPromises: Promise[] = []; const moveAttrs = move.attrs.filter(a => attrFilter(a)); @@ -5784,11 +5930,11 @@ function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon, targe }); } -export function applyMoveAttrs(attrType: Constructor, user: Pokemon, target: Pokemon, move: Move, ...args: any[]): Promise { +export function applyMoveAttrs(attrType: Constructor, user: Pokemon | null, target: Pokemon | null, move: Move, ...args: any[]): Promise { return applyMoveAttrsInternal((attr: MoveAttr) => attr instanceof attrType, user, target, move, args); } -export function applyFilteredMoveAttrs(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon, move: Move, ...args: any[]): Promise { +export function applyFilteredMoveAttrs(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon | null, move: Move, ...args: any[]): Promise { return applyMoveAttrsInternal(attrFilter, user, target, move, args); } @@ -5885,6 +6031,39 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr { } } +/** + * Drops the target's immunity to types it is immune to + * and makes its evasiveness be ignored during accuracy + * checks. Used by: {@linkcode Moves.ODOR_SLEUTH | Odor Sleuth}, {@linkcode Moves.MIRACLE_EYE | Miracle Eye} and {@linkcode Moves.FORESIGHT | Foresight} + * + * @extends AddBattlerTagAttr + * @see {@linkcode apply} + */ +export class ExposedMoveAttr extends AddBattlerTagAttr { + constructor(tagType: BattlerTagType) { + super(tagType, false, true); + } + + /** + * Applies {@linkcode ExposedTag} to the target. + * @param user {@linkcode Pokemon} using this move + * @param target {@linkcode Pokemon} target of this move + * @param move {@linkcode Move} being used + * @param args N/A + * @returns `true` if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) { + return false; + } + + user.scene.queueMessage(i18next.t("moveTriggers:exposedMove", { pokemonName: getPokemonNameWithAffix(user), targetPokemonName: getPokemonNameWithAffix(target)})); + + return true; + } +} + + const unknownTypeCondition: MoveConditionFunc = (user, target, move) => !user.getTypes().includes(Type.UNKNOWN); export type MoveTargetSet = { @@ -6106,7 +6285,8 @@ export function initMoves() { new AttackMove(Moves.HYDRO_PUMP, Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, -1, 0, 1), new AttackMove(Moves.SURF, Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 1) .target(MoveTarget.ALL_NEAR_OTHERS) - .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true), + .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true) + .attr(GulpMissileTagAttr), new AttackMove(Moves.ICE_BEAM, Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1) .attr(StatusEffectAttr, StatusEffect.FREEZE), new AttackMove(Moves.BLIZZARD, Type.ICE, MoveCategory.SPECIAL, 110, 70, 5, 10, 0, 1) @@ -6480,7 +6660,7 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.PARALYSIS) .ballBombMove(), new StatusMove(Moves.FORESIGHT, Type.NORMAL, -1, 40, -1, 0, 2) - .unimplemented(), + .attr(ExposedMoveAttr, BattlerTagType.IGNORE_GHOST), new SelfStatusMove(Moves.DESTINY_BOND, Type.GHOST, -1, 5, -1, 0, 2) .ignoresProtect() .attr(DestinyBondAttr), @@ -6648,6 +6828,7 @@ export function initMoves() { .attr(StatChangeAttr, BattleStat.SPDEF, -1) .ballBombMove(), new AttackMove(Moves.FUTURE_SIGHT, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 2) + .partial() .attr(DelayedAttackAttr, ArenaTagType.FUTURE_SIGHT, ChargeAnim.FUTURE_SIGHT_CHARGING, i18next.t("moveTriggers:foresawAnAttack", {pokemonName: "{USER}"})), new AttackMove(Moves.ROCK_SMASH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, 50, 0, 2) .attr(StatChangeAttr, BattleStat.DEF, -1), @@ -6701,6 +6882,7 @@ export function initMoves() { && (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1) .attr(BypassBurnDamageReductionAttr), new AttackMove(Moves.FOCUS_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, -1, -3, 3) + .attr(MessageHeaderAttr, (user, move) => i18next.t("moveTriggers:isTighteningFocus", {pokemonName: getPokemonNameWithAffix(user)})) .punchingMove() .ignoresVirtual() .condition((user, target, move) => !user.turnData.attacksReceived.find(r => r.damage)), @@ -6760,7 +6942,7 @@ export function initMoves() { .unimplemented(), new SelfStatusMove(Moves.REFRESH, Type.NORMAL, -1, 20, -1, 0, 3) .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN) - .condition((user, target, move) => user.status && (user.status.effect === StatusEffect.PARALYSIS || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.BURN)), + .condition((user, target, move) => !!user.status && (user.status.effect === StatusEffect.PARALYSIS || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.BURN)), new SelfStatusMove(Moves.GRUDGE, Type.GHOST, -1, 5, -1, 0, 3) .unimplemented(), new SelfStatusMove(Moves.SNATCH, Type.DARK, -1, 10, -1, 4, 3) @@ -6770,6 +6952,7 @@ export function initMoves() { .partial(), new AttackMove(Moves.DIVE, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 3) .attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, i18next.t("moveTriggers:hidUnderwater", {pokemonName: "{USER}"}), BattlerTagType.UNDERWATER) + .attr(GulpMissileTagAttr) .ignoresVirtual(), new AttackMove(Moves.ARM_THRUST, Type.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, 0, 3) .attr(MultiHitAttr), @@ -6822,7 +7005,7 @@ export function initMoves() { .attr(FlinchAttr), new AttackMove(Moves.WEATHER_BALL, Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 3) .attr(WeatherBallTypeAttr) - .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 2 : 1) + .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType!) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 2 : 1) // TODO: is this bang correct? .ballBombMove(), new StatusMove(Moves.AROMATHERAPY, Type.GRASS, -1, 5, -1, 0, 3) .attr(PartyStatusCureAttr, i18next.t("moveTriggers:soothingAromaWaftedThroughArea"), Abilities.SAP_SIPPER) @@ -6838,7 +7021,7 @@ export function initMoves() { .attr(StatChangeAttr, BattleStat.SPATK, -2, true) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE), new StatusMove(Moves.ODOR_SLEUTH, Type.NORMAL, -1, 40, -1, 0, 3) - .unimplemented(), + .attr(ExposedMoveAttr, BattlerTagType.IGNORE_GHOST), new AttackMove(Moves.ROCK_TOMB, Type.ROCK, MoveCategory.PHYSICAL, 60, 95, 15, 100, 0, 3) .attr(StatChangeAttr, BattleStat.SPD, -1) .makesContact(false), @@ -6936,6 +7119,7 @@ export function initMoves() { .attr(ConfuseAttr) .pulseMove(), new AttackMove(Moves.DOOM_DESIRE, Type.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 3) + .partial() .attr(DelayedAttackAttr, ArenaTagType.DOOM_DESIRE, ChargeAnim.DOOM_DESIRE_CHARGING, i18next.t("moveTriggers:choseDoomDesireAsDestiny", {pokemonName: "{USER}"})), new AttackMove(Moves.PSYCHO_BOOST, Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3) .attr(StatChangeAttr, BattleStat.SPATK, -2, true), @@ -6947,7 +7131,7 @@ export function initMoves() { .attr(AddArenaTagAttr, ArenaTagType.GRAVITY, 5) .target(MoveTarget.BOTH_SIDES), new StatusMove(Moves.MIRACLE_EYE, Type.PSYCHIC, -1, 40, -1, 0, 4) - .unimplemented(), + .attr(ExposedMoveAttr, BattlerTagType.IGNORE_DARK), new AttackMove(Moves.WAKE_UP_SLAP, Type.FIGHTING, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 4) .attr(MovePowerMultiplierAttr, (user, target, move) => targetSleptOrComatoseCondition(user, target, move) ? 2 : 1) .attr(HealStatusEffectAttr, false, StatusEffect.SLEEP), @@ -6989,7 +7173,7 @@ export function initMoves() { new AttackMove(Moves.CLOSE_COMBAT, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4) .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), new AttackMove(Moves.PAYBACK, Type.DARK, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 4) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.BALL ? 2 : 1), + .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.BALL ? 2 : 1), new AttackMove(Moves.ASSURANCE, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 4) .attr(MovePowerMultiplierAttr, (user, target, move) => target.turnData.damageTaken > 0 ? 2 : 1), new StatusMove(Moves.EMBARGO, Type.DARK, 100, 15, -1, 0, 4) @@ -7004,7 +7188,7 @@ export function initMoves() { if (user.status?.effect && isNonVolatileStatusEffect(user.status.effect)) { statusToApply = user.status.effect; } - return statusToApply && target.canSetStatus(statusToApply, false, false, user); + return !!statusToApply && target.canSetStatus(statusToApply, false, false, user); } ), new AttackMove(Moves.TRUMP_CARD, Type.NORMAL, MoveCategory.SPECIAL, -1, -1, 5, -1, 0, 4) @@ -7021,9 +7205,8 @@ export function initMoves() { new StatusMove(Moves.GASTRO_ACID, Type.POISON, 100, 10, -1, 0, 4) .attr(SuppressAbilitiesAttr), new StatusMove(Moves.LUCKY_CHANT, Type.NORMAL, -1, 30, -1, 0, 4) - .attr(AddBattlerTagAttr, BattlerTagType.NO_CRIT, false, false, 5) - .target(MoveTarget.USER_SIDE) - .unimplemented(), + .attr(AddArenaTagAttr, ArenaTagType.NO_CRIT, 5, true, true) + .target(MoveTarget.USER_SIDE), new StatusMove(Moves.ME_FIRST, Type.NORMAL, -1, 20, -1, 0, 4) .ignoresVirtual() .target(MoveTarget.NEAR_ENEMY) @@ -7043,7 +7226,7 @@ export function initMoves() { new StatusMove(Moves.WORRY_SEED, Type.GRASS, 100, 10, -1, 0, 4) .attr(AbilityChangeAttr, Abilities.INSOMNIA), new AttackMove(Moves.SUCKER_PUNCH, Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, -1, 1, 4) - .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct? new StatusMove(Moves.TOXIC_SPIKES, Type.POISON, -1, 20, -1, 0, 4) .attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES) .target(MoveTarget.ENEMY_SIDE), @@ -7410,7 +7593,8 @@ export function initMoves() { new AttackMove(Moves.FROST_BREATH, Type.ICE, MoveCategory.SPECIAL, 60, 90, 10, 100, 0, 5) .attr(CritOnlyAttr), new AttackMove(Moves.DRAGON_TAIL, Type.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) - .attr(ForceSwitchOutAttr), + .attr(ForceSwitchOutAttr) + .hidesTarget(), new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 1, true), new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5) @@ -7886,7 +8070,7 @@ export function initMoves() { new AttackMove(Moves.SMART_STRIKE, Type.STEEL, MoveCategory.PHYSICAL, 70, -1, 10, -1, 0, 7), new StatusMove(Moves.PURIFY, Type.POISON, -1, 20, -1, 0, 7) .condition( - (user: Pokemon, target: Pokemon, move: Move) => isNonVolatileStatusEffect(target.status?.effect)) + (user: Pokemon, target: Pokemon, move: Move) => isNonVolatileStatusEffect(target.status?.effect!)) // TODO: is this bang correct? .attr(HealAttr, 0.5) .attr(HealStatusEffectAttr, false, ...getNonVolatileStatusEffects()) .triageMove(), @@ -7906,7 +8090,7 @@ export function initMoves() { .makesContact(false) .partial(), new AttackMove(Moves.CLANGING_SCALES, Type.DRAGON, MoveCategory.SPECIAL, 110, 100, 5, -1, 0, 7) - .attr(StatChangeAttr, BattleStat.DEF, -1, true) + .attr(StatChangeAttr, BattleStat.DEF, -1, true, null, true, false, MoveEffectTrigger.HIT, true) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.DRAGON_HAMMER, Type.DRAGON, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 7), @@ -8397,17 +8581,17 @@ export function initMoves() { .attr(AddArenaTrapTagHitAttr, ArenaTagType.SPIKES) .slicingMove(), new AttackMove(Moves.BLEAKWIND_STORM, Type.FLYING, MoveCategory.SPECIAL, 100, 80, 10, 30, 0, 8) - .attr(ThunderAccuracyAttr) + .attr(StormAccuracyAttr) .attr(StatChangeAttr, BattleStat.SPD, -1) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.WILDBOLT_STORM, Type.ELECTRIC, MoveCategory.SPECIAL, 100, 80, 10, 20, 0, 8) - .attr(ThunderAccuracyAttr) + .attr(StormAccuracyAttr) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.SANDSEAR_STORM, Type.GROUND, MoveCategory.SPECIAL, 100, 80, 10, 20, 0, 8) - .attr(ThunderAccuracyAttr) + .attr(StormAccuracyAttr) .attr(StatusEffectAttr, StatusEffect.BURN) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), @@ -8608,7 +8792,7 @@ export function initMoves() { .slicingMove(), new AttackMove(Moves.HYDRO_STEAM, Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9) .attr(IgnoreWeatherTypeDebuffAttr, WeatherType.SUNNY) - .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 1.5 : 1), + .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType!) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 1.5 : 1), // TODO: is this bang correct? new AttackMove(Moves.RUINATION, Type.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9) .attr(TargetHalfHpDamageAttr), new AttackMove(Moves.COLLISION_COURSE, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) @@ -8716,7 +8900,7 @@ export function initMoves() { new SelfStatusMove(Moves.BURNING_BULWARK, Type.FIRE, -1, 10, -1, 4, 9) .attr(ProtectAttr, BattlerTagType.BURNING_BULWARK), new AttackMove(Moves.THUNDERCLAP, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9) - .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct? new AttackMove(Moves.MIGHTY_CLEAVE, Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9) .slicingMove() .ignoresProtect(), @@ -8743,7 +8927,7 @@ export function initMoves() { .partial(), new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9) .attr(FlinchAttr) - .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].priority > 0 ) + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].priority > 0 ) // TODO: is this bang correct? //TODO: Should also apply when target move priority increased by ability ex. gale wings .partial(), new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) diff --git a/src/data/nature.ts b/src/data/nature.ts index 1ae3b76a6b6..72e5bb7863c 100644 --- a/src/data/nature.ts +++ b/src/data/nature.ts @@ -15,8 +15,8 @@ export function getNatureName(nature: Nature, includeStatEffects: boolean = fals } if (includeStatEffects) { const stats = Utils.getEnumValues(Stat).slice(1); - let increasedStat: Stat = null; - let decreasedStat: Stat = null; + let increasedStat: Stat | null = null; + let decreasedStat: Stat | null = null; for (const stat of stats) { const multiplier = getNatureStatMultiplier(nature, stat); if (multiplier > 1) { diff --git a/src/data/pokemon-evolutions.ts b/src/data/pokemon-evolutions.ts index 236d174492f..e29a4c16c29 100644 --- a/src/data/pokemon-evolutions.ts +++ b/src/data/pokemon-evolutions.ts @@ -59,26 +59,26 @@ export type EvolutionConditionEnforceFunc = (p: Pokemon) => void; export class SpeciesFormEvolution { public speciesId: Species; - public preFormKey: string; - public evoFormKey: string; + public preFormKey: string | null; + public evoFormKey: string | null; public level: integer; - public item: EvolutionItem; - public condition: SpeciesEvolutionCondition; + public item: EvolutionItem | null; + public condition: SpeciesEvolutionCondition | null; public wildDelay: SpeciesWildEvolutionDelay; - constructor(speciesId: Species, preFormKey: string, evoFormKey: string, level: integer, item: EvolutionItem, condition: SpeciesEvolutionCondition, wildDelay?: SpeciesWildEvolutionDelay) { + constructor(speciesId: Species, preFormKey: string | null, evoFormKey: string | null, level: integer, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay) { this.speciesId = speciesId; this.preFormKey = preFormKey; this.evoFormKey = evoFormKey; this.level = level; this.item = item || EvolutionItem.NONE; this.condition = condition; - this.wildDelay = wildDelay || SpeciesWildEvolutionDelay.NONE; + this.wildDelay = wildDelay ?? SpeciesWildEvolutionDelay.NONE; } } export class SpeciesEvolution extends SpeciesFormEvolution { - constructor(speciesId: Species, level: integer, item: EvolutionItem, condition: SpeciesEvolutionCondition, wildDelay?: SpeciesWildEvolutionDelay) { + constructor(speciesId: Species, level: integer, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay) { super(speciesId, null, null, level, item, condition, wildDelay); } } @@ -95,7 +95,7 @@ export class FusionSpeciesFormEvolution extends SpeciesFormEvolution { export class SpeciesEvolutionCondition { public predicate: EvolutionConditionPredicate; - public enforceFunc: EvolutionConditionEnforceFunc; + public enforceFunc: EvolutionConditionEnforceFunc | undefined; constructor(predicate: EvolutionConditionPredicate, enforceFunc?: EvolutionConditionEnforceFunc) { this.predicate = predicate; @@ -400,8 +400,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.LINOONE, 20, null, null) ], [Species.WURMPLE]: [ - new SpeciesEvolution(Species.SILCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), null), - new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), null) + new SpeciesEvolution(Species.SILCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), + new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) ], [Species.SILCOON]: [ new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null) @@ -945,7 +945,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.SHIINOTIC, 24, null, null) ], [Species.SALANDIT]: [ - new SpeciesEvolution(Species.SALAZZLE, 33, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE), null) + new SpeciesEvolution(Species.SALAZZLE, 33, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)) ], [Species.STUFFUL]: [ new SpeciesEvolution(Species.BEWEAR, 27, null, null) @@ -969,8 +969,8 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.COSMOEM, 43, null, null) ], [Species.COSMOEM]: [ - new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), null), - new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), null) + new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), + new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) ], [Species.MELTAN]: [ new SpeciesEvolution(Species.MELMETAL, 48, null, null) @@ -1264,17 +1264,17 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.TANGELA]: [ - new SpeciesEvolution(Species.TANGROWTH, 34, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.TANGROWTH, 34, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.LICKITUNG]: [ - new SpeciesEvolution(Species.LICKILICKY, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.ROLLOUT).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.LICKILICKY, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ROLLOUT).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.STARYU]: [ new SpeciesEvolution(Species.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.EEVEE]: [ - new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), @@ -1294,13 +1294,13 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.AIPOM]: [ - new SpeciesEvolution(Species.AMBIPOM, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.DOUBLE_HIT).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.AMBIPOM, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DOUBLE_HIT).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.SUNKERN]: [ new SpeciesEvolution(Species.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.YANMA]: [ - new SpeciesEvolution(Species.YANMEGA, 33, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.YANMEGA, 33, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.MURKROW]: [ new SpeciesEvolution(Species.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) @@ -1309,17 +1309,17 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.GIRAFARIG]: [ - new SpeciesEvolution(Species.FARIGIRAF, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.TWIN_BEAM).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.FARIGIRAF, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TWIN_BEAM).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.DUNSPARCE]: [ new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => { let ret = false; - if (p.moveset.filter(m => m.moveId === Moves.HYPER_DRILL).length > 0) { + if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) { p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); } return ret; }), SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.GLIGAR]: [ new SpeciesEvolution(Species.GLISCOR, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.LONG) @@ -1331,10 +1331,10 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna ], [Species.PILOSWINE]: [ - new SpeciesEvolution(Species.MAMOSWINE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.MAMOSWINE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.STANTLER]: [ - new SpeciesEvolution(Species.WYRDEER, 25, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.PSYSHIELD_BASH).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.WYRDEER, 25, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.PSYSHIELD_BASH).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.LOMBRE]: [ new SpeciesEvolution(Species.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1352,11 +1352,11 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.BONSLY]: [ - new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.MIME_JR]: [ - new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.biomeType === Biome.ICE_CAVE || p.scene.arena.biomeType === Biome.SNOWY_FOREST)), SpeciesWildEvolutionDelay.MEDIUM), - new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.biomeType === Biome.ICE_CAVE || p.scene.arena.biomeType === Biome.SNOWY_FOREST)), SpeciesWildEvolutionDelay.MEDIUM), + new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.PANSAGE]: [ new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1406,15 +1406,15 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.ROCKRUFF]: [ - new SpeciesFormEvolution(Species.LYCANROC, "", "midday", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0)), null), - new SpeciesFormEvolution(Species.LYCANROC, "", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1), null), - new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0)), null) + new SpeciesFormEvolution(Species.LYCANROC, "", "midday", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0))), + new SpeciesFormEvolution(Species.LYCANROC, "", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1)), + new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0))) ], [Species.STEENEE]: [ - new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.POIPOLE]: [ - new SpeciesEvolution(Species.NAGANADEL, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.DRAGON_PULSE).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.NAGANADEL, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DRAGON_PULSE).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.ALOLA_SANDSHREW]: [ new SpeciesEvolution(Species.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1428,7 +1428,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.CLOBBOPUS]: [ - new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.TAUNT).length > 0), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TAUNT).length > 0), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.SINISTEA]: [ new SpeciesFormEvolution(Species.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, SpeciesWildEvolutionDelay.LONG), @@ -1462,7 +1462,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.HISUI_QWILFISH]: [ - new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.HISUI_SNEASEL]: [ new SpeciesEvolution(Species.SNEASLER, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.LONG) @@ -1485,7 +1485,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesFormEvolution(Species.SINISTCHA, "artisan", "masterpiece", 1, EvolutionItem.MASTERPIECE_TEACUP, null, SpeciesWildEvolutionDelay.LONG) ], [Species.DIPPLIN]: [ - new SpeciesEvolution(Species.HYDRAPPLE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.DRAGON_CHEER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.HYDRAPPLE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DRAGON_CHEER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.KADABRA]: [ new SpeciesEvolution(Species.ALAKAZAM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) @@ -1501,7 +1501,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [Species.ONIX]: [ new SpeciesEvolution(Species.STEELIX, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( - p => p.moveset.filter(m => m.getMove().type === Type.STEEL).length > 0), + p => p.moveset.filter(m => m?.getMove().type === Type.STEEL).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.RHYDON]: [ @@ -1512,7 +1512,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [Species.SCYTHER]: [ new SpeciesEvolution(Species.SCIZOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( - p => p.moveset.filter(m => m.getMove().type === Type.STEEL).length > 0), + p => p.moveset.filter(m => m?.getMove().type === Type.STEEL).length > 0), SpeciesWildEvolutionDelay.VERY_LONG), new SpeciesEvolution(Species.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], @@ -1566,7 +1566,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.ALOLA_GOLEM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.PRIMEAPE]: [ - new SpeciesEvolution(Species.ANNIHILAPE, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.RAGE_FIST).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.ANNIHILAPE, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.RAGE_FIST).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.GOLBAT]: [ new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(110), SpeciesWildEvolutionDelay.VERY_LONG) diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 93781063061..a55b9186839 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -181,7 +181,7 @@ export class SpeciesFormChange { return true; } - findTrigger(triggerType: Constructor): SpeciesFormChangeTrigger { + findTrigger(triggerType: Constructor): SpeciesFormChangeTrigger | null { if (!this.trigger.hasTriggerType(triggerType)) { return null; } @@ -189,7 +189,7 @@ export class SpeciesFormChange { const trigger = this.trigger; if (trigger instanceof SpeciesFormChangeCompoundTrigger) { - return trigger.triggers.find(t => t.hasTriggerType(triggerType)); + return trigger.triggers.find(t => t.hasTriggerType(triggerType))!; // TODO: is this bang correct? } return trigger; @@ -198,11 +198,11 @@ export class SpeciesFormChange { export class SpeciesFormChangeCondition { public predicate: SpeciesFormChangeConditionPredicate; - public enforceFunc: SpeciesFormChangeConditionEnforceFunc; + public enforceFunc: SpeciesFormChangeConditionEnforceFunc | null; constructor(predicate: SpeciesFormChangeConditionPredicate, enforceFunc?: SpeciesFormChangeConditionEnforceFunc) { this.predicate = predicate; - this.enforceFunc = enforceFunc; + this.enforceFunc = enforceFunc!; // TODO: is this bang correct? } } @@ -314,7 +314,7 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge } canChange(pokemon: Pokemon): boolean { - return (!!pokemon.moveset.filter(m => m.moveId === this.move).length) === this.known; + return (!!pokemon.moveset.filter(m => m?.moveId === this.move).length) === this.known; } } @@ -332,7 +332,7 @@ export abstract class SpeciesFormChangeMoveTrigger extends SpeciesFormChangeTrig export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger { canChange(pokemon: Pokemon): boolean { const command = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; - return command?.move && this.movePredicate(command.move.move) === this.used; + return !!command?.move && this.movePredicate(command.move.move) === this.used; } } @@ -828,6 +828,12 @@ export const pokemonFormChanges: PokemonFormChanges = { [Species.EISCUE]: [ new SpeciesFormChange(Species.EISCUE, "", "no-ice", new SpeciesFormChangeManualTrigger(), true), new SpeciesFormChange(Species.EISCUE, "no-ice", "", new SpeciesFormChangeManualTrigger(), true), + ], + [Species.CRAMORANT]: [ + new SpeciesFormChange(Species.CRAMORANT, "", "gulping", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() >= .5)), + new SpeciesFormChange(Species.CRAMORANT, "", "gorging", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() < .5)), + new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeManualTrigger, true), + new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeManualTrigger, true), ] }; diff --git a/src/data/pokemon-level-moves.ts b/src/data/pokemon-level-moves.ts index b23c43ef215..ccf6ac022ae 100644 --- a/src/data/pokemon-level-moves.ts +++ b/src/data/pokemon-level-moves.ts @@ -18666,7 +18666,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 48, Moves.PIKA_PAPOW ], ], 3: [ - [ EVOLVE_MOVE, Moves.METEOR_MASH ], + [ 1, Moves.METEOR_MASH ], [ 1, Moves.TAIL_WHIP ], [ 1, Moves.GROWL ], [ 1, Moves.THUNDER_SHOCK ], @@ -18690,7 +18690,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 48, Moves.PIKA_PAPOW ], ], 4: [ - [ EVOLVE_MOVE, Moves.ICICLE_CRASH ], + [ 1, Moves.ICICLE_CRASH ], [ 1, Moves.TAIL_WHIP ], [ 1, Moves.GROWL ], [ 1, Moves.THUNDER_SHOCK ], @@ -18714,7 +18714,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 48, Moves.PIKA_PAPOW ], ], 5: [ - [ EVOLVE_MOVE, Moves.DRAINING_KISS ], + [ 1, Moves.DRAINING_KISS ], [ 1, Moves.TAIL_WHIP ], [ 1, Moves.GROWL ], [ 1, Moves.THUNDER_SHOCK ], @@ -18738,7 +18738,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 48, Moves.PIKA_PAPOW ], ], 6: [ - [ EVOLVE_MOVE, Moves.ELECTRIC_TERRAIN ], + [ 1, Moves.ELECTRIC_TERRAIN ], [ 1, Moves.TAIL_WHIP ], [ 1, Moves.GROWL ], [ 1, Moves.THUNDER_SHOCK ], @@ -18762,7 +18762,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 48, Moves.PIKA_PAPOW ], ], 7: [ - [ EVOLVE_MOVE, Moves.FLYING_PRESS ], + [ 1, Moves.FLYING_PRESS ], [ 1, Moves.TAIL_WHIP ], [ 1, Moves.GROWL ], [ 1, Moves.THUNDER_SHOCK ], @@ -18886,7 +18886,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { }, [Species.ROTOM]: { 1: [ - [ EVOLVE_MOVE, Moves.OVERHEAT ], + [ 1, Moves.OVERHEAT ], [ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.ASTONISH ], [ 5, Moves.THUNDER_SHOCK ], @@ -18902,7 +18902,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 55, Moves.UPROAR ], ], 2: [ - [ EVOLVE_MOVE, Moves.HYDRO_PUMP ], + [ 1, Moves.HYDRO_PUMP ], [ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.ASTONISH ], [ 5, Moves.THUNDER_SHOCK ], @@ -18918,7 +18918,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 55, Moves.UPROAR ], ], 3: [ - [ EVOLVE_MOVE, Moves.BLIZZARD ], + [ 1, Moves.BLIZZARD ], [ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.ASTONISH ], [ 5, Moves.THUNDER_SHOCK ], @@ -18934,7 +18934,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 55, Moves.UPROAR ], ], 4: [ - [ EVOLVE_MOVE, Moves.AIR_SLASH ], + [ 1, Moves.AIR_SLASH ], [ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.ASTONISH ], [ 5, Moves.THUNDER_SHOCK ], @@ -18950,7 +18950,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = { [ 55, Moves.UPROAR ], ], 5: [ - [ EVOLVE_MOVE, Moves.LEAF_STORM ], + [ 1, Moves.LEAF_STORM ], [ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.ASTONISH ], [ 5, Moves.THUNDER_SHOCK ], diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index ac79b6223c0..837cad4a0df 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -28,21 +28,21 @@ export enum Region { PALDEA } -export function getPokemonSpecies(species: Species): PokemonSpecies { +export function getPokemonSpecies(species: Species | Species[]): PokemonSpecies { // If a special pool (named trainers) is used here it CAN happen that they have a array as species (which means choose one of those two). So we catch that with this code block if (Array.isArray(species)) { // Pick a random species from the list species = species[Math.floor(Math.random() * species.length)]; } if (species >= 2000) { - return allSpecies.find(s => s.speciesId === species); + return allSpecies.find(s => s.speciesId === species)!; // TODO: is this bang correct? } return allSpecies[species - 1]; } export function getPokemonSpeciesForm(species: Species, formIndex: integer): PokemonSpeciesForm { const retSpecies: PokemonSpecies = species >= 2000 - ? allSpecies.find(s => s.speciesId === species) + ? allSpecies.find(s => s.speciesId === species)! // TODO: is the bang correct? : allSpecies[species - 1]; if (formIndex < retSpecies.forms?.length) { return retSpecies.forms[formIndex]; @@ -97,7 +97,7 @@ export function getFusedSpeciesName(speciesAName: string, speciesBName: string): fragB = fragB.slice(1); } else { const newCharMatch = new RegExp(`[^${lastCharA}]`).exec(fragB); - if (newCharMatch?.index > 0) { + if (newCharMatch?.index !== undefined && newCharMatch.index > 0) { fragB = fragB.slice(newCharMatch.index); } } @@ -125,7 +125,7 @@ export abstract class PokemonSpeciesForm { public formIndex: integer; public generation: integer; public type1: Type; - public type2: Type; + public type2: Type | null; public height: number; public weight: number; public ability1: Abilities; @@ -139,7 +139,7 @@ export abstract class PokemonSpeciesForm { public genderDiffs: boolean; public isStarterSelectable: boolean; - constructor(type1: Type, type2: Type, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, + constructor(type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer, catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs: boolean, isStarterSelectable: boolean) { this.type1 = type1; @@ -267,7 +267,7 @@ export abstract class PokemonSpeciesForm { return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } - getSpriteId(female: boolean, formIndex?: integer, shiny?: boolean, variant?: integer, back?: boolean): string { + getSpriteId(female: boolean, formIndex?: integer, shiny?: boolean, variant: integer = 0, back?: boolean): string { if (formIndex === undefined || this instanceof PokemonForm) { formIndex = this.formIndex; } @@ -281,7 +281,7 @@ export abstract class PokemonSpeciesForm { `${back ? "back__" : ""}${baseSpriteKey}`.split("__").map(p => config ? config = config[p] : null); const variantSet = config as VariantSet; - return `${back ? "back__" : ""}${shiny && (!variantSet || (!variant && !variantSet[variant || 0])) ? "shiny__" : ""}${baseSpriteKey}${shiny && variantSet && variantSet[variant || 0] === 2 ? `_${variant + 1}` : ""}`; + return `${back ? "back__" : ""}${shiny && (!variantSet || (!variant && !variantSet[variant || 0])) ? "shiny__" : ""}${baseSpriteKey}${shiny && variantSet && variantSet[variant] === 2 ? `_${variant + 1}` : ""}`; } getSpriteKey(female: boolean, formIndex?: integer, shiny?: boolean, variant?: integer): string { @@ -297,10 +297,10 @@ export abstract class PokemonSpeciesForm { * @returns species id if no additional forms, index with formkey if a pokemon with a form */ getVariantDataIndex(formIndex?: integer) { - let formkey = null; - let variantDataIndex: integer|string = this.speciesId; + let formkey: string | null = null; + let variantDataIndex: integer | string = this.speciesId; const species = getPokemonSpecies(this.speciesId); - if (species.forms.length > 0) { + if (species.forms.length > 0 && formIndex !== undefined) { formkey = species.forms[formIndex]?.formSpriteKey; if (formkey) { variantDataIndex = `${this.speciesId}-${formkey}`; @@ -311,7 +311,7 @@ export abstract class PokemonSpeciesForm { getIconAtlasKey(formIndex?: integer, shiny?: boolean, variant?: integer): string { const variantDataIndex = this.getVariantDataIndex(formIndex); - const isVariant = shiny && variantData[variantDataIndex] && variantData[variantDataIndex][variant]; + const isVariant = shiny && variantData[variantDataIndex] && (variant !== undefined && variantData[variantDataIndex][variant]); return `pokemon_icons_${this.generation}${isVariant ? "v" : ""}`; } @@ -324,7 +324,7 @@ export abstract class PokemonSpeciesForm { let ret = this.speciesId.toString(); - const isVariant = shiny && variantData[variantDataIndex] && variantData[variantDataIndex][variant]; + const isVariant = shiny && variantData[variantDataIndex] && (variant !== undefined && variantData[variantDataIndex][variant]); if (shiny && !isVariant) { ret += "s"; @@ -382,7 +382,7 @@ export abstract class PokemonSpeciesForm { let ret = speciesId.toString(); const forms = getPokemonSpecies(speciesId).forms; if (forms.length) { - if (formIndex >= forms.length) { + if (formIndex !== undefined && formIndex >= forms.length) { console.warn(`Attempted accessing form with index ${formIndex} of species ${getPokemonSpecies(speciesId).getName()} with only ${forms.length || 0} forms`); formIndex = Math.min(formIndex, forms.length - 1); } @@ -478,7 +478,7 @@ export abstract class PokemonSpeciesForm { let config = variantData; spritePath.split("/").map(p => config ? config = config[p] : null); const variantSet = config as VariantSet; - if (variantSet && variantSet[variant] === 1) { + if (variantSet && (variant !== undefined && variantSet[variant] === 1)) { const populateVariantColors = (key: string): Promise => { return new Promise(resolve => { if (variantColorCache.hasOwnProperty(key)) { @@ -507,7 +507,7 @@ export abstract class PokemonSpeciesForm { cry(scene: BattleScene, soundConfig?: Phaser.Types.Sound.SoundConfig, ignorePlay?: boolean): AnySound { const cryKey = this.getCryKey(this.formIndex); - let cry = scene.sound.get(cryKey) as AnySound; + let cry: AnySound | null = scene.sound.get(cryKey) as AnySound; if (cry?.pendingRemove) { cry = null; } @@ -532,30 +532,32 @@ export abstract class PokemonSpeciesForm { const frame = sourceFrame; canvas.width = frame.width; canvas.height = frame.height; - context.drawImage(sourceImage, frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); - const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); - const pixelData = imageData.data; + context?.drawImage(sourceImage, frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); + const imageData = context?.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); + const pixelData = imageData?.data; + const pixelColors: number[] = []; - for (let i = 0; i < pixelData.length; i += 4) { - if (pixelData[i + 3]) { - const pixel = pixelData.slice(i, i + 4); - const [ r, g, b, a ] = pixel; - if (!spriteColors.find(c => c[0] === r && c[1] === g && c[2] === b)) { - spriteColors.push([ r, g, b, a ]); + if (pixelData?.length !== undefined) { + for (let i = 0; i < pixelData.length; i += 4) { + if (pixelData[i + 3]) { + const pixel = pixelData.slice(i, i + 4); + const [ r, g, b, a ] = pixel; + if (!spriteColors.find(c => c[0] === r && c[1] === g && c[2] === b)) { + spriteColors.push([ r, g, b, a ]); + } } } - } - const pixelColors = []; - for (let i = 0; i < pixelData.length; i += 4) { - const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0); - if (!total) { - continue; + for (let i = 0; i < pixelData.length; i += 4) { + const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0); + if (!total) { + continue; + } + pixelColors.push(argbFromRgba({ r: pixelData[i], g: pixelData[i + 1], b: pixelData[i + 2], a: pixelData[i + 3] })); } - pixelColors.push(argbFromRgba({ r: pixelData[i], g: pixelData[i + 1], b: pixelData[i + 2], a: pixelData[i + 3] })); } - let paletteColors: Map; + let paletteColors: Map = new Map(); const originalRandom = Math.random; Math.random = () => Phaser.Math.RND.realInRange(0, 1); @@ -577,15 +579,15 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali public mythical: boolean; public species: string; public growthRate: GrowthRate; - public malePercent: number; + public malePercent: number | null; public genderDiffs: boolean; public canChangeForm: boolean; public forms: PokemonForm[]; constructor(id: Species, generation: integer, subLegendary: boolean, legendary: boolean, mythical: boolean, species: string, - type1: Type, type2: Type, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, + type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer, - catchRate: integer, baseFriendship: integer, baseExp: integer, growthRate: GrowthRate, malePercent: number, + catchRate: integer, baseFriendship: integer, baseExp: integer, growthRate: GrowthRate, malePercent: number | null, genderDiffs: boolean, canChangeForm?: boolean, ...forms: PokemonForm[]) { super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd, catchRate, baseFriendship, baseExp, genderDiffs, false); @@ -614,7 +616,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali getName(formIndex?: integer): string { if (formIndex !== undefined && this.forms.length) { const form = this.forms[formIndex]; - let key: string; + let key: string | null; switch (form.formKey) { case SpeciesFormKey.MEGA: case SpeciesFormKey.PRIMAL: @@ -626,6 +628,8 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali default: if (form.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1) { key = "gigantamax"; + } else { + key = null; } } @@ -713,11 +717,11 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali evolutionChance = Math.min(minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance), 1); } } else { - const preferredMinLevel = Math.max((ev.level - 1) + ev.wildDelay * this.getStrengthLevelDiff(strength), 1); + const preferredMinLevel = Math.max((ev.level - 1) + (ev.wildDelay!) * this.getStrengthLevelDiff(strength), 1); // TODO: is the bang correct? let evolutionLevel = Math.max(ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2), 1); if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) { - const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find(ev => ev.speciesId === this.speciesId).level; + const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find(ev => ev.speciesId === this.speciesId)!.level; // TODO: is the bang correct? if (prevolutionLevel > 1) { evolutionLevel = prevolutionLevel; } @@ -750,15 +754,15 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali for (const weight of evolutionPool.keys()) { if (randValue < weight) { - return getPokemonSpecies(evolutionPool.get(weight)).getSpeciesForLevel(level, true, forTrainer, strength); + return getPokemonSpecies(evolutionPool.get(weight)!).getSpeciesForLevel(level, true, forTrainer, strength); // TODO: is the bang correct? } } return this.speciesId; } - getEvolutionLevels() { - const evolutionLevels = []; + getEvolutionLevels(): [Species, integer][] { + const evolutionLevels: [Species, integer][] = []; //console.log(Species[this.speciesId], pokemonEvolutions[this.speciesId]) @@ -778,8 +782,8 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali return evolutionLevels; } - getPrevolutionLevels() { - const prevolutionLevels = []; + getPrevolutionLevels(): [Species, integer][] { + const prevolutionLevels: [Species, integer][] = []; const allEvolvingPokemon = Object.keys(pokemonEvolutions); for (const p of allEvolvingPokemon) { @@ -801,18 +805,18 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali // This could definitely be written better and more accurate to the getSpeciesForLevel logic, but it is only for generating movesets for evolved Pokemon getSimulatedEvolutionChain(currentLevel: integer, forTrainer: boolean = false, isBoss: boolean = false, player: boolean = false): [Species, integer][] { - const ret = []; + const ret: [Species, integer][] = []; if (pokemonPrevolutions.hasOwnProperty(this.speciesId)) { const prevolutionLevels = this.getPrevolutionLevels().reverse(); const levelDiff = player ? 0 : forTrainer || isBoss ? forTrainer && isBoss ? 2.5 : 5 : 10; ret.push([ prevolutionLevels[0][0], 1 ]); for (let l = 1; l < prevolutionLevels.length; l++) { const evolution = pokemonEvolutions[prevolutionLevels[l - 1][0]].find(e => e.speciesId === prevolutionLevels[l][0]); - ret.push([ prevolutionLevels[l][0], Math.min(Math.max(evolution.level + Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution.wildDelay, 0.5) * 5) - 1, 2, evolution.level), currentLevel - 1) ]); + ret.push([ prevolutionLevels[l][0], Math.min(Math.max((evolution?.level!) + Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max((evolution?.wildDelay!), 0.5) * 5) - 1, 2, (evolution?.level!)), currentLevel - 1) ]); // TODO: are those bangs correct? } const lastPrevolutionLevel = ret[prevolutionLevels.length - 1][1]; const evolution = pokemonEvolutions[prevolutionLevels[prevolutionLevels.length - 1][0]].find(e => e.speciesId === this.speciesId); - ret.push([ this.speciesId, Math.min(Math.max(lastPrevolutionLevel + Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution.wildDelay, 0.5) * 5), lastPrevolutionLevel + 1, evolution.level), currentLevel) ]); + ret.push([ this.speciesId, Math.min(Math.max(lastPrevolutionLevel + Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max((evolution?.wildDelay!), 0.5) * 5), lastPrevolutionLevel + 1, (evolution?.level!)), currentLevel) ]); // TODO: are those bangs correct? } else { ret.push([ this.speciesId, 1 ]); } @@ -853,7 +857,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali } getFormSpriteKey(formIndex?: integer) { - if (this.forms.length && formIndex >= this.forms.length) { + if (this.forms.length && (formIndex !== undefined && formIndex >= this.forms.length)) { console.warn(`Attempted accessing form with index ${formIndex} of species ${this.getName()} with only ${this.forms.length || 0} forms`); formIndex = Math.min(formIndex, this.forms.length - 1); } @@ -866,14 +870,14 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali export class PokemonForm extends PokemonSpeciesForm { public formName: string; public formKey: string; - public formSpriteKey: string; + public formSpriteKey: string | null; // This is a collection of form keys that have in-run form changes, but should still be separately selectable from the start screen private starterSelectableKeys: string[] = ["10", "50", "10-pc", "50-pc", "red", "orange", "yellow", "green", "blue", "indigo", "violet"]; - constructor(formName: string, formKey: string, type1: Type, type2: Type, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, + constructor(formName: string, formKey: string, type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer, - catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs?: boolean, formSpriteKey?: string, isStarterSelectable?: boolean, ) { + catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs?: boolean, formSpriteKey?: string | null, isStarterSelectable?: boolean, ) { super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd, catchRate, baseFriendship, baseExp, !!genderDiffs, (!!isStarterSelectable || !formKey)); this.formName = formName; @@ -2725,8 +2729,8 @@ export const speciesStarters = { [Species.VOLTORB]: 2, [Species.EXEGGCUTE]: 3, [Species.CUBONE]: 3, - [Species.HITMONLEE]: 4, - [Species.HITMONCHAN]: 4, + [Species.HITMONLEE]: 5, + [Species.HITMONCHAN]: 5, [Species.LICKITUNG]: 3, [Species.KOFFING]: 2, [Species.RHYHORN]: 3, @@ -2738,7 +2742,7 @@ export const speciesStarters = { [Species.STARYU]: 3, [Species.MR_MIME]: 3, [Species.SCYTHER]: 5, - [Species.JYNX]: 3, + [Species.JYNX]: 4, [Species.ELECTABUZZ]: 4, [Species.MAGMAR]: 4, [Species.PINSIR]: 4, @@ -2765,7 +2769,7 @@ export const speciesStarters = { [Species.SENTRET]: 1, [Species.HOOTHOOT]: 2, [Species.LEDYBA]: 1, - [Species.SPINARAK]: 2, + [Species.SPINARAK]: 1, [Species.CHINCHOU]: 2, [Species.PICHU]: 2, [Species.CLEFFA]: 2, @@ -2805,8 +2809,8 @@ export const speciesStarters = { [Species.PHANPY]: 3, [Species.STANTLER]: 3, [Species.SMEARGLE]: 1, - [Species.TYROGUE]: 2, - [Species.SMOOCHUM]: 2, + [Species.TYROGUE]: 3, + [Species.SMOOCHUM]: 3, [Species.ELEKID]: 3, [Species.MAGBY]: 3, [Species.MILTANK]: 4, @@ -2819,7 +2823,7 @@ export const speciesStarters = { [Species.CELEBI]: 6, [Species.TREECKO]: 3, - [Species.TORCHIC]: 3, + [Species.TORCHIC]: 4, [Species.MUDKIP]: 3, [Species.POOCHYENA]: 2, [Species.ZIGZAGOON]: 2, @@ -2898,7 +2902,7 @@ export const speciesStarters = { [Species.CHIMCHAR]: 3, [Species.PIPLUP]: 3, [Species.STARLY]: 3, - [Species.BIDOOF]: 3, + [Species.BIDOOF]: 2, [Species.KRICKETOT]: 1, [Species.SHINX]: 2, [Species.BUDEW]: 3, @@ -2986,7 +2990,7 @@ export const speciesStarters = { [Species.ZORUA]: 3, [Species.MINCCINO]: 3, [Species.GOTHITA]: 3, - [Species.SOLOSIS]: 4, + [Species.SOLOSIS]: 3, [Species.DUCKLETT]: 2, [Species.VANILLITE]: 3, [Species.DEERLING]: 2, @@ -3205,7 +3209,7 @@ export const speciesStarters = { [Species.LECHONK]: 2, [Species.TAROUNTULA]: 1, [Species.NYMBLE]: 3, - [Species.PAWMI]: 4, + [Species.PAWMI]: 3, [Species.TANDEMAUS]: 4, [Species.FIDOUGH]: 2, [Species.SMOLIV]: 3, @@ -3319,14 +3323,14 @@ export const starterPassiveAbilities = { [Species.SQUIRTLE]: Abilities.STURDY, [Species.CATERPIE]: Abilities.MAGICIAN, [Species.WEEDLE]: Abilities.TINTED_LENS, - [Species.PIDGEY]: Abilities.GALE_WINGS, + [Species.PIDGEY]: Abilities.FLARE_BOOST, [Species.RATTATA]: Abilities.STRONG_JAW, [Species.SPEAROW]: Abilities.MOXIE, [Species.EKANS]: Abilities.REGENERATOR, [Species.SANDSHREW]: Abilities.TOUGH_CLAWS, [Species.NIDORAN_F]: Abilities.FLARE_BOOST, [Species.NIDORAN_M]: Abilities.GUTS, - [Species.VULPIX]: Abilities.SOLAR_POWER, + [Species.VULPIX]: Abilities.FUR_COAT, [Species.ZUBAT]: Abilities.INTIMIDATE, [Species.ODDISH]: Abilities.TRIAGE, [Species.PARAS]: Abilities.TRIAGE, @@ -3345,16 +3349,16 @@ export const starterPassiveAbilities = { [Species.PONYTA]: Abilities.MAGIC_GUARD, [Species.SLOWPOKE]: Abilities.UNAWARE, [Species.MAGNEMITE]: Abilities.LEVITATE, - [Species.FARFETCHD]: Abilities.HUGE_POWER, + [Species.FARFETCHD]: Abilities.SNIPER, [Species.DODUO]: Abilities.PARENTAL_BOND, [Species.SEEL]: Abilities.WATER_BUBBLE, [Species.GRIMER]: Abilities.WATER_ABSORB, [Species.SHELLDER]: Abilities.ICE_SCALES, [Species.GASTLY]: Abilities.SHADOW_SHIELD, [Species.ONIX]: Abilities.ROCKY_PAYLOAD, - [Species.DROWZEE]: Abilities.BAD_DREAMS, + [Species.DROWZEE]: Abilities.MAGICIAN, [Species.KRABBY]: Abilities.UNBURDEN, - [Species.VOLTORB]: Abilities.ELECTRIC_SURGE, + [Species.VOLTORB]: Abilities.TRANSISTOR, [Species.EXEGGCUTE]: Abilities.RIPEN, [Species.CUBONE]: Abilities.PARENTAL_BOND, [Species.LICKITUNG]: Abilities.THICK_FAT, @@ -3374,7 +3378,7 @@ export const starterPassiveAbilities = { [Species.EEVEE]: Abilities.SIMPLE, [Species.PORYGON]: Abilities.PROTEAN, [Species.OMANYTE]: Abilities.STURDY, - [Species.KABUTO]: Abilities.SHARPNESS, + [Species.KABUTO]: Abilities.TOUGH_CLAWS, [Species.AERODACTYL]: Abilities.ORICHALCUM_PULSE, [Species.ARTICUNO]: Abilities.SNOW_WARNING, [Species.ZAPDOS]: Abilities.DRIZZLE, @@ -3476,7 +3480,7 @@ export const starterPassiveAbilities = { [Species.CACNEA]: Abilities.SAND_RUSH, [Species.SWABLU]: Abilities.ADAPTABILITY, [Species.ZANGOOSE]: Abilities.POISON_HEAL, - [Species.SEVIPER]: Abilities.INTIMIDATE, + [Species.SEVIPER]: Abilities.MULTISCALE, [Species.LUNATONE]: Abilities.SHADOW_SHIELD, [Species.SOLROCK]: Abilities.DROUGHT, [Species.BARBOACH]: Abilities.SIMPLE, @@ -3495,16 +3499,16 @@ export const starterPassiveAbilities = { [Species.SNORUNT]: Abilities.SNOW_WARNING, [Species.SPHEAL]: Abilities.UNAWARE, [Species.CLAMPERL]: Abilities.DRIZZLE, - [Species.RELICANTH]: Abilities.SOLID_ROCK, + [Species.RELICANTH]: Abilities.PRIMORDIAL_SEA, [Species.LUVDISC]: Abilities.MULTISCALE, - [Species.BAGON]: Abilities.ADAPTABILITY, + [Species.BAGON]: Abilities.DRAGONS_MAW, [Species.BELDUM]: Abilities.LEVITATE, [Species.REGIROCK]: Abilities.SAND_STREAM, [Species.REGICE]: Abilities.SNOW_WARNING, [Species.REGISTEEL]: Abilities.FILTER, - [Species.LATIAS]: Abilities.SOUL_HEART, + [Species.LATIAS]: Abilities.PRISM_ARMOR, [Species.LATIOS]: Abilities.TINTED_LENS, - [Species.KYOGRE]: Abilities.RAIN_DISH, + [Species.KYOGRE]: Abilities.MOLD_BREAKER, [Species.GROUDON]: Abilities.TURBOBLAZE, [Species.RAYQUAZA]: Abilities.UNNERVE, [Species.JIRACHI]: Abilities.COMATOSE, @@ -3523,7 +3527,7 @@ export const starterPassiveAbilities = { [Species.COMBEE]: Abilities.INTIMIDATE, [Species.PACHIRISU]: Abilities.HONEY_GATHER, [Species.BUIZEL]: Abilities.MOXIE, - [Species.CHERUBI]: Abilities.DROUGHT, + [Species.CHERUBI]: Abilities.ORICHALCUM_PULSE, [Species.SHELLOS]: Abilities.REGENERATOR, [Species.DRIFLOON]: Abilities.MAGIC_GUARD, [Species.BUNEARY]: Abilities.ADAPTABILITY, @@ -3537,13 +3541,13 @@ export const starterPassiveAbilities = { [Species.CHATOT]: Abilities.PUNK_ROCK, [Species.SPIRITOMB]: Abilities.VESSEL_OF_RUIN, [Species.GIBLE]: Abilities.SAND_STREAM, - [Species.MUNCHLAX]: Abilities.RIPEN, + [Species.MUNCHLAX]: Abilities.HARVEST, [Species.RIOLU]: Abilities.MINDS_EYE, [Species.HIPPOPOTAS]: Abilities.UNAWARE, [Species.SKORUPI]: Abilities.SUPER_LUCK, [Species.CROAGUNK]: Abilities.MOXIE, [Species.CARNIVINE]: Abilities.ARENA_TRAP, - [Species.FINNEON]: Abilities.DRIZZLE, + [Species.FINNEON]: Abilities.WATER_BUBBLE, [Species.MANTYKE]: Abilities.UNAWARE, [Species.SNOVER]: Abilities.THICK_FAT, [Species.ROTOM]: Abilities.HADRON_ENGINE, @@ -3557,7 +3561,7 @@ export const starterPassiveAbilities = { [Species.GIRATINA]: Abilities.SHADOW_SHIELD, [Species.CRESSELIA]: Abilities.MAGIC_BOUNCE, [Species.PHIONE]: Abilities.SIMPLE, - [Species.MANAPHY]: Abilities.SIMPLE, + [Species.MANAPHY]: Abilities.PRIMORDIAL_SEA, [Species.DARKRAI]: Abilities.UNNERVE, [Species.SHAYMIN]: Abilities.WIND_RIDER, [Species.ARCEUS]: Abilities.ADAPTABILITY, @@ -3590,13 +3594,13 @@ export const starterPassiveAbilities = { [Species.SANDILE]: Abilities.TOUGH_CLAWS, [Species.DARUMAKA]: Abilities.GORILLA_TACTICS, [Species.MARACTUS]: Abilities.WELL_BAKED_BODY, - [Species.DWEBBLE]: Abilities.ANGER_SHELL, + [Species.DWEBBLE]: Abilities.ROCKY_PAYLOAD, [Species.SCRAGGY]: Abilities.PROTEAN, - [Species.SIGILYPH]: Abilities.MAGICIAN, + [Species.SIGILYPH]: Abilities.FLARE_BOOST, [Species.YAMASK]: Abilities.PURIFYING_SALT, - [Species.TIRTOUGA]: Abilities.ANGER_SHELL, + [Species.TIRTOUGA]: Abilities.WATER_ABSORB, [Species.ARCHEN]: Abilities.MULTISCALE, - [Species.TRUBBISH]: Abilities.TOXIC_DEBRIS, + [Species.TRUBBISH]: Abilities.NEUTRALIZING_GAS, [Species.ZORUA]: Abilities.DARK_AURA, [Species.MINCCINO]: Abilities.FUR_COAT, [Species.GOTHITA]: Abilities.UNNERVE, @@ -3611,7 +3615,7 @@ export const starterPassiveAbilities = { [Species.ALOMOMOLA]: Abilities.MULTISCALE, [Species.JOLTIK]: Abilities.TRANSISTOR, [Species.FERROSEED]: Abilities.ROUGH_SKIN, - [Species.KLINK]: Abilities.STEELWORKER, + [Species.KLINK]: Abilities.STEELY_SPIRIT, [Species.TYNAMO]: Abilities.POISON_HEAL, [Species.ELGYEM]: Abilities.PRISM_ARMOR, [Species.LITWICK]: Abilities.SOUL_HEART, @@ -3625,7 +3629,7 @@ export const starterPassiveAbilities = { [Species.GOLETT]: Abilities.SHADOW_SHIELD, [Species.PAWNIARD]: Abilities.SWORD_OF_RUIN, [Species.BOUFFALANT]: Abilities.ROCK_HEAD, - [Species.RUFFLET]: Abilities.GALE_WINGS, + [Species.RUFFLET]: Abilities.SPEED_BOOST, [Species.VULLABY]: Abilities.THICK_FAT, [Species.HEATMOR]: Abilities.CONTRARY, [Species.DURANT]: Abilities.COMPOUND_EYES, @@ -3651,12 +3655,12 @@ export const starterPassiveAbilities = { [Species.SCATTERBUG]: Abilities.PRANKSTER, [Species.LITLEO]: Abilities.BEAST_BOOST, [Species.FLABEBE]: Abilities.GRASSY_SURGE, - [Species.SKIDDO]: Abilities.GRASSY_SURGE, + [Species.SKIDDO]: Abilities.SEED_SOWER, [Species.PANCHAM]: Abilities.FUR_COAT, [Species.FURFROU]: Abilities.FLUFFY, [Species.ESPURR]: Abilities.FUR_COAT, [Species.HONEDGE]: Abilities.SHARPNESS, - [Species.SPRITZEE]: Abilities.MISTY_SURGE, + [Species.SPRITZEE]: Abilities.FUR_COAT, [Species.SWIRLIX]: Abilities.WELL_BAKED_BODY, [Species.INKAY]: Abilities.UNNERVE, [Species.BINACLE]: Abilities.SAP_SIPPER, @@ -3670,17 +3674,17 @@ export const starterPassiveAbilities = { [Species.CARBINK]: Abilities.SOLID_ROCK, [Species.GOOMY]: Abilities.REGENERATOR, [Species.KLEFKI]: Abilities.LEVITATE, - [Species.PHANTUMP]: Abilities.RIPEN, + [Species.PHANTUMP]: Abilities.SHADOW_TAG, [Species.PUMPKABOO]: Abilities.WELL_BAKED_BODY, [Species.BERGMITE]: Abilities.ICE_SCALES, [Species.NOIBAT]: Abilities.PUNK_ROCK, - [Species.XERNEAS]: Abilities.MISTY_SURGE, + [Species.XERNEAS]: Abilities.HARVEST, [Species.YVELTAL]: Abilities.SOUL_HEART, [Species.ZYGARDE]: Abilities.HUGE_POWER, [Species.DIANCIE]: Abilities.LEVITATE, [Species.HOOPA]: Abilities.OPPORTUNIST, [Species.VOLCANION]: Abilities.FILTER, - [Species.ROWLET]: Abilities.UNBURDEN, + [Species.ROWLET]: Abilities.SNIPER, [Species.LITTEN]: Abilities.FUR_COAT, [Species.POPPLIO]: Abilities.PUNK_ROCK, [Species.PIKIPEK]: Abilities.TECHNICIAN, @@ -3714,7 +3718,7 @@ export const starterPassiveAbilities = { [Species.BRUXISH]: Abilities.MULTISCALE, [Species.DRAMPA]: Abilities.THICK_FAT, [Species.DHELMISE]: Abilities.WATER_BUBBLE, - [Species.JANGMO_O]: Abilities.PUNK_ROCK, + [Species.JANGMO_O]: Abilities.DAUNTLESS_SHIELD, [Species.TAPU_KOKO]: Abilities.TRANSISTOR, [Species.TAPU_LELE]: Abilities.SHEER_FORCE, [Species.TAPU_BULU]: Abilities.TRIAGE, @@ -3726,7 +3730,7 @@ export const starterPassiveAbilities = { [Species.XURKITREE]: Abilities.TRANSISTOR, [Species.CELESTEELA]: Abilities.HEATPROOF, [Species.KARTANA]: Abilities.SHARPNESS, - [Species.GUZZLORD]: Abilities.INNARDS_OUT, + [Species.GUZZLORD]: Abilities.POISON_HEAL, [Species.NECROZMA]: Abilities.BEAST_BOOST, [Species.MAGEARNA]: Abilities.STEELY_SPIRIT, [Species.MARSHADOW]: Abilities.IRON_FIST, @@ -3738,13 +3742,13 @@ export const starterPassiveAbilities = { [Species.GROOKEY]: Abilities.GRASS_PELT, [Species.SCORBUNNY]: Abilities.NO_GUARD, [Species.SOBBLE]: Abilities.SUPER_LUCK, - [Species.SKWOVET]: Abilities.RIPEN, + [Species.SKWOVET]: Abilities.HARVEST, [Species.ROOKIDEE]: Abilities.IRON_BARBS, [Species.BLIPBUG]: Abilities.PSYCHIC_SURGE, [Species.NICKIT]: Abilities.MAGICIAN, [Species.GOSSIFLEUR]: Abilities.GRASSY_SURGE, [Species.WOOLOO]: Abilities.SIMPLE, - [Species.CHEWTLE]: Abilities.ROCK_HEAD, + [Species.CHEWTLE]: Abilities.ROCKY_PAYLOAD, [Species.YAMPER]: Abilities.SHEER_FORCE, [Species.ROLYCOLY]: Abilities.SOLID_ROCK, [Species.APPLIN]: Abilities.DRAGONS_MAW, @@ -3757,7 +3761,7 @@ export const starterPassiveAbilities = { [Species.SINISTEA]: Abilities.SHADOW_SHIELD, [Species.HATENNA]: Abilities.FAIRY_AURA, [Species.IMPIDIMP]: Abilities.FUR_COAT, - [Species.MILCERY]: Abilities.MISTY_SURGE, + [Species.MILCERY]: Abilities.REGENERATOR, [Species.FALINKS]: Abilities.PARENTAL_BOND, [Species.PINCURCHIN]: Abilities.ELECTROMORPHOSIS, [Species.SNOM]: Abilities.SNOW_WARNING, @@ -3776,7 +3780,7 @@ export const starterPassiveAbilities = { [Species.ZAMAZENTA]: Abilities.STAMINA, [Species.ETERNATUS]: Abilities.SUPREME_OVERLORD, [Species.KUBFU]: Abilities.IRON_FIST, - [Species.ZARUDE]: Abilities.GRASSY_SURGE, + [Species.ZARUDE]: Abilities.TOUGH_CLAWS, [Species.REGIELEKI]: Abilities.ELECTRIC_SURGE, [Species.REGIDRAGO]: Abilities.MULTISCALE, [Species.GLASTRIER]: Abilities.FILTER, @@ -3785,7 +3789,7 @@ export const starterPassiveAbilities = { [Species.ENAMORUS]: Abilities.FAIRY_AURA, [Species.SPRIGATITO]: Abilities.MAGICIAN, [Species.FUECOCO]: Abilities.PUNK_ROCK, - [Species.QUAXLY]: Abilities.DEFIANT, + [Species.QUAXLY]: Abilities.OPPORTUNIST, [Species.LECHONK]: Abilities.SIMPLE, [Species.TAROUNTULA]: Abilities.HONEY_GATHER, [Species.NYMBLE]: Abilities.GUTS, @@ -3833,7 +3837,7 @@ export const starterPassiveAbilities = { [Species.IRON_MOTH]: Abilities.LEVITATE, [Species.IRON_THORNS]: Abilities.SAND_STREAM, [Species.FRIGIBAX]: Abilities.SNOW_WARNING, - [Species.GIMMIGHOUL]: Abilities.CONTRARY, + [Species.GIMMIGHOUL]: Abilities.HONEY_GATHER, [Species.WO_CHIEN]: Abilities.VESSEL_OF_RUIN, [Species.CHIEN_PAO]: Abilities.INTREPID_SWORD, [Species.TING_LU]: Abilities.STAMINA, @@ -3864,7 +3868,7 @@ export const starterPassiveAbilities = { [Species.ALOLA_GRIMER]: Abilities.TOXIC_DEBRIS, [Species.ETERNAL_FLOETTE]: Abilities.MAGIC_GUARD, [Species.GALAR_MEOWTH]: Abilities.STEELWORKER, - [Species.GALAR_PONYTA]: Abilities.PIXILATE, + [Species.GALAR_PONYTA]: Abilities.MOXIE, [Species.GALAR_SLOWPOKE]: Abilities.UNAWARE, [Species.GALAR_FARFETCHD]: Abilities.INTREPID_SWORD, [Species.GALAR_ARTICUNO]: Abilities.SERENE_GRACE, @@ -3876,7 +3880,7 @@ export const starterPassiveAbilities = { [Species.GALAR_YAMASK]: Abilities.TABLETS_OF_RUIN, [Species.GALAR_STUNFISK]: Abilities.ARENA_TRAP, [Species.HISUI_GROWLITHE]: Abilities.RECKLESS, - [Species.HISUI_VOLTORB]: Abilities.ELECTRIC_SURGE, + [Species.HISUI_VOLTORB]: Abilities.TRANSISTOR, [Species.HISUI_QWILFISH]: Abilities.MERCILESS, [Species.HISUI_SNEASEL]: Abilities.SCRAPPY, [Species.HISUI_ZORUA]: Abilities.ADAPTABILITY, diff --git a/src/data/status-effect.ts b/src/data/status-effect.ts index 65caf58ccb9..828c52cae13 100644 --- a/src/data/status-effect.ts +++ b/src/data/status-effect.ts @@ -7,12 +7,12 @@ export { StatusEffect }; export class Status { public effect: StatusEffect; public turnCount: integer; - public cureTurn: integer; + public cureTurn: integer | null; constructor(effect: StatusEffect, turnCount: integer = 0, cureTurn?: integer) { this.effect = effect; this.turnCount = turnCount === undefined ? 0 : turnCount; - this.cureTurn = cureTurn; + this.cureTurn = cureTurn!; // TODO: is this bang correct? } incrementTurn(): void { @@ -24,7 +24,7 @@ export class Status { } } -function getStatusEffectMessageKey(statusEffect: StatusEffect): string { +function getStatusEffectMessageKey(statusEffect: StatusEffect | undefined): string { switch (statusEffect) { case StatusEffect.POISON: return "statusEffect:poison"; @@ -43,7 +43,7 @@ function getStatusEffectMessageKey(statusEffect: StatusEffect): string { } } -export function getStatusEffectObtainText(statusEffect: StatusEffect, pokemonNameWithAffix: string, sourceText?: string): string { +export function getStatusEffectObtainText(statusEffect: StatusEffect | undefined, pokemonNameWithAffix: string, sourceText?: string): string { if (!sourceText) { const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain`as ParseKeys; return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix }); diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 2cc228a93cf..5f47ce42a62 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -12,27 +12,27 @@ import {PersistentModifier} from "../modifier/modifier"; import {TrainerVariant} from "../field/trainer"; import {getIsInitialized, initI18n} from "#app/plugins/i18n"; import i18next from "i18next"; -import { Moves } from "#enums/moves"; -import { PartyMemberStrength } from "#enums/party-member-strength"; -import { Species } from "#enums/species"; -import { TrainerType } from "#enums/trainer-type"; +import {Moves} from "#enums/moves"; +import {PartyMemberStrength} from "#enums/party-member-strength"; +import {Species} from "#enums/species"; +import {TrainerType} from "#enums/trainer-type"; export enum TrainerPoolTier { - COMMON, - UNCOMMON, - RARE, - SUPER_RARE, - ULTRA_RARE + COMMON, + UNCOMMON, + RARE, + SUPER_RARE, + ULTRA_RARE } export interface TrainerTierPools { - [key: integer]: Species[] + [key: integer]: Species[] } export enum TrainerSlot { - NONE, - TRAINER, - TRAINER_PARTNER + NONE, + TRAINER, + TRAINER_PARTNER } export class TrainerPartyTemplate { @@ -168,7 +168,7 @@ type PartyMemberFunc = (scene: BattleScene, level: integer, strength: PartyMembe type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[]; export interface PartyMemberFuncs { - [key: integer]: PartyMemberFunc + [key: integer]: PartyMemberFunc } export class TrainerConfig { @@ -201,6 +201,7 @@ export class TrainerConfig { public speciesPools: TrainerTierPools; public speciesFilter: PokemonSpeciesFilter; public specialtyTypes: Type[] = []; + public hasVoucher: boolean = false; public encounterMessages: string[] = []; public victoryMessages: string[] = []; @@ -228,7 +229,7 @@ export class TrainerConfig { return TrainerType[this.getDerivedType()].toString().toLowerCase(); } - getSpriteKey(female?: boolean,isDouble: boolean = false): string { + getSpriteKey(female?: boolean, isDouble: boolean = false): string { let ret = this.getKey(); if (this.hasGenders) { ret += `_${female ? "f" : "m"}`; @@ -257,6 +258,14 @@ export class TrainerConfig { return this; } + /** + * Sets if a boss trainer will have a voucher or not. + * @param hasVoucher - If the boss trainer will have a voucher. + */ + setHasVoucher(hasVoucher: boolean): void { + this.hasVoucher = hasVoucher; + } + setTitle(title: string): TrainerConfig { // First check if i18n is initialized if (!getIsInitialized()) { @@ -275,11 +284,11 @@ export class TrainerConfig { /** - * Returns the derived trainer type for a given trainer type. - * @param trainerTypeToDeriveFrom - The trainer type to derive from. (If null, the this.trainerType property will be used.) - * @returns {TrainerType} - The derived trainer type. - */ - getDerivedType(trainerTypeToDeriveFrom: TrainerType = null): TrainerType { + * Returns the derived trainer type for a given trainer type. + * @param trainerTypeToDeriveFrom - The trainer type to derive from. (If null, the this.trainerType property will be used.) + * @returns {TrainerType} - The derived trainer type. + */ + getDerivedType(trainerTypeToDeriveFrom: TrainerType | null = null): TrainerType { let trainerType = trainerTypeToDeriveFrom ? trainerTypeToDeriveFrom : this.trainerType; switch (trainerType) { case TrainerType.RIVAL_2: @@ -335,11 +344,11 @@ export class TrainerConfig { } /** - * Sets the configuration for trainers with genders, including the female name and encounter background music (BGM). - * @param {string} [nameFemale] - The name of the female trainer. If 'Ivy', a localized name will be assigned. - * @param {TrainerType | string} [femaleEncounterBgm] - The encounter BGM for the female trainer, which can be a TrainerType or a string. - * @returns {TrainerConfig} - The updated TrainerConfig instance. - **/ + * Sets the configuration for trainers with genders, including the female name and encounter background music (BGM). + * @param {string} [nameFemale] - The name of the female trainer. If 'Ivy', a localized name will be assigned. + * @param {TrainerType | string} [femaleEncounterBgm] - The encounter BGM for the female trainer, which can be a TrainerType or a string. + * @returns {TrainerConfig} - The updated TrainerConfig instance. + **/ setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig { // If the female name is 'Ivy' (the rival), assign a localized name. if (nameFemale === "Ivy") { @@ -352,7 +361,7 @@ export class TrainerConfig { this.nameFemale = i18next.t("trainerNames:rival_female"); } else { // Otherwise, assign the provided female name. - this.nameFemale = nameFemale; + this.nameFemale = nameFemale!; // TODO: is this bang correct? } // Indicate that this trainer configuration includes genders. @@ -372,11 +381,11 @@ export class TrainerConfig { } /** - * Sets the configuration for trainers with double battles, including the name of the double trainer and the encounter BGM. - * @param nameDouble - The name of the double trainer (e.g., "Ace Duo" for Trainer Class Doubles or "red_blue_double" for NAMED trainer doubles). - * @param doubleEncounterBgm - The encounter BGM for the double trainer, which can be a TrainerType or a string. - * @returns {TrainerConfig} - The updated TrainerConfig instance. - */ + * Sets the configuration for trainers with double battles, including the name of the double trainer and the encounter BGM. + * @param nameDouble - The name of the double trainer (e.g., "Ace Duo" for Trainer Class Doubles or "red_blue_double" for NAMED trainer doubles). + * @param doubleEncounterBgm - The encounter BGM for the double trainer, which can be a TrainerType or a string. + * @returns {TrainerConfig} - The updated TrainerConfig instance. + */ setHasDouble(nameDouble: string, doubleEncounterBgm?: TrainerType | string): TrainerConfig { this.hasDouble = true; this.nameDouble = nameDouble; @@ -387,10 +396,10 @@ export class TrainerConfig { } /** - * Sets the trainer type for double battles. - * @param trainerTypeDouble - The TrainerType of the partner in a double battle. - * @returns {TrainerConfig} - The updated TrainerConfig instance. - */ + * Sets the trainer type for double battles. + * @param trainerTypeDouble - The TrainerType of the partner in a double battle. + * @returns {TrainerConfig} - The updated TrainerConfig instance. + */ setDoubleTrainerType(trainerTypeDouble: TrainerType): TrainerConfig { this.trainerTypeDouble = trainerTypeDouble; this.setDoubleMessages(this.nameDouble); @@ -398,9 +407,9 @@ export class TrainerConfig { } /** - * Sets the encounter and victory messages for double trainers. - * @param nameDouble - The name of the pair (e.g. "red_blue_double"). - */ + * Sets the encounter and victory messages for double trainers. + * @param nameDouble - The name of the pair (e.g. "red_blue_double"). + */ setDoubleMessages(nameDouble: string) { // Check if there is double battle dialogue for this trainer if (doubleBattleDialogue[nameDouble]) { @@ -412,10 +421,10 @@ export class TrainerConfig { } /** - * Sets the title for double trainers - * @param titleDouble - the key for the title in the i18n file. (e.g., "champion_double"). - * @returns {TrainerConfig} - The updated TrainerConfig instance. - */ + * Sets the title for double trainers + * @param titleDouble - the key for the title in the i18n file. (e.g., "champion_double"). + * @returns {TrainerConfig} - The updated TrainerConfig instance. + */ setDoubleTitle(titleDouble: string): TrainerConfig { // First check if i18n is initialized if (!getIsInitialized()) { @@ -527,6 +536,98 @@ export class TrainerConfig { return this; } + /** + * Returns the pool of species for an evil team admin + * @param team - The evil team the admin belongs to. + * @returns {TrainerTierPools} + */ + speciesPoolPerEvilTeamAdmin(team): TrainerTierPools { + team = team.toLowerCase(); + switch (team) { + case "rocket": { + return { + [TrainerPoolTier.COMMON]: [Species.RATTATA, Species.KOFFING, Species.EKANS, Species.GYARADOS, Species.TAUROS, Species.SCYTHER, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB], + [TrainerPoolTier.UNCOMMON]: [Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE], + [TrainerPoolTier.RARE]: [Species.DRATINI, Species.LARVITAR] + }; + } + case "magma": { + return { + [TrainerPoolTier.COMMON]: [Species.NUMEL, Species.POOCHYENA, Species.SLUGMA, Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.SWINUB, Species.GLIGAR], + [TrainerPoolTier.UNCOMMON]: [Species.TRAPINCH, Species.HEATMOR], + [TrainerPoolTier.RARE]: [Species.TURTONATOR, Species.CHARCADET] + }; + } + case "aqua": { + return { + [TrainerPoolTier.COMMON]: [Species.CARVANHA, Species.CORPHISH, Species.ZIGZAGOON, Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.QWILFISH], + [TrainerPoolTier.UNCOMMON]: [Species.MANTINE, Species.BASCULEGION, Species.REMORAID, Species.ARROKUDA], + [TrainerPoolTier.RARE]: [Species.DONDOZO] + }; + } + case "galactic": { + return { + [TrainerPoolTier.COMMON]: [Species.GLAMEOW, Species.STUNKY, Species.BRONZOR, Species.CARNIVINE, Species.GROWLITHE, Species.QWILFISH, Species.SNEASEL], + [TrainerPoolTier.UNCOMMON]: [Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.HISUI_SNEASEL], + [TrainerPoolTier.RARE]: [Species.HISUI_ZORUA, Species.HISUI_SLIGGOO] + }; + } + case "plasma": { + return { + [TrainerPoolTier.COMMON]: [Species.SCRAFTY, Species.LILLIPUP, Species.PURRLOIN, Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.AMOONGUSS], + [TrainerPoolTier.UNCOMMON]: [Species.PAWNIARD, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK], + [TrainerPoolTier.RARE]: [Species.DRUDDIGON, Species.BOUFFALANT, Species.AXEW, Species.DEINO, Species.DURANT] + }; + } + case "flare": { + return { + [TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.GULPIN, Species.PURRLOIN, Species.POOCHYENA, Species.SCATTERBUG], + [TrainerPoolTier.UNCOMMON]: [Species.LITWICK, Species.SNEASEL, Species.PANCHAM, Species.PAWNIARD], + [TrainerPoolTier.RARE]: [Species.NOIVERN, Species.DRUDDIGON] + }; + } + } + + console.warn(`Evil team admin for ${team} not found. Returning empty species pools.`); + return []; + } + + /** + * Initializes the trainer configuration for an evil team admin. + * @param title - The title of the evil team admin. + * @param poolName - The evil team the admin belongs to. + * @param {Species | Species[]} signatureSpecies - The signature species for the evil team leader. + * @returns {TrainerConfig} - The updated TrainerConfig instance. + * **/ + initForEvilTeamAdmin(title: string, poolName: string, signatureSpecies: (Species | Species[])[],): TrainerConfig { + if (!getIsInitialized()) { + initI18n(); + } + this.setPartyTemplates(trainerPartyTemplates.RIVAL_5); + + // Set the species pools for the evil team admin. + this.speciesPools = this.speciesPoolPerEvilTeamAdmin(poolName); + + signatureSpecies.forEach((speciesPool, s) => { + if (!Array.isArray(speciesPool)) { + speciesPool = [speciesPool]; + } + this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); + }); + + const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); + this.name = i18next.t(`trainerNames:${nameForCall}`); + this.setHasVoucher(false); + this.setTitle(title); + this.setMoneyMultiplier(1.5); + this.setBoss(); + this.setStaticParty(); + this.setBattleBgm("battle_plasma_boss"); + this.setVictoryBgm("victory_team_plasma"); + + return this; + } + /** * Initializes the trainer configuration for an evil team leader. Temporarily hardcoding evil leader teams though. * @param {Species | Species[]} signatureSpecies - The signature species for the evil team leader. @@ -559,6 +660,7 @@ export class TrainerConfig { this.setMoneyMultiplier(2.5); this.setBoss(); this.setStaticParty(); + this.setHasVoucher(true); this.setBattleBgm("battle_plasma_boss"); this.setVictoryBgm("victory_team_plasma"); @@ -566,13 +668,13 @@ export class TrainerConfig { } /** - * Initializes the trainer configuration for a Gym Leader. - * @param {Species | Species[]} signatureSpecies - The signature species for the Gym Leader. - * @param {Type[]} specialtyTypes - The specialty types for the Gym Leader. - * @param isMale - Whether the Gym Leader is Male or Not (for localization of the title). - * @returns {TrainerConfig} - The updated TrainerConfig instance. - * **/ - initForGymLeader(signatureSpecies: (Species | Species[])[],isMale:boolean, ...specialtyTypes: Type[]): TrainerConfig { + * Initializes the trainer configuration for a Gym Leader. + * @param {Species | Species[]} signatureSpecies - The signature species for the Gym Leader. + * @param {Type[]} specialtyTypes - The specialty types for the Gym Leader. + * @param isMale - Whether the Gym Leader is Male or Not (for localization of the title). + * @returns {TrainerConfig} - The updated TrainerConfig instance. + * **/ + initForGymLeader(signatureSpecies: (Species | Species[])[], isMale: boolean, ...specialtyTypes: Type[]): TrainerConfig { // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { initI18n(); @@ -611,24 +713,25 @@ export class TrainerConfig { this.setMoneyMultiplier(2.5); this.setBoss(); this.setStaticParty(); + this.setHasVoucher(true); this.setBattleBgm("battle_unova_gym"); this.setVictoryBgm("victory_gym"); this.setGenModifiersFunc(party => { const waveIndex = party[0].scene.currentBattle.waveIndex; - return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : null); + return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : undefined); }); return this; } /** - * Initializes the trainer configuration for an Elite Four member. - * @param {Species | Species[]} signatureSpecies - The signature species for the Elite Four member. - * @param {Type[]} specialtyTypes - The specialty types for the Elite Four member. - * @param isMale - Whether the Elite Four Member is Male or Female (for localization of the title). - * @returns {TrainerConfig} - The updated TrainerConfig instance. - **/ - initForEliteFour(signatureSpecies: (Species | Species[])[],isMale: boolean, ...specialtyTypes: Type[]): TrainerConfig { + * Initializes the trainer configuration for an Elite Four member. + * @param {Species | Species[]} signatureSpecies - The signature species for the Elite Four member. + * @param {Type[]} specialtyTypes - The specialty types for the Elite Four member. + * @param isMale - Whether the Elite Four Member is Male or Female (for localization of the title). + * @returns {TrainerConfig} - The updated TrainerConfig instance. + **/ + initForEliteFour(signatureSpecies: (Species | Species[])[], isMale: boolean, ...specialtyTypes: Type[]): TrainerConfig { // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { initI18n(); @@ -649,7 +752,7 @@ export class TrainerConfig { // Set species filter and specialty types if provided, otherwise filter by base total. if (specialtyTypes.length) { - this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) && p.baseTotal >= 450); + this.setSpeciesFilter(p => specialtyTypes.some(t => p.isOfType(t)) && p.baseTotal >= 450); this.setSpecialtyTypes(...specialtyTypes); } else { this.setSpeciesFilter(p => p.baseTotal >= 450); @@ -669,19 +772,20 @@ export class TrainerConfig { this.setMoneyMultiplier(3.25); this.setBoss(); this.setStaticParty(); + this.setHasVoucher(true); this.setBattleBgm("battle_unova_elite"); this.setVictoryBgm("victory_gym"); - this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : null)); + this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : undefined)); return this; } /** - * Initializes the trainer configuration for a Champion. - * @param {Species | Species[]} signatureSpecies - The signature species for the Champion. - * @param isMale - Whether the Champion is Male or Female (for localization of the title). - * @returns {TrainerConfig} - The updated TrainerConfig instance. - **/ + * Initializes the trainer configuration for a Champion. + * @param {Species | Species[]} signatureSpecies - The signature species for the Champion. + * @param isMale - Whether the Champion is Male or Female (for localization of the title). + * @returns {TrainerConfig} - The updated TrainerConfig instance. + **/ initForChampion(signatureSpecies: (Species | Species[])[], isMale: boolean): TrainerConfig { // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { @@ -719,6 +823,7 @@ export class TrainerConfig { this.setMoneyMultiplier(10); this.setBoss(); this.setStaticParty(); + this.setHasVoucher(true); this.setBattleBgm("battle_champion_alder"); this.setVictoryBgm("victory_champion"); this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 3)); @@ -727,11 +832,11 @@ export class TrainerConfig { } /** - * Retrieves the title for the trainer based on the provided trainer slot and variant. - * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE. - * @param {TrainerVariant} variant - The variant of the trainer to determine the specific title. - * @returns {string} - The title of the trainer. - **/ + * Retrieves the title for the trainer based on the provided trainer slot and variant. + * @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE. + * @param {TrainerVariant} variant - The variant of the trainer to determine the specific title. + * @returns {string} - The title of the trainer. + **/ getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string { const ret = this.name; @@ -756,11 +861,11 @@ export class TrainerConfig { } // Check if the female version exists in the i18n file if (i18next.exists(`trainerClasses:${this.name.toLowerCase()}`)) { - // If it does, return + // If it does, return return ret + "_female"; } else { - // If it doesn't, we do not do anything and go to the normal return - // This is to prevent the game from displaying an error if a female version of the trainer does not exist in the localization + // If it doesn't, we do not do anything and go to the normal return + // This is to prevent the game from displaying an error if a female version of the trainer does not exist in the localization } } } @@ -772,7 +877,7 @@ export class TrainerConfig { return new Promise(resolve => { const isDouble = variant === TrainerVariant.DOUBLE; const trainerKey = this.getSpriteKey(variant === TrainerVariant.FEMALE, false); - const partnerTrainerKey = this.getSpriteKey(true,true); + const partnerTrainerKey = this.getSpriteKey(true, true); scene.loadAtlas(trainerKey, "trainer"); if (isDouble) { scene.loadAtlas(partnerTrainerKey, "trainer"); @@ -782,10 +887,20 @@ export class TrainerConfig { // Ignore warnings for missing frames, because there will be a lot console.warn = () => { }; - const frameNames = scene.anims.generateFrameNames(trainerKey, {zeroPad: 4,suffix: ".png",start: 1,end: 128}); + const frameNames = scene.anims.generateFrameNames(trainerKey, { + zeroPad: 4, + suffix: ".png", + start: 1, + end: 128 + }); const partnerFrameNames = isDouble - ? scene.anims.generateFrameNames(partnerTrainerKey, {zeroPad: 4,suffix: ".png",start: 1,end: 128}) - : null; + ? scene.anims.generateFrameNames(partnerTrainerKey, { + zeroPad: 4, + suffix: ".png", + start: 1, + end: 128 + }) + : ""; console.warn = originalWarn; if (!(scene.anims.exists(trainerKey))) { scene.anims.create({ @@ -815,7 +930,7 @@ export class TrainerConfig { let t = 0; interface TrainerConfigs { - [key: integer]: TrainerConfig + [key: integer]: TrainerConfig } /** @@ -871,13 +986,13 @@ function getRandomTeraModifiers(party: EnemyPokemon[], count: integer, types?: T for (let t = 0; t < Math.min(count, party.length); t++) { const randomIndex = Utils.randSeedItem(partyMemberIndexes); partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1); - ret.push(modifierTypes.TERA_SHARD().generateType(null, [Utils.randSeedItem(types ? types : party[randomIndex].getTypes())]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); + ret.push(modifierTypes.TERA_SHARD().generateType([], [Utils.randSeedItem(types ? types : party[randomIndex].getTypes())])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); // TODO: is the bang correct? } return ret; } type SignatureSpecies = { - [key in string]: (Species | Species[])[]; + [key in string]: (Species | Species[])[]; }; /* @@ -991,7 +1106,7 @@ export const signatureSpecies: SignatureSpecies = { MARNIE_ELITE: [Species.MORPEKO, Species.LIEPARD, [Species.TOXICROAK, Species.SCRAFTY], Species.GRIMMSNARL], NESSA_ELITE: [Species.GOLISOPOD, [Species.PELIPPER, Species.QUAGSIRE], Species.TOXAPEX, Species.DREDNAW], BEA_ELITE: [Species.HAWLUCHA, [Species.GRAPPLOCT, Species.SIRFETCHD], Species.FALINKS, Species.MACHAMP], - ALLISTER_ELITE:[Species.DUSKNOIR, [Species.POLTEAGEIST, Species.RUNERIGUS], Species.CURSOLA, Species.GENGAR], + ALLISTER_ELITE: [Species.DUSKNOIR, [Species.POLTEAGEIST, Species.RUNERIGUS], Species.CURSOLA, Species.GENGAR], RAIHAN_ELITE: [Species.GOODRA, [Species.TORKOAL, Species.TURTONATOR], Species.FLYGON, Species.ARCHALUDON], RIKA: [Species.WHISCASH, [Species.DONPHAN, Species.DUGTRIO], Species.CAMERUPT, Species.CLODSIRE], POPPY: [Species.COPPERAJAH, Species.BRONZONG, Species.CORVIKNIGHT, Species.TINKATON], @@ -1182,17 +1297,15 @@ export const trainerConfigs: TrainerConfigs = { ), [TrainerType.ROCKET_GRUNT]: new TrainerConfig(++t).setHasGenders("Rocket Grunt Female").setHasDouble("Rocket Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.WEEDLE, Species.RATTATA, Species.EKANS, Species.SANDSHREW, Species.ZUBAT, Species.GEODUDE, Species.KOFFING, Species.GRIMER, Species.ODDISH], - [TrainerPoolTier.UNCOMMON]: [ Species.GYARADOS, Species.TAUROS, Species.SCYTHER, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB], + [TrainerPoolTier.COMMON]: [Species.WEEDLE, Species.RATTATA, Species.EKANS, Species.SANDSHREW, Species.ZUBAT, Species.GEODUDE, Species.KOFFING, Species.GRIMER, Species.ODDISH], + [TrainerPoolTier.UNCOMMON]: [Species.GYARADOS, Species.TAUROS, Species.SCYTHER, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB], [TrainerPoolTier.RARE]: [Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE], [TrainerPoolTier.SUPER_RARE]: [Species.DRATINI, Species.LARVITAR] }), - [TrainerType.ROCKET_ADMIN]: new TrainerConfig(++t).setHasGenders().setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) - .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.RATTATA, Species.KOFFING, Species.EKANS, Species.GYARADOS, Species.TAUROS, Species.SCYTHER, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB], - [TrainerPoolTier.UNCOMMON]: [Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE], - [TrainerPoolTier.RARE]: [Species.DRATINI, Species.LARVITAR] - }), + [TrainerType.ARCHER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [Species.HOUNDOOM]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.ARIANA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin_female", "rocket", [Species.ARBOK]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.PROTON]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [Species.CROBAT]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.PETREL]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [Species.WEEZING]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.MAGMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Magma Grunt Female").setHasDouble("Magma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [Species.SLUGMA, Species.POOCHYENA, Species.NUMEL, Species.ZIGZAGOON, Species.DIGLETT, Species.MAGBY, Species.TORKOAL, Species.BALTOY, Species.BARBOACH], @@ -1200,182 +1313,165 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.RARE]: [Species.TRAPINCH, Species.HEATMOR], [TrainerPoolTier.SUPER_RARE]: [Species.TURTONATOR, Species.CHARCADET] }), - [TrainerType.MAGMA_ADMIN]: new TrainerConfig(++t).setHasGenders().setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) - .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.NUMEL, Species.POOCHYENA, Species.SLUGMA, Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.SWINUB, Species.GLIGAR], - [TrainerPoolTier.UNCOMMON]: [Species.TRAPINCH, Species.HEATMOR], - [TrainerPoolTier.RARE]: [Species.TURTONATOR, Species.CHARCADET] - }), + [TrainerType.TABITHA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin", "magma", [Species.CAMERUPT]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.COURTNEY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin_female", "magma", [Species.CAMERUPT]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.AQUA_GRUNT]: new TrainerConfig(++t).setHasGenders("Aqua Grunt Female").setHasDouble("Aqua Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL ], - [TrainerPoolTier.UNCOMMON]: [Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.QWILFISH ], + [TrainerPoolTier.COMMON]: [Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL], + [TrainerPoolTier.UNCOMMON]: [Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.QWILFISH], [TrainerPoolTier.RARE]: [Species.MANTINE, Species.BASCULEGION, Species.REMORAID, Species.ARROKUDA], [TrainerPoolTier.SUPER_RARE]: [Species.DONDOZO] }), - [TrainerType.AQUA_ADMIN]: new TrainerConfig(++t).setHasGenders().setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) - .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.CARVANHA, Species.CORPHISH, Species.ZIGZAGOON, Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.QWILFISH ], - [TrainerPoolTier.UNCOMMON]: [Species.MANTINE, Species.BASCULEGION, Species.REMORAID, Species.ARROKUDA], - [TrainerPoolTier.RARE]: [Species.DONDOZO] - }), + [TrainerType.MATT]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin", "aqua", [Species.SHARPEDO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.SHELLY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin_female", "aqua", [Species.SHARPEDO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.GALACTIC_GRUNT]: new TrainerConfig(++t).setHasGenders("Galactic Grunt Female").setHasDouble("Galactic Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.GLAMEOW, Species.STUNKY, Species.CROAGUNK, Species.SHINX, Species.WURMPLE, Species.BRONZOR, Species.DRIFLOON, Species.BURMY], - [TrainerPoolTier.UNCOMMON]: [ Species.CARNIVINE, Species.GROWLITHE, Species.QWILFISH, Species.SNEASEL ], + [TrainerPoolTier.COMMON]: [Species.GLAMEOW, Species.STUNKY, Species.CROAGUNK, Species.SHINX, Species.WURMPLE, Species.BRONZOR, Species.DRIFLOON, Species.BURMY], + [TrainerPoolTier.UNCOMMON]: [Species.CARNIVINE, Species.GROWLITHE, Species.QWILFISH, Species.SNEASEL], [TrainerPoolTier.RARE]: [Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.HISUI_SNEASEL], [TrainerPoolTier.SUPER_RARE]: [Species.HISUI_ZORUA, Species.HISUI_SLIGGOO] }), - [TrainerType.GALACTIC_ADMIN]: new TrainerConfig(++t).setHasGenders().setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) - .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.GLAMEOW, Species.STUNKY, Species.BRONZOR, Species.CARNIVINE, Species.GROWLITHE, Species.QWILFISH, Species.SNEASEL ], - [TrainerPoolTier.UNCOMMON]: [Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.HISUI_SNEASEL], - [TrainerPoolTier.RARE]: [Species.HISUI_ZORUA, Species.HISUI_SLIGGOO] - }), + + [TrainerType.JUPITER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [Species.SKUNTANK]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.MARS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [Species.PURUGLY]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.SATURN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander", "galactic", [Species.TOXICROAK]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.PLASMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Plasma Grunt Female").setHasDouble("Plasma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH], - [TrainerPoolTier.UNCOMMON]: [ Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.AMOONGUSS], + [TrainerPoolTier.COMMON]: [Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH], + [TrainerPoolTier.UNCOMMON]: [Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.AMOONGUSS], [TrainerPoolTier.RARE]: [Species.PAWNIARD, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK], [TrainerPoolTier.SUPER_RARE]: [Species.DRUDDIGON, Species.BOUFFALANT, Species.AXEW, Species.DEINO, Species.DURANT] }), - [TrainerType.PLASMA_SAGE]: new TrainerConfig(++t).setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) - .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.SCRAFTY, Species.LILLIPUP, Species.PURRLOIN, Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.AMOONGUSS], - [TrainerPoolTier.UNCOMMON]: [Species.PAWNIARD, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK], - [TrainerPoolTier.RARE]: [Species.DRUDDIGON, Species.BOUFFALANT, Species.AXEW, Species.DEINO, Species.DURANT] - }), + [TrainerType.ZINZOLIN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [Species.CRYOGONAL]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.ROOD]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [Species.SWOOBAT]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.FLARE_GRUNT]: new TrainerConfig(++t).setHasGenders("Flare Grunt Female").setHasDouble("Flare Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK], + [TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK], [TrainerPoolTier.UNCOMMON]: [Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.GULPIN, Species.PURRLOIN, Species.POOCHYENA, Species.SCATTERBUG], [TrainerPoolTier.RARE]: [Species.LITWICK, Species.SNEASEL, Species.PANCHAM, Species.PAWNIARD], [TrainerPoolTier.SUPER_RARE]: [Species.NOIVERN, Species.DRUDDIGON] }), - [TrainerType.FLARE_ADMIN]: new TrainerConfig(++t).setHasGenders().setMoneyMultiplier(1.5).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) - .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.GULPIN, Species.PURRLOIN, Species.POOCHYENA, Species.SCATTERBUG], - [TrainerPoolTier.UNCOMMON]: [Species.LITWICK, Species.SNEASEL, Species.PANCHAM, Species.PAWNIARD], - [TrainerPoolTier.RARE]: [Species.NOIVERN, Species.DRUDDIGON] - }), - [TrainerType.BROCK]: new TrainerConfig((t = TrainerType.BROCK)).initForGymLeader(signatureSpecies["BROCK"],true, Type.ROCK).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.MISTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MISTY"],false, Type.WATER).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.LT_SURGE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LT_SURGE"],true, Type.ELECTRIC).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.ERIKA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ERIKA"],false, Type.GRASS).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.JANINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JANINE"],false, Type.POISON).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.SABRINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["SABRINA"],false, Type.PSYCHIC).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.BLAINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BLAINE"],true, Type.FIRE).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.GIOVANNI]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GIOVANNI"],true, Type.DARK).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.FALKNER]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FALKNER"],true, Type.FLYING).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.BUGSY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BUGSY"],true, Type.BUG).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.WHITNEY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WHITNEY"],false, Type.NORMAL).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.MORTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MORTY"],true, Type.GHOST).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.CHUCK]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHUCK"],true, Type.FIGHTING).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.JASMINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JASMINE"],false, Type.STEEL).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.PRYCE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["PRYCE"],true, Type.ICE).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.CLAIR]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLAIR"],false, Type.DRAGON).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.ROXANNE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROXANNE"],false, Type.ROCK).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), - [TrainerType.BRAWLY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRAWLY"],true, Type.FIGHTING).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), - [TrainerType.WATTSON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WATTSON"],true, Type.ELECTRIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), - [TrainerType.FLANNERY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FLANNERY"],false, Type.FIRE).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), - [TrainerType.NORMAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["NORMAN"],true, Type.NORMAL).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), - [TrainerType.WINONA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WINONA"],false, Type.FLYING).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), - [TrainerType.TATE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TATE"],true, Type.PSYCHIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym").setHasDouble("tate_liza_double").setDoubleTrainerType(TrainerType.LIZA).setDoubleTitle("gym_leader_double"), - [TrainerType.LIZA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LIZA"],false, Type.PSYCHIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym").setHasDouble("liza_tate_double").setDoubleTrainerType(TrainerType.TATE).setDoubleTitle("gym_leader_double"), - [TrainerType.JUAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JUAN"],true, Type.WATER).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), - [TrainerType.ROARK]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROARK"],true, Type.ROCK).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.GARDENIA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GARDENIA"],false, Type.GRASS).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.MAYLENE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MAYLENE"],false, Type.FIGHTING).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.CRASHER_WAKE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CRASHER_WAKE"],true, Type.WATER).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.FANTINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FANTINA"],false, Type.GHOST).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.BYRON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BYRON"],true, Type.STEEL).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.CANDICE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CANDICE"],false, Type.ICE).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.VOLKNER]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VOLKNER"],true, Type.ELECTRIC).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.CILAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CILAN"],true, Type.GRASS).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.CHILI]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHILI"],true, Type.FIRE).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.CRESS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CRESS"],true, Type.WATER).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.CHEREN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHEREN"],true, Type.NORMAL).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.LENORA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LENORA"],false, Type.NORMAL).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.ROXIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROXIE"],false, Type.POISON).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.BURGH]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BURGH"],true, Type.BUG).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.ELESA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ELESA"],false, Type.ELECTRIC).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.CLAY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLAY"],true, Type.GROUND).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.SKYLA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["SKYLA"],false, Type.FLYING).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.BRYCEN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRYCEN"],true, Type.ICE).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.DRAYDEN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["DRAYDEN"],true, Type.DRAGON).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.MARLON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MARLON"],true, Type.WATER).setMixedBattleBgm("battle_unova_gym"), - [TrainerType.VIOLA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VIOLA"],false, Type.BUG).setMixedBattleBgm("battle_kalos_gym"), - [TrainerType.GRANT]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRANT"],true, Type.ROCK).setMixedBattleBgm("battle_kalos_gym"), - [TrainerType.KORRINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KORRINA"],false, Type.FIGHTING).setMixedBattleBgm("battle_kalos_gym"), - [TrainerType.RAMOS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RAMOS"],true, Type.GRASS).setMixedBattleBgm("battle_kalos_gym"), - [TrainerType.CLEMONT]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLEMONT"],true, Type.ELECTRIC).setMixedBattleBgm("battle_kalos_gym"), - [TrainerType.VALERIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VALERIE"],false, Type.FAIRY).setMixedBattleBgm("battle_kalos_gym"), - [TrainerType.OLYMPIA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["OLYMPIA"],false, Type.PSYCHIC).setMixedBattleBgm("battle_kalos_gym"), - [TrainerType.WULFRIC]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WULFRIC"],true, Type.ICE).setMixedBattleBgm("battle_kalos_gym"), - [TrainerType.MILO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MILO"],true, Type.GRASS).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.NESSA]: new TrainerConfig(++t).setName("Nessa").initForGymLeader(signatureSpecies["NESSA"],false, Type.WATER).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.KABU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KABU"],true, Type.FIRE).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.BEA]: new TrainerConfig(++t).setName("Bea").initForGymLeader(signatureSpecies["BEA"],false, Type.FIGHTING).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.ALLISTER]: new TrainerConfig(++t).setName("Allister").initForGymLeader(signatureSpecies["ALLISTER"],true, Type.GHOST).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.OPAL]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["OPAL"],false, Type.FAIRY).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.BEDE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BEDE"],true, Type.FAIRY).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.GORDIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GORDIE"],true, Type.ROCK).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.MELONY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MELONY"],false, Type.ICE).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.PIERS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["PIERS"],true, Type.DARK).setHasDouble("piers_marnie_double").setDoubleTrainerType(TrainerType.MARNIE).setDoubleTitle("gym_leader_double").setMixedBattleBgm("battle_galar_gym"), - [TrainerType.MARNIE]: new TrainerConfig(++t).setName("Marnie").initForGymLeader(signatureSpecies["MARNIE"],false, Type.DARK).setHasDouble("marnie_piers_double").setDoubleTrainerType(TrainerType.PIERS).setDoubleTitle("gym_leader_double").setMixedBattleBgm("battle_galar_gym"), - [TrainerType.RAIHAN]: new TrainerConfig(++t).setName("Raihan").initForGymLeader(signatureSpecies["RAIHAN"],true, Type.DRAGON).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.KATY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KATY"],false, Type.BUG).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.BRASSIUS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRASSIUS"],true, Type.GRASS).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.IONO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["IONO"],false, Type.ELECTRIC).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.KOFU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KOFU"],true, Type.WATER).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.LARRY]: new TrainerConfig(++t).setName("Larry").initForGymLeader(signatureSpecies["LARRY"],true, Type.NORMAL).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.RYME]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RYME"],false, Type.GHOST).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.TULIP]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TULIP"],false, Type.PSYCHIC).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.GRUSHA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRUSHA"],true, Type.ICE).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.BRYONY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin_female", "flare", [Species.LIEPARD]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.XEROSIC]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin", "flare", [Species.MALAMAR]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), + [TrainerType.BROCK]: new TrainerConfig((t = TrainerType.BROCK)).initForGymLeader(signatureSpecies["BROCK"], true, Type.ROCK).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.MISTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MISTY"], false, Type.WATER).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.LT_SURGE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LT_SURGE"], true, Type.ELECTRIC).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.ERIKA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ERIKA"], false, Type.GRASS).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.JANINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JANINE"], false, Type.POISON).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.SABRINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["SABRINA"], false, Type.PSYCHIC).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.BLAINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BLAINE"], true, Type.FIRE).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.GIOVANNI]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GIOVANNI"], true, Type.DARK).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.FALKNER]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FALKNER"], true, Type.FLYING).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.BUGSY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BUGSY"], true, Type.BUG).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.WHITNEY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WHITNEY"], false, Type.NORMAL).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.MORTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MORTY"], true, Type.GHOST).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.CHUCK]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHUCK"], true, Type.FIGHTING).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.JASMINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JASMINE"], false, Type.STEEL).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.PRYCE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["PRYCE"], true, Type.ICE).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.CLAIR]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLAIR"], false, Type.DRAGON).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.ROXANNE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROXANNE"], false, Type.ROCK).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), + [TrainerType.BRAWLY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRAWLY"], true, Type.FIGHTING).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), + [TrainerType.WATTSON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WATTSON"], true, Type.ELECTRIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), + [TrainerType.FLANNERY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FLANNERY"], false, Type.FIRE).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), + [TrainerType.NORMAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["NORMAN"], true, Type.NORMAL).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), + [TrainerType.WINONA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WINONA"], false, Type.FLYING).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), + [TrainerType.TATE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TATE"], true, Type.PSYCHIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym").setHasDouble("tate_liza_double").setDoubleTrainerType(TrainerType.LIZA).setDoubleTitle("gym_leader_double"), + [TrainerType.LIZA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LIZA"], false, Type.PSYCHIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym").setHasDouble("liza_tate_double").setDoubleTrainerType(TrainerType.TATE).setDoubleTitle("gym_leader_double"), + [TrainerType.JUAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JUAN"], true, Type.WATER).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"), + [TrainerType.ROARK]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROARK"], true, Type.ROCK).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.GARDENIA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GARDENIA"], false, Type.GRASS).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.MAYLENE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MAYLENE"], false, Type.FIGHTING).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.CRASHER_WAKE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CRASHER_WAKE"], true, Type.WATER).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.FANTINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FANTINA"], false, Type.GHOST).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.BYRON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BYRON"], true, Type.STEEL).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.CANDICE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CANDICE"], false, Type.ICE).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.VOLKNER]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VOLKNER"], true, Type.ELECTRIC).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.CILAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CILAN"], true, Type.GRASS).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.CHILI]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHILI"], true, Type.FIRE).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.CRESS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CRESS"], true, Type.WATER).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.CHEREN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHEREN"], true, Type.NORMAL).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.LENORA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LENORA"], false, Type.NORMAL).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.ROXIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROXIE"], false, Type.POISON).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.BURGH]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BURGH"], true, Type.BUG).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.ELESA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ELESA"], false, Type.ELECTRIC).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.CLAY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLAY"], true, Type.GROUND).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.SKYLA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["SKYLA"], false, Type.FLYING).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.BRYCEN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRYCEN"], true, Type.ICE).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.DRAYDEN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["DRAYDEN"], true, Type.DRAGON).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.MARLON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MARLON"], true, Type.WATER).setMixedBattleBgm("battle_unova_gym"), + [TrainerType.VIOLA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VIOLA"], false, Type.BUG).setMixedBattleBgm("battle_kalos_gym"), + [TrainerType.GRANT]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRANT"], true, Type.ROCK).setMixedBattleBgm("battle_kalos_gym"), + [TrainerType.KORRINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KORRINA"], false, Type.FIGHTING).setMixedBattleBgm("battle_kalos_gym"), + [TrainerType.RAMOS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RAMOS"], true, Type.GRASS).setMixedBattleBgm("battle_kalos_gym"), + [TrainerType.CLEMONT]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLEMONT"], true, Type.ELECTRIC).setMixedBattleBgm("battle_kalos_gym"), + [TrainerType.VALERIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VALERIE"], false, Type.FAIRY).setMixedBattleBgm("battle_kalos_gym"), + [TrainerType.OLYMPIA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["OLYMPIA"], false, Type.PSYCHIC).setMixedBattleBgm("battle_kalos_gym"), + [TrainerType.WULFRIC]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WULFRIC"], true, Type.ICE).setMixedBattleBgm("battle_kalos_gym"), + [TrainerType.MILO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MILO"], true, Type.GRASS).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.NESSA]: new TrainerConfig(++t).setName("Nessa").initForGymLeader(signatureSpecies["NESSA"], false, Type.WATER).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.KABU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KABU"], true, Type.FIRE).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.BEA]: new TrainerConfig(++t).setName("Bea").initForGymLeader(signatureSpecies["BEA"], false, Type.FIGHTING).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.ALLISTER]: new TrainerConfig(++t).setName("Allister").initForGymLeader(signatureSpecies["ALLISTER"], true, Type.GHOST).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.OPAL]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["OPAL"], false, Type.FAIRY).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.BEDE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BEDE"], true, Type.FAIRY).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.GORDIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GORDIE"], true, Type.ROCK).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.MELONY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MELONY"], false, Type.ICE).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.PIERS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["PIERS"], true, Type.DARK).setHasDouble("piers_marnie_double").setDoubleTrainerType(TrainerType.MARNIE).setDoubleTitle("gym_leader_double").setMixedBattleBgm("battle_galar_gym"), + [TrainerType.MARNIE]: new TrainerConfig(++t).setName("Marnie").initForGymLeader(signatureSpecies["MARNIE"], false, Type.DARK).setHasDouble("marnie_piers_double").setDoubleTrainerType(TrainerType.PIERS).setDoubleTitle("gym_leader_double").setMixedBattleBgm("battle_galar_gym"), + [TrainerType.RAIHAN]: new TrainerConfig(++t).setName("Raihan").initForGymLeader(signatureSpecies["RAIHAN"], true, Type.DRAGON).setMixedBattleBgm("battle_galar_gym"), + [TrainerType.KATY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KATY"], false, Type.BUG).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.BRASSIUS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRASSIUS"], true, Type.GRASS).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.IONO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["IONO"], false, Type.ELECTRIC).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.KOFU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KOFU"], true, Type.WATER).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.LARRY]: new TrainerConfig(++t).setName("Larry").initForGymLeader(signatureSpecies["LARRY"], true, Type.NORMAL).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.RYME]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RYME"], false, Type.GHOST).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.TULIP]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TULIP"], false, Type.PSYCHIC).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.GRUSHA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRUSHA"], true, Type.ICE).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.LORELEI]: new TrainerConfig((t = TrainerType.LORELEI)).initForEliteFour(signatureSpecies["LORELEI"],false, Type.ICE).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.LORELEI]: new TrainerConfig((t = TrainerType.LORELEI)).initForEliteFour(signatureSpecies["LORELEI"], false, Type.ICE).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), [TrainerType.BRUNO]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["BRUNO"], true, Type.FIGHTING).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.AGATHA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AGATHA"], false,Type.GHOST).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.LANCE]: new TrainerConfig(++t).setName("Lance").initForEliteFour(signatureSpecies["LANCE"],true, Type.DRAGON).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), - [TrainerType.WILL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["WILL"],true, Type.PSYCHIC).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.AGATHA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AGATHA"], false, Type.GHOST).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.LANCE]: new TrainerConfig(++t).setName("Lance").initForEliteFour(signatureSpecies["LANCE"], true, Type.DRAGON).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), + [TrainerType.WILL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["WILL"], true, Type.PSYCHIC).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), [TrainerType.KOGA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KOGA"], true, Type.POISON).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.KAREN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KAREN"],false, Type.DARK).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), - [TrainerType.SIDNEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SIDNEY"],true, Type.DARK).setMixedBattleBgm("battle_hoenn_elite"), - [TrainerType.PHOEBE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["PHOEBE"],false, Type.GHOST).setMixedBattleBgm("battle_hoenn_elite"), - [TrainerType.GLACIA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["GLACIA"],false, Type.ICE).setMixedBattleBgm("battle_hoenn_elite"), - [TrainerType.DRAKE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAKE"],true, Type.DRAGON).setMixedBattleBgm("battle_hoenn_elite"), - [TrainerType.AARON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AARON"],true, Type.BUG).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.BERTHA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["BERTHA"],false, Type.GROUND).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.FLINT]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["FLINT"],true, Type.FIRE).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.LUCIAN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LUCIAN"], true,Type.PSYCHIC).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.SHAUNTAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SHAUNTAL"],false, Type.GHOST).setMixedBattleBgm("battle_unova_elite"), - [TrainerType.MARSHAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MARSHAL"],true, Type.FIGHTING).setMixedBattleBgm("battle_unova_elite"), - [TrainerType.GRIMSLEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["GRIMSLEY"],true, Type.DARK).setMixedBattleBgm("battle_unova_elite"), - [TrainerType.CAITLIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CAITLIN"],false, Type.PSYCHIC).setMixedBattleBgm("battle_unova_elite"), - [TrainerType.MALVA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MALVA"], false,Type.FIRE).setMixedBattleBgm("battle_kalos_elite"), - [TrainerType.SIEBOLD]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SIEBOLD"], true,Type.WATER).setMixedBattleBgm("battle_kalos_elite"), - [TrainerType.WIKSTROM]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["WIKSTROM"],true, Type.STEEL).setMixedBattleBgm("battle_kalos_elite"), - [TrainerType.DRASNA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRASNA"],false, Type.DRAGON).setMixedBattleBgm("battle_kalos_elite"), - [TrainerType.HALA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HALA"],true, Type.FIGHTING).setMixedBattleBgm("battle_alola_elite"), - [TrainerType.MOLAYNE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MOLAYNE"],true, Type.STEEL).setMixedBattleBgm("battle_alola_elite"), - [TrainerType.OLIVIA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["OLIVIA"],false, Type.ROCK).setMixedBattleBgm("battle_alola_elite"), - [TrainerType.ACEROLA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["ACEROLA"],false, Type.GHOST).setMixedBattleBgm("battle_alola_elite"), - [TrainerType.KAHILI]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KAHILI"],false, Type.FLYING).setMixedBattleBgm("battle_alola_elite"), - [TrainerType.MARNIE_ELITE]: new TrainerConfig(++t).setName("Marnie").initForEliteFour(signatureSpecies["MARNIE_ELITE"],false, Type.DARK).setMixedBattleBgm("battle_galar_elite"), - [TrainerType.NESSA_ELITE]: new TrainerConfig(++t).setName("Nessa").initForEliteFour(signatureSpecies["NESSA_ELITE"],false, Type.WATER).setMixedBattleBgm("battle_galar_elite"), - [TrainerType.BEA_ELITE]: new TrainerConfig(++t).setName("Bea").initForEliteFour(signatureSpecies["BEA_ELITE"],false, Type.FIGHTING).setMixedBattleBgm("battle_galar_elite"), - [TrainerType.ALLISTER_ELITE]: new TrainerConfig(++t).setName("Allister").initForEliteFour(signatureSpecies["ALLISTER_ELITE"],true, Type.GHOST).setMixedBattleBgm("battle_galar_elite"), - [TrainerType.RAIHAN_ELITE]: new TrainerConfig(++t).setName("Raihan").initForEliteFour(signatureSpecies["RAIHAN_ELITE"],true, Type.DRAGON).setMixedBattleBgm("battle_galar_elite"), - [TrainerType.RIKA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["RIKA"],false, Type.GROUND).setMixedBattleBgm("battle_paldea_elite"), - [TrainerType.POPPY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["POPPY"],false, Type.STEEL).setMixedBattleBgm("battle_paldea_elite"), - [TrainerType.LARRY_ELITE]: new TrainerConfig(++t).setName("Larry").initForEliteFour(signatureSpecies["LARRY_ELITE"],true, Type.NORMAL, Type.FLYING).setMixedBattleBgm("battle_paldea_elite"), - [TrainerType.HASSEL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HASSEL"],true, Type.DRAGON).setMixedBattleBgm("battle_paldea_elite"), - [TrainerType.CRISPIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CRISPIN"],true, Type.FIRE).setMixedBattleBgm("battle_bb_elite"), - [TrainerType.AMARYS]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AMARYS"],false, Type.STEEL).setMixedBattleBgm("battle_bb_elite"), - [TrainerType.LACEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LACEY"],false, Type.FAIRY).setMixedBattleBgm("battle_bb_elite"), - [TrainerType.DRAYTON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAYTON"],true, Type.DRAGON).setMixedBattleBgm("battle_bb_elite"), + [TrainerType.KAREN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KAREN"], false, Type.DARK).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"), + [TrainerType.SIDNEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SIDNEY"], true, Type.DARK).setMixedBattleBgm("battle_hoenn_elite"), + [TrainerType.PHOEBE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["PHOEBE"], false, Type.GHOST).setMixedBattleBgm("battle_hoenn_elite"), + [TrainerType.GLACIA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["GLACIA"], false, Type.ICE).setMixedBattleBgm("battle_hoenn_elite"), + [TrainerType.DRAKE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAKE"], true, Type.DRAGON).setMixedBattleBgm("battle_hoenn_elite"), + [TrainerType.AARON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AARON"], true, Type.BUG).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.BERTHA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["BERTHA"], false, Type.GROUND).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.FLINT]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["FLINT"], true, Type.FIRE).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.LUCIAN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LUCIAN"], true, Type.PSYCHIC).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.SHAUNTAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SHAUNTAL"], false, Type.GHOST).setMixedBattleBgm("battle_unova_elite"), + [TrainerType.MARSHAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MARSHAL"], true, Type.FIGHTING).setMixedBattleBgm("battle_unova_elite"), + [TrainerType.GRIMSLEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["GRIMSLEY"], true, Type.DARK).setMixedBattleBgm("battle_unova_elite"), + [TrainerType.CAITLIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CAITLIN"], false, Type.PSYCHIC).setMixedBattleBgm("battle_unova_elite"), + [TrainerType.MALVA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MALVA"], false, Type.FIRE).setMixedBattleBgm("battle_kalos_elite"), + [TrainerType.SIEBOLD]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SIEBOLD"], true, Type.WATER).setMixedBattleBgm("battle_kalos_elite"), + [TrainerType.WIKSTROM]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["WIKSTROM"], true, Type.STEEL).setMixedBattleBgm("battle_kalos_elite"), + [TrainerType.DRASNA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRASNA"], false, Type.DRAGON).setMixedBattleBgm("battle_kalos_elite"), + [TrainerType.HALA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HALA"], true, Type.FIGHTING).setMixedBattleBgm("battle_alola_elite"), + [TrainerType.MOLAYNE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MOLAYNE"], true, Type.STEEL).setMixedBattleBgm("battle_alola_elite"), + [TrainerType.OLIVIA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["OLIVIA"], false, Type.ROCK).setMixedBattleBgm("battle_alola_elite"), + [TrainerType.ACEROLA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["ACEROLA"], false, Type.GHOST).setMixedBattleBgm("battle_alola_elite"), + [TrainerType.KAHILI]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KAHILI"], false, Type.FLYING).setMixedBattleBgm("battle_alola_elite"), + [TrainerType.MARNIE_ELITE]: new TrainerConfig(++t).setName("Marnie").initForEliteFour(signatureSpecies["MARNIE_ELITE"], false, Type.DARK).setMixedBattleBgm("battle_galar_elite"), + [TrainerType.NESSA_ELITE]: new TrainerConfig(++t).setName("Nessa").initForEliteFour(signatureSpecies["NESSA_ELITE"], false, Type.WATER).setMixedBattleBgm("battle_galar_elite"), + [TrainerType.BEA_ELITE]: new TrainerConfig(++t).setName("Bea").initForEliteFour(signatureSpecies["BEA_ELITE"], false, Type.FIGHTING).setMixedBattleBgm("battle_galar_elite"), + [TrainerType.ALLISTER_ELITE]: new TrainerConfig(++t).setName("Allister").initForEliteFour(signatureSpecies["ALLISTER_ELITE"], true, Type.GHOST).setMixedBattleBgm("battle_galar_elite"), + [TrainerType.RAIHAN_ELITE]: new TrainerConfig(++t).setName("Raihan").initForEliteFour(signatureSpecies["RAIHAN_ELITE"], true, Type.DRAGON).setMixedBattleBgm("battle_galar_elite"), + [TrainerType.RIKA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["RIKA"], false, Type.GROUND).setMixedBattleBgm("battle_paldea_elite"), + [TrainerType.POPPY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["POPPY"], false, Type.STEEL).setMixedBattleBgm("battle_paldea_elite"), + [TrainerType.LARRY_ELITE]: new TrainerConfig(++t).setName("Larry").initForEliteFour(signatureSpecies["LARRY_ELITE"], true, Type.NORMAL, Type.FLYING).setMixedBattleBgm("battle_paldea_elite"), + [TrainerType.HASSEL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HASSEL"], true, Type.DRAGON).setMixedBattleBgm("battle_paldea_elite"), + [TrainerType.CRISPIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CRISPIN"], true, Type.FIRE).setMixedBattleBgm("battle_bb_elite"), + [TrainerType.AMARYS]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AMARYS"], false, Type.STEEL).setMixedBattleBgm("battle_bb_elite"), + [TrainerType.LACEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LACEY"], false, Type.FAIRY).setMixedBattleBgm("battle_bb_elite"), + [TrainerType.DRAYTON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAYTON"], true, Type.DRAGON).setMixedBattleBgm("battle_bb_elite"), - [TrainerType.BLUE]: new TrainerConfig((t = TrainerType.BLUE)).initForChampion(signatureSpecies["BLUE"],true).setBattleBgm("battle_kanto_champion").setMixedBattleBgm("battle_kanto_champion").setHasDouble("blue_red_double").setDoubleTrainerType(TrainerType.RED).setDoubleTitle("champion_double") + [TrainerType.BLUE]: new TrainerConfig((t = TrainerType.BLUE)).initForChampion(signatureSpecies["BLUE"], true).setBattleBgm("battle_kanto_champion").setMixedBattleBgm("battle_kanto_champion").setHasDouble("blue_red_double").setDoubleTrainerType(TrainerType.RED).setDoubleTitle("champion_double") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.ALAKAZAM], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })) @@ -1384,7 +1480,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); p.generateName(); })), - [TrainerType.RED]: new TrainerConfig(++t).initForChampion(signatureSpecies["RED"],true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion").setHasDouble("red_blue_double").setDoubleTrainerType(TrainerType.BLUE).setDoubleTitle("champion_double") + [TrainerType.RED]: new TrainerConfig(++t).initForChampion(signatureSpecies["RED"], true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion").setHasDouble("red_blue_double").setDoubleTrainerType(TrainerType.BLUE).setDoubleTitle("champion_double") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.PIKACHU], TrainerSlot.TRAINER, true, p => { p.formIndex = 8; p.generateAndPopulateMoveset(); @@ -1395,7 +1491,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); p.generateName(); })), - [TrainerType.LANCE_CHAMPION]: new TrainerConfig(++t).setName("Lance").initForChampion(signatureSpecies["LANCE_CHAMPION"],true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion") + [TrainerType.LANCE_CHAMPION]: new TrainerConfig(++t).setName("Lance").initForChampion(signatureSpecies["LANCE_CHAMPION"], true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.AERODACTYL], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })) @@ -1404,7 +1500,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); p.generateName(); })), - [TrainerType.STEVEN]: new TrainerConfig(++t).initForChampion(signatureSpecies["STEVEN"],true).setBattleBgm("battle_hoenn_champion").setMixedBattleBgm("battle_hoenn_champion").setHasDouble("steven_wallace_double").setDoubleTrainerType(TrainerType.WALLACE).setDoubleTitle("champion_double") + [TrainerType.STEVEN]: new TrainerConfig(++t).initForChampion(signatureSpecies["STEVEN"], true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("steven_wallace_double").setDoubleTrainerType(TrainerType.WALLACE).setDoubleTitle("champion_double") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.SKARMORY], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })) @@ -1413,7 +1509,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); p.generateName(); })), - [TrainerType.WALLACE]: new TrainerConfig(++t).initForChampion(signatureSpecies["WALLACE"],true).setBattleBgm("battle_hoenn_champion").setMixedBattleBgm("battle_hoenn_champion").setHasDouble("wallace_steven_double").setDoubleTrainerType(TrainerType.STEVEN).setDoubleTitle("champion_double") + [TrainerType.WALLACE]: new TrainerConfig(++t).initForChampion(signatureSpecies["WALLACE"], true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("wallace_steven_double").setDoubleTrainerType(TrainerType.STEVEN).setDoubleTitle("champion_double") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.PELIPPER], TrainerSlot.TRAINER, true, p => { p.abilityIndex = 1; // Drizzle p.generateAndPopulateMoveset(); @@ -1422,7 +1518,7 @@ export const trainerConfigs: TrainerConfigs = { p.formIndex = 1; p.generateAndPopulateMoveset(); })), - [TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(signatureSpecies["CYNTHIA"],false).setBattleBgm("battle_sinnoh_champion").setMixedBattleBgm("battle_sinnoh_champion") + [TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(signatureSpecies["CYNTHIA"], false).setBattleBgm("battle_sinnoh_champion").setMixedBattleBgm("battle_sinnoh_champion") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.SPIRITOMB], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })) @@ -1431,11 +1527,11 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); p.generateName(); })), - [TrainerType.ALDER]: new TrainerConfig(++t).initForChampion(signatureSpecies["ALDER"],true).setHasDouble("alder_iris_double").setDoubleTrainerType(TrainerType.IRIS).setDoubleTitle("champion_double").setBattleBgm("battle_champion_alder").setMixedBattleBgm("battle_champion_alder") + [TrainerType.ALDER]: new TrainerConfig(++t).initForChampion(signatureSpecies["ALDER"], true).setHasDouble("alder_iris_double").setDoubleTrainerType(TrainerType.IRIS).setDoubleTitle("champion_double").setBattleBgm("battle_champion_alder").setMixedBattleBgm("battle_champion_alder") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.BOUFFALANT, Species.BRAVIARY], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })), - [TrainerType.IRIS]: new TrainerConfig(++t).initForChampion(signatureSpecies["IRIS"],false).setBattleBgm("battle_champion_iris").setMixedBattleBgm("battle_champion_iris").setHasDouble("iris_alder_double").setDoubleTrainerType(TrainerType.ALDER).setDoubleTitle("champion_double") + [TrainerType.IRIS]: new TrainerConfig(++t).initForChampion(signatureSpecies["IRIS"], false).setBattleBgm("battle_champion_iris").setMixedBattleBgm("battle_champion_iris").setHasDouble("iris_alder_double").setDoubleTrainerType(TrainerType.ALDER).setDoubleTitle("champion_double") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.DRUDDIGON], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })) @@ -1444,7 +1540,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); p.generateName(); })), - [TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(signatureSpecies["DIANTHA"],false).setMixedBattleBgm("battle_kalos_champion") + [TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(signatureSpecies["DIANTHA"], false).setMixedBattleBgm("battle_kalos_champion") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.GOURGEIST], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })) @@ -1453,11 +1549,11 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); p.generateName(); })), - [TrainerType.HAU]: new TrainerConfig(++t).initForChampion(signatureSpecies["HAU"],true).setMixedBattleBgm("battle_alola_champion") + [TrainerType.HAU]: new TrainerConfig(++t).initForChampion(signatureSpecies["HAU"], true).setMixedBattleBgm("battle_alola_champion") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.ALOLA_RAICHU], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })), - [TrainerType.LEON]: new TrainerConfig(++t).initForChampion(signatureSpecies["LEON"],true).setMixedBattleBgm("battle_galar_champion") + [TrainerType.LEON]: new TrainerConfig(++t).initForChampion(signatureSpecies["LEON"], true).setMixedBattleBgm("battle_galar_champion") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.RILLABOOM, Species.CINDERACE, Species.INTELEON], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })) @@ -1466,16 +1562,16 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); p.generateName(); })), - [TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(signatureSpecies["GEETA"],false).setMixedBattleBgm("battle_champion_geeta") + [TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(signatureSpecies["GEETA"], false).setMixedBattleBgm("battle_champion_geeta") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.GLIMMORA], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })), - [TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(signatureSpecies["NEMONA"],false).setMixedBattleBgm("battle_champion_nemona") + [TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(signatureSpecies["NEMONA"], false).setMixedBattleBgm("battle_champion_nemona") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.LYCANROC], TrainerSlot.TRAINER, true, p => { p.formIndex = 0; // Midday form p.generateAndPopulateMoveset(); })), - [TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(signatureSpecies["KIERAN"],true).setMixedBattleBgm("battle_champion_kieran") + [TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(signatureSpecies["KIERAN"], true).setMixedBattleBgm("battle_champion_kieran") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.POLIWRATH, Species.POLITOED], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); })), @@ -1501,7 +1597,7 @@ export const trainerConfigs: TrainerConfigs = { .setSpeciesFilter(species => species.baseTotal >= 540) .setGenModifiersFunc(party => { const starter = party[0]; - return [modifierTypes.TERA_SHARD().generateType(null, [starter.species.type1]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; + return [modifierTypes.TERA_SHARD().generateType([], [starter.species.type1])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; // TODO: is the bang correct? }), [TrainerType.RIVAL_5]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_5) .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL], TrainerSlot.TRAINER, true, @@ -1517,7 +1613,7 @@ export const trainerConfigs: TrainerConfigs = { })) .setGenModifiersFunc(party => { const starter = party[0]; - return [modifierTypes.TERA_SHARD().generateType(null, [starter.species.type1]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; + return [modifierTypes.TERA_SHARD().generateType([], [starter.species.type1])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; //TODO: is the bang correct? }), [TrainerType.RIVAL_6]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(3).setEncounterBgm("final").setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_6) .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL], TrainerSlot.TRAINER, true, @@ -1543,16 +1639,16 @@ export const trainerConfigs: TrainerConfigs = { })) .setGenModifiersFunc(party => { const starter = party[0]; - return [modifierTypes.TERA_SHARD().generateType(null, [starter.species.type1]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; + return [modifierTypes.TERA_SHARD().generateType([], [starter.species.type1])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; // TODO: is the bang correct? }), - [TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss",[]).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PERSIAN , Species.ALOLA_PERSIAN])) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.NIDOKING , Species.NIDOQUEEN ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.RHYPERIOR ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.DUGTRIO, Species.ALOLA_DUGTRIO ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.MAROWAK , Species.ALOLA_MAROWAK])) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KANGASKHAN ], TrainerSlot.TRAINER, true, p => { + [TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", []).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.PERSIAN, Species.ALOLA_PERSIAN])) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.NIDOKING, Species.NIDOQUEEN])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.RHYPERIOR])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.DUGTRIO, Species.ALOLA_DUGTRIO])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.MAROWAK, Species.ALOLA_MAROWAK])) + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.KANGASKHAN], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -1560,208 +1656,208 @@ export const trainerConfigs: TrainerConfigs = { p.generateName(); })), [TrainerType.ROCKET_BOSS_GIOVANNI_2]: new TrainerConfig(++t).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", [], true).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.TYRANITAR , Species.IRON_THORNS], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.TYRANITAR, Species.IRON_THORNS], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HIPPOWDON ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.EXCADRILL ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.KANGASKHAN ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.HIPPOWDON])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.EXCADRILL])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.KANGASKHAN], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GASTRODON])) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.MEWTWO ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.GASTRODON])) + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.MEWTWO], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.MASTER_BALL; })), - [TrainerType.MAXIE]: new TrainerConfig(++t).setName("Maxie").initForEvilTeamLeader("Magma Boss",[]).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.MIGHTYENA ])) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.CROBAT, Species.GLISCOR ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.WEEZING, Species.GALAR_WEEZING ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.MAGMORTAR, Species.TORKOAL ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.FLYGON])) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.CAMERUPT ], TrainerSlot.TRAINER, true, p => { + [TrainerType.MAXIE]: new TrainerConfig(++t).setName("Maxie").initForEvilTeamLeader("Magma Boss", []).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.MIGHTYENA])) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.CROBAT, Species.GLISCOR])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.WEEZING, Species.GALAR_WEEZING])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.MAGMORTAR, Species.TORKOAL])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.FLYGON])) + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.CAMERUPT], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })), - [TrainerType.MAXIE_2]: new TrainerConfig(++t).setName("Maxie").initForEvilTeamLeader("Magma Boss",[], true).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SOLROCK, Species.TYPHLOSION ], TrainerSlot.TRAINER, true, p => { + [TrainerType.MAXIE_2]: new TrainerConfig(++t).setName("Maxie").initForEvilTeamLeader("Magma Boss", [], true).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.SOLROCK, Species.TYPHLOSION], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.TORKOAL, Species.NINETALES ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.TORKOAL, Species.NINETALES], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.abilityIndex = 2; // DROUGHT })) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SHIFTRY, Species.SCOVILLAIN ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.SHIFTRY, Species.SCOVILLAIN], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.abilityIndex = 0; // Chlorophyll })) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GREAT_TUSK ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.CAMERUPT ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.GREAT_TUSK])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.CAMERUPT], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.GROUDON ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.GROUDON], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.MASTER_BALL; })), - [TrainerType.ARCHIE]: new TrainerConfig(++t).setName("Archie").initForEvilTeamLeader("Aqua Boss",[]).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LINOONE ])) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.CROBAT, Species.PELIPPER ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.MUK, Species.ALOLA_MUK ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TENTACRUEL ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.RELICANTH, Species.WAILORD ])) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.SHARPEDO ], TrainerSlot.TRAINER, true, p => { + [TrainerType.ARCHIE]: new TrainerConfig(++t).setName("Archie").initForEvilTeamLeader("Aqua Boss", []).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.LINOONE])) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.CROBAT, Species.PELIPPER])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.MUK, Species.ALOLA_MUK])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.TENTACRUEL])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.RELICANTH, Species.WAILORD])) + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.SHARPEDO], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })), - [TrainerType.ARCHIE_2]: new TrainerConfig(++t).setName("Archie").initForEvilTeamLeader("Aqua Boss",[], true).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.KINGDRA, Species.LUDICOLO ], TrainerSlot.TRAINER, true, p => { + [TrainerType.ARCHIE_2]: new TrainerConfig(++t).setName("Archie").initForEvilTeamLeader("Aqua Boss", [], true).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.KINGDRA, Species.LUDICOLO], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.POLITOED, Species.PELIPPER ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.POLITOED, Species.PELIPPER], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.abilityIndex = 2; // Drizzle })) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.BEARTIC, Species.ARMALDO ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.BEARTIC, Species.ARMALDO], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.abilityIndex = 2; // Swift Swim })) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.HUNTAIL, Species.GOREBYSS ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.HUNTAIL, Species.GOREBYSS], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.abilityIndex = 0; // Swift Swim })) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.SHARPEDO ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.SHARPEDO], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KYOGRE ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.KYOGRE], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.MASTER_BALL; })), - [TrainerType.CYRUS]: new TrainerConfig(++t).setName("Cyrus").initForEvilTeamLeader("Galactic Boss",[]).setMixedBattleBgm("battle_galactic_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GYARADOS, Species.BASCULEGION ])) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HONCHKROW, Species.HISUI_BRAVIARY ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.CROBAT, Species.OVERQWIL ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.AZELF, Species.UXIE, Species.MESPRIT ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.HOUNDOOM ], TrainerSlot.TRAINER, true, p => { + [TrainerType.CYRUS]: new TrainerConfig(++t).setName("Cyrus").initForEvilTeamLeader("Galactic Boss", []).setMixedBattleBgm("battle_galactic_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.GYARADOS, Species.BASCULEGION])) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.HONCHKROW, Species.HISUI_BRAVIARY])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.CROBAT, Species.OVERQWIL])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.AZELF, Species.UXIE, Species.MESPRIT])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.HOUNDOOM], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.WEAVILE ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.WEAVILE], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })), - [TrainerType.CYRUS_2]: new TrainerConfig(++t).setName("Cyrus").initForEvilTeamLeader("Galactic Boss",[], true).setMixedBattleBgm("battle_galactic_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.AZELF, Species.UXIE, Species.MESPRIT ], TrainerSlot.TRAINER, true, p => { + [TrainerType.CYRUS_2]: new TrainerConfig(++t).setName("Cyrus").initForEvilTeamLeader("Galactic Boss", [], true).setMixedBattleBgm("battle_galactic_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.AZELF, Species.UXIE, Species.MESPRIT], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); })) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ELECTRODE, Species.HISUI_ELECTRODE ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SALAMENCE, Species.ROARING_MOON ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.HOUNDOOM ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.ELECTRODE, Species.HISUI_ELECTRODE])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.SALAMENCE, Species.ROARING_MOON])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.HOUNDOOM], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.WEAVILE ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.WEAVILE], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.DARKRAI ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.DARKRAI], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.MASTER_BALL; })), - [TrainerType.GHETSIS]: new TrainerConfig(++t).setName("Ghetsis").initForEvilTeamLeader("Plasma Boss",[]).setMixedBattleBgm("battle_plasma_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.COFAGRIGUS, Species.RUNERIGUS ])) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BOUFFALANT ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SEISMITOAD, Species.CARRACOSTA ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.EELEKTROSS, Species.GALVANTULA ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.VOLCARONA ])) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.HYDREIGON ], TrainerSlot.TRAINER, true, p => { + [TrainerType.GHETSIS]: new TrainerConfig(++t).setName("Ghetsis").initForEvilTeamLeader("Plasma Boss", []).setMixedBattleBgm("battle_plasma_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.COFAGRIGUS, Species.RUNERIGUS])) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.BOUFFALANT])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.SEISMITOAD, Species.CARRACOSTA])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.EELEKTROSS, Species.GALVANTULA])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.VOLCARONA])) + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.HYDREIGON], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })), - [TrainerType.GHETSIS_2]: new TrainerConfig(++t).setName("Ghetsis").initForEvilTeamLeader("Plasma Boss",[], true).setMixedBattleBgm("battle_plasma_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SLITHER_WING, Species.IRON_MOTH ], TrainerSlot.TRAINER, true, p => { + [TrainerType.GHETSIS_2]: new TrainerConfig(++t).setName("Ghetsis").initForEvilTeamLeader("Plasma Boss", [], true).setMixedBattleBgm("battle_plasma_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.SLITHER_WING, Species.IRON_MOTH], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.DURANT ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.DARMANITAN, Species.GALAR_DARMANITAN ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.KINGAMBIT ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.HYDREIGON, Species.IRON_JUGULIS ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.DURANT])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.DARMANITAN, Species.GALAR_DARMANITAN])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.KINGAMBIT])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.HYDREIGON, Species.IRON_JUGULIS], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KYUREM ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.KYUREM], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.MASTER_BALL; })), - [TrainerType.LYSANDRE]: new TrainerConfig(++t).setName("Lysandre").initForEvilTeamLeader("Flare Boss",[]).setMixedBattleBgm("battle_flare_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.MIENSHAO ])) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HONCHKROW, Species.TALONFLAME ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.PYROAR ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.MILOTIC ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.HELIOLISK ])) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.GYARADOS ], TrainerSlot.TRAINER, true, p => { + [TrainerType.LYSANDRE]: new TrainerConfig(++t).setName("Lysandre").initForEvilTeamLeader("Flare Boss", []).setMixedBattleBgm("battle_flare_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.MIENSHAO])) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.HONCHKROW, Species.TALONFLAME])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.PYROAR])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.MILOTIC])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.HELIOLISK])) + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.GYARADOS], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })), - [TrainerType.LYSANDRE_2]: new TrainerConfig(++t).setName("Lysandre").initForEvilTeamLeader("Flare Boss",[], true).setMixedBattleBgm("battle_flare_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SCREAM_TAIL, Species.FLUTTER_MANE ], TrainerSlot.TRAINER, true, p => { + [TrainerType.LYSANDRE_2]: new TrainerConfig(++t).setName("Lysandre").initForEvilTeamLeader("Flare Boss", [], true).setMixedBattleBgm("battle_flare_boss").setVictoryBgm("victory_team_plasma") + .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.SCREAM_TAIL, Species.FLUTTER_MANE], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.GHOLDENGO, Species.AEGISLASH ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.PYROAR ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GOODRA, Species.HISUI_GOODRA ])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GYARADOS ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.GHOLDENGO, Species.AEGISLASH])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.PYROAR])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.GOODRA, Species.HISUI_GOODRA])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.GYARADOS], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; p.generateName(); })) - .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.YVELTAL ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.YVELTAL], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.MASTER_BALL; diff --git a/src/data/trainer-names.ts b/src/data/trainer-names.ts index b986378ee47..447f9fd4284 100644 --- a/src/data/trainer-names.ts +++ b/src/data/trainer-names.ts @@ -3,7 +3,7 @@ import * as Utils from "../utils"; class TrainerNameConfig { public urls: string[]; - public femaleUrls: string[]; + public femaleUrls: string[] | null; constructor(type: TrainerType, ...urls: string[]) { this.urls = urls.length ? urls : [ Utils.toReadableString(TrainerType[type]).replace(/ /g, "_") ]; @@ -136,8 +136,11 @@ function fetchAndPopulateTrainerNames(url: string, parser: DOMParser, trainerNam .then(html => { console.log(url); const htmlDoc = parser.parseFromString(html, "text/html"); - const trainerListHeader = htmlDoc.querySelector("#Trainer_list").parentElement; - const elements = [...trainerListHeader.parentElement.childNodes]; + const trainerListHeader = htmlDoc.querySelector("#Trainer_list")?.parentElement; + if (!trainerListHeader) { + return []; + } + const elements = [...(trainerListHeader?.parentElement?.childNodes ?? [])]; const startChildIndex = elements.indexOf(trainerListHeader); const endChildIndex = elements.findIndex(h => h.nodeName === "H2" && elements.indexOf(h) > startChildIndex); const tables = elements.filter(t => { @@ -152,6 +155,9 @@ function fetchAndPopulateTrainerNames(url: string, parser: DOMParser, trainerNam const trainerRows = [...table.querySelectorAll("tr:not(:first-child)")].filter(r => r.children.length === 9); for (const row of trainerRows) { const nameCell = row.firstElementChild; + if (!nameCell) { + continue; + } const content = nameCell.innerHTML; if (content.indexOf(" -1) { const female = /♀/.test(content); diff --git a/src/data/type.ts b/src/data/type.ts index 1330eb83f4b..7a9f7f3605e 100644 --- a/src/data/type.ts +++ b/src/data/type.ts @@ -499,6 +499,8 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer): case Type.STELLAR: return 1; } + + return 0; } /** diff --git a/src/data/weather.ts b/src/data/weather.ts index 901ad08d164..2421f719e6e 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -103,7 +103,7 @@ export class Weather { const field = scene.getField(true); for (const pokemon of field) { - let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0]; + let suppressWeatherEffectAbAttr: SuppressWeatherEffectAbAttr | null = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0]; if (!suppressWeatherEffectAbAttr) { suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr)[0] : null; } @@ -116,7 +116,7 @@ export class Weather { } } -export function getWeatherStartMessage(weatherType: WeatherType): string { +export function getWeatherStartMessage(weatherType: WeatherType): string | null { switch (weatherType) { case WeatherType.SUNNY: return i18next.t("weather:sunnyStartMessage"); @@ -141,7 +141,7 @@ export function getWeatherStartMessage(weatherType: WeatherType): string { return null; } -export function getWeatherLapseMessage(weatherType: WeatherType): string { +export function getWeatherLapseMessage(weatherType: WeatherType): string | null { switch (weatherType) { case WeatherType.SUNNY: return i18next.t("weather:sunnyLapseMessage"); @@ -166,7 +166,7 @@ export function getWeatherLapseMessage(weatherType: WeatherType): string { return null; } -export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon): string { +export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon): string | null { switch (weatherType) { case WeatherType.SANDSTORM: return i18next.t("weather:sandstormDamageMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)}); @@ -177,7 +177,7 @@ export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokem return null; } -export function getWeatherClearMessage(weatherType: WeatherType): string { +export function getWeatherClearMessage(weatherType: WeatherType): string | null { switch (weatherType) { case WeatherType.SUNNY: return i18next.t("weather:sunnyClearMessage"); @@ -202,7 +202,7 @@ export function getWeatherClearMessage(weatherType: WeatherType): string { return null; } -export function getTerrainStartMessage(terrainType: TerrainType): string { +export function getTerrainStartMessage(terrainType: TerrainType): string | null { switch (terrainType) { case TerrainType.MISTY: return i18next.t("terrain:mistyStartMessage"); @@ -212,10 +212,13 @@ export function getTerrainStartMessage(terrainType: TerrainType): string { return i18next.t("terrain:grassyStartMessage"); case TerrainType.PSYCHIC: return i18next.t("terrain:psychicStartMessage"); + default: + console.warn("getTerrainStartMessage not defined. Using default null"); + return null; } } -export function getTerrainClearMessage(terrainType: TerrainType): string { +export function getTerrainClearMessage(terrainType: TerrainType): string | null { switch (terrainType) { case TerrainType.MISTY: return i18next.t("terrain:mistyClearMessage"); @@ -225,6 +228,9 @@ export function getTerrainClearMessage(terrainType: TerrainType): string { return i18next.t("terrain:grassyClearMessage"); case TerrainType.PSYCHIC: return i18next.t("terrain:psychicClearMessage"); + default: + console.warn("getTerrainClearMessage not defined. Using default null"); + return null; } } diff --git a/src/egg-hatch-phase.ts b/src/egg-hatch-phase.ts index 3965eae74f6..73c88cbde37 100644 --- a/src/egg-hatch-phase.ts +++ b/src/egg-hatch-phase.ts @@ -84,7 +84,7 @@ export class EggHatchPhase extends Phase { this.scene.gameData.eggs.splice(eggIndex, 1); - this.scene.fadeOutBgm(null, false); + this.scene.fadeOutBgm(undefined, false); this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler; @@ -234,8 +234,8 @@ export class EggHatchPhase extends Phase { ease: "Sine.easeInOut", duration: 250, onComplete: () => { - count++; - if (count < repeatCount) { + count!++; + if (count! < repeatCount!) { // we know they are defined return this.doEggShake(intensity, repeatCount, count).then(() => resolve()); } this.scene.tweens.add({ @@ -347,7 +347,7 @@ export class EggHatchPhase extends Phase { this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => { this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.end(); }); }); @@ -447,6 +447,6 @@ export class EggHatchPhase extends Phase { }, this.egg.id, EGG_SEED.toString()); - return ret; + return ret!; } } diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts index 722096c42cd..1265b815bf4 100644 --- a/src/enums/arena-tag-type.ts +++ b/src/enums/arena-tag-type.ts @@ -21,5 +21,6 @@ export enum ArenaTagType { MAT_BLOCK = "MAT_BLOCK", CRAFTY_SHIELD = "CRAFTY_SHIELD", TAILWIND = "TAILWIND", - HAPPY_HOUR = "HAPPY_HOUR" + HAPPY_HOUR = "HAPPY_HOUR", + NO_CRIT = "NO_CRIT" } diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index 52f6402861e..405e8cc4822 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -48,7 +48,6 @@ export enum BattlerTagType { FIRE_BOOST = "FIRE_BOOST", CRIT_BOOST = "CRIT_BOOST", ALWAYS_CRIT = "ALWAYS_CRIT", - NO_CRIT = "NO_CRIT", IGNORE_ACCURACY = "IGNORE_ACCURACY", BYPASS_SLEEP = "BYPASS_SLEEP", IGNORE_FLYING = "IGNORE_FLYING", @@ -63,5 +62,9 @@ export enum BattlerTagType { ICE_FACE = "ICE_FACE", STOCKPILING = "STOCKPILING", RECEIVE_DOUBLE_DAMAGE = "RECEIVE_DOUBLE_DAMAGE", - ALWAYS_GET_HIT = "ALWAYS_GET_HIT" + ALWAYS_GET_HIT = "ALWAYS_GET_HIT", + IGNORE_GHOST = "IGNORE_GHOST", + IGNORE_DARK = "IGNORE_DARK", + GULP_MISSILE_ARROKUDA = "GULP_MISSILE_ARROKUDA", + GULP_MISSILE_PIKACHU = "GULP_MISSILE_PIKACHU" } diff --git a/src/enums/trainer-type.ts b/src/enums/trainer-type.ts index db0bf3a8d64..1d4d9579ee3 100644 --- a/src/enums/trainer-type.ts +++ b/src/enums/trainer-type.ts @@ -1,215 +1,223 @@ - export enum TrainerType { - UNKNOWN, + UNKNOWN, - ACE_TRAINER, - ARTIST, - BACKERS, - BACKPACKER, - BAKER, - BEAUTY, - BIKER, - BLACK_BELT, - BREEDER, - CLERK, - CYCLIST, - DANCER, - DEPOT_AGENT, - DOCTOR, - FIREBREATHER, - FISHERMAN, - GUITARIST, - HARLEQUIN, - HIKER, - HOOLIGANS, - HOOPSTER, - INFIELDER, - JANITOR, - LINEBACKER, - MAID, - MUSICIAN, - HEX_MANIAC, - NURSERY_AIDE, - OFFICER, - PARASOL_LADY, - PILOT, - POKEFAN, - PRESCHOOLER, - PSYCHIC, - RANGER, - RICH, - RICH_KID, - ROUGHNECK, - SAILOR, - SCIENTIST, - SMASHER, - SNOW_WORKER, - STRIKER, - SCHOOL_KID, - SWIMMER, - TWINS, - VETERAN, - WAITER, - WORKER, - YOUNGSTER, - ROCKET_GRUNT, - ROCKET_ADMIN, - MAGMA_GRUNT, - MAGMA_ADMIN, - AQUA_GRUNT, - AQUA_ADMIN, - GALACTIC_GRUNT, - GALACTIC_ADMIN, - PLASMA_GRUNT, - PLASMA_SAGE, - FLARE_GRUNT, - FLARE_ADMIN, - ROCKET_BOSS_GIOVANNI_1, - ROCKET_BOSS_GIOVANNI_2, - MAXIE, - MAXIE_2, - ARCHIE, - ARCHIE_2, - CYRUS, - CYRUS_2, - GHETSIS, - GHETSIS_2, - LYSANDRE, - LYSANDRE_2, + ACE_TRAINER, + ARTIST, + BACKERS, + BACKPACKER, + BAKER, + BEAUTY, + BIKER, + BLACK_BELT, + BREEDER, + CLERK, + CYCLIST, + DANCER, + DEPOT_AGENT, + DOCTOR, + FIREBREATHER, + FISHERMAN, + GUITARIST, + HARLEQUIN, + HIKER, + HOOLIGANS, + HOOPSTER, + INFIELDER, + JANITOR, + LINEBACKER, + MAID, + MUSICIAN, + HEX_MANIAC, + NURSERY_AIDE, + OFFICER, + PARASOL_LADY, + PILOT, + POKEFAN, + PRESCHOOLER, + PSYCHIC, + RANGER, + RICH, + RICH_KID, + ROUGHNECK, + SAILOR, + SCIENTIST, + SMASHER, + SNOW_WORKER, + STRIKER, + SCHOOL_KID, + SWIMMER, + TWINS, + VETERAN, + WAITER, + WORKER, + YOUNGSTER, + ROCKET_GRUNT, + ARCHER, + ARIANA, + PROTON, + PETREL, + MAGMA_GRUNT, + TABITHA, + COURTNEY, + AQUA_GRUNT, + MATT, + SHELLY, + GALACTIC_GRUNT, + JUPITER, + MARS, + SATURN, + PLASMA_GRUNT, + ZINZOLIN, + ROOD, + FLARE_GRUNT, + BRYONY, + XEROSIC, + ROCKET_BOSS_GIOVANNI_1, + ROCKET_BOSS_GIOVANNI_2, + MAXIE, + MAXIE_2, + ARCHIE, + ARCHIE_2, + CYRUS, + CYRUS_2, + GHETSIS, + GHETSIS_2, + LYSANDRE, + LYSANDRE_2, - BROCK = 200, - MISTY, - LT_SURGE, - ERIKA, - JANINE, - SABRINA, - BLAINE, - GIOVANNI, - FALKNER, - BUGSY, - WHITNEY, - MORTY, - CHUCK, - JASMINE, - PRYCE, - CLAIR, - ROXANNE, - BRAWLY, - WATTSON, - FLANNERY, - NORMAN, - WINONA, - TATE, - LIZA, - JUAN, - ROARK, - GARDENIA, - MAYLENE, - CRASHER_WAKE, - FANTINA, - BYRON, - CANDICE, - VOLKNER, - CILAN, - CHILI, - CRESS, - CHEREN, - LENORA, - ROXIE, - BURGH, - ELESA, - CLAY, - SKYLA, - BRYCEN, - DRAYDEN, - MARLON, - VIOLA, - GRANT, - KORRINA, - RAMOS, - CLEMONT, - VALERIE, - OLYMPIA, - WULFRIC, - MILO, - NESSA, - KABU, - BEA, - ALLISTER, - OPAL, - BEDE, - GORDIE, - MELONY, - PIERS, - MARNIE, - RAIHAN, - KATY, - BRASSIUS, - IONO, - KOFU, - LARRY, - RYME, - TULIP, - GRUSHA, - LORELEI = 300, - BRUNO, - AGATHA, - LANCE, - WILL, - KOGA, - KAREN, - SIDNEY, - PHOEBE, - GLACIA, - DRAKE, - AARON, - BERTHA, - FLINT, - LUCIAN, - SHAUNTAL, - MARSHAL, - GRIMSLEY, - CAITLIN, - MALVA, - SIEBOLD, - WIKSTROM, - DRASNA, - HALA, - MOLAYNE, - OLIVIA, - ACEROLA, - KAHILI, - MARNIE_ELITE, - NESSA_ELITE, - BEA_ELITE, - ALLISTER_ELITE, - RAIHAN_ELITE, - RIKA, - POPPY, - LARRY_ELITE, - HASSEL, - CRISPIN, - AMARYS, - LACEY, - DRAYTON, - BLUE = 350, - RED, - LANCE_CHAMPION, - STEVEN, - WALLACE, - CYNTHIA, - ALDER, - IRIS, - DIANTHA, - HAU, - LEON, - GEETA, - NEMONA, - KIERAN, - RIVAL = 375, - RIVAL_2, - RIVAL_3, - RIVAL_4, - RIVAL_5, - RIVAL_6 + BROCK = 200, + MISTY, + LT_SURGE, + ERIKA, + JANINE, + SABRINA, + BLAINE, + GIOVANNI, + FALKNER, + BUGSY, + WHITNEY, + MORTY, + CHUCK, + JASMINE, + PRYCE, + CLAIR, + ROXANNE, + BRAWLY, + WATTSON, + FLANNERY, + NORMAN, + WINONA, + TATE, + LIZA, + JUAN, + ROARK, + GARDENIA, + MAYLENE, + CRASHER_WAKE, + FANTINA, + BYRON, + CANDICE, + VOLKNER, + CILAN, + CHILI, + CRESS, + CHEREN, + LENORA, + ROXIE, + BURGH, + ELESA, + CLAY, + SKYLA, + BRYCEN, + DRAYDEN, + MARLON, + VIOLA, + GRANT, + KORRINA, + RAMOS, + CLEMONT, + VALERIE, + OLYMPIA, + WULFRIC, + MILO, + NESSA, + KABU, + BEA, + ALLISTER, + OPAL, + BEDE, + GORDIE, + MELONY, + PIERS, + MARNIE, + RAIHAN, + KATY, + BRASSIUS, + IONO, + KOFU, + LARRY, + RYME, + TULIP, + GRUSHA, + LORELEI = 300, + BRUNO, + AGATHA, + LANCE, + WILL, + KOGA, + KAREN, + SIDNEY, + PHOEBE, + GLACIA, + DRAKE, + AARON, + BERTHA, + FLINT, + LUCIAN, + SHAUNTAL, + MARSHAL, + GRIMSLEY, + CAITLIN, + MALVA, + SIEBOLD, + WIKSTROM, + DRASNA, + HALA, + MOLAYNE, + OLIVIA, + ACEROLA, + KAHILI, + MARNIE_ELITE, + NESSA_ELITE, + BEA_ELITE, + ALLISTER_ELITE, + RAIHAN_ELITE, + RIKA, + POPPY, + LARRY_ELITE, + HASSEL, + CRISPIN, + AMARYS, + LACEY, + DRAYTON, + BLUE = 350, + RED, + LANCE_CHAMPION, + STEVEN, + WALLACE, + CYNTHIA, + ALDER, + IRIS, + DIANTHA, + HAU, + LEON, + GEETA, + NEMONA, + KIERAN, + RIVAL = 375, + RIVAL_2, + RIVAL_3, + RIVAL_4, + RIVAL_5, + RIVAL_6 } diff --git a/src/events/arena.ts b/src/events/arena.ts index 67b423f3b75..9fbbe572601 100644 --- a/src/events/arena.ts +++ b/src/events/arena.ts @@ -81,8 +81,8 @@ export class TagAddedEvent extends ArenaEvent { this.arenaTagType = arenaTagType; this.arenaTagSide = arenaTagSide; - this.arenaTagLayers = arenaTagLayers; - this.arenaTagMaxLayers = arenaTagMaxLayers; + this.arenaTagLayers = arenaTagLayers!; // TODO: is this bang correct? + this.arenaTagMaxLayers = arenaTagMaxLayers!; // TODO: is this bang correct? } } /** diff --git a/src/evolution-phase.ts b/src/evolution-phase.ts index 7633fbb3fdd..7b50a6368f6 100644 --- a/src/evolution-phase.ts +++ b/src/evolution-phase.ts @@ -16,7 +16,7 @@ export class EvolutionPhase extends Phase { protected pokemon: PlayerPokemon; protected lastLevel: integer; - private evolution: SpeciesFormEvolution; + private evolution: SpeciesFormEvolution | null; protected evolutionContainer: Phaser.GameObjects.Container; protected evolutionBaseBg: Phaser.GameObjects.Image; @@ -28,7 +28,7 @@ export class EvolutionPhase extends Phase { protected pokemonEvoSprite: Phaser.GameObjects.Sprite; protected pokemonEvoTintSprite: Phaser.GameObjects.Sprite; - constructor(scene: BattleScene, pokemon: PlayerPokemon, evolution: SpeciesFormEvolution, lastLevel: integer) { + constructor(scene: BattleScene, pokemon: PlayerPokemon, evolution: SpeciesFormEvolution | null, lastLevel: integer) { super(scene); this.pokemon = pokemon; @@ -53,7 +53,7 @@ export class EvolutionPhase extends Phase { return this.end(); } - this.scene.fadeOutBgm(null, false); + this.scene.fadeOutBgm(undefined, false); const evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler; @@ -195,7 +195,7 @@ export class EvolutionPhase extends Phase { this.scene.ui.showText(i18next.t("menu:stoppedEvolving", { pokemonName: preName }), null, () => { this.scene.ui.showText(i18next.t("menu:pauseEvolutionsQuestion", { pokemonName: preName }), null, () => { const end = () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.playBgm(); evolvedPokemon.destroy(); this.end(); diff --git a/src/field/arena.ts b/src/field/arena.ts index cb045cc76ac..923a0a4e286 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -25,8 +25,8 @@ import { TrainerType } from "#enums/trainer-type"; export class Arena { public scene: BattleScene; public biomeType: Biome; - public weather: Weather; - public terrain: Terrain; + public weather: Weather | null; + public terrain: Terrain | null; public tags: ArenaTag[]; public bgm: string; public ignoreAbilities: boolean; @@ -121,7 +121,7 @@ export class Arena { } } - ret = getPokemonSpecies(species); + ret = getPokemonSpecies(species!); if (ret.subLegendary || ret.legendary || ret.mythical) { switch (true) { @@ -292,7 +292,7 @@ export class Arena { trySetWeatherOverride(weather: WeatherType): boolean { this.weather = new Weather(weather, 0); this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); - this.scene.queueMessage(getWeatherStartMessage(weather)); + this.scene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? return true; } @@ -314,13 +314,13 @@ export class Arena { const oldWeatherType = this.weather?.weatherType || WeatherType.NONE; this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null; - this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft)); + this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType!, this.weather?.turnsLeft!)); // TODO: is this bang correct? if (this.weather) { this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); - this.scene.queueMessage(getWeatherStartMessage(weather)); + this.scene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? } else { - this.scene.queueMessage(getWeatherClearMessage(oldWeatherType)); + this.scene.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct? } this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => { @@ -339,15 +339,15 @@ export class Arena { const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE; this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null; - this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType,this.terrain?.terrainType, this.terrain?.turnsLeft)); + this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType,this.terrain?.terrainType!, this.terrain?.turnsLeft!)); // TODO: are those bangs correct? if (this.terrain) { if (!ignoreAnim) { this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1))); } - this.scene.queueMessage(getTerrainStartMessage(terrain)); + this.scene.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct? } else { - this.scene.queueMessage(getTerrainClearMessage(oldTerrainType)); + this.scene.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct? } this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => { @@ -554,7 +554,7 @@ export class Arena { this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args); } - addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean { + addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean { const existingTag = this.getTagOnSide(tagType, side); if (existingTag) { existingTag.onOverlap(this); @@ -568,21 +568,23 @@ export class Arena { } const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side); - this.tags.push(newTag); - newTag.onAdd(this, quiet); + if (newTag) { + this.tags.push(newTag); + newTag.onAdd(this, quiet); - const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {}; + const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {}; - this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount, layers, maxLayers)); + this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount, layers, maxLayers)); + } return true; } - getTag(tagType: ArenaTagType | Constructor): ArenaTag { + getTag(tagType: ArenaTagType | Constructor): ArenaTag | undefined { return this.getTagOnSide(tagType, ArenaTagSide.BOTH); } - getTagOnSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide): ArenaTag { + getTagOnSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide): ArenaTag | undefined { return typeof(tagType) === "string" ? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)) : this.tags.find(t => t instanceof tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)); @@ -724,6 +726,9 @@ export class Arena { return 0.000; case Biome.SNOWY_FOREST: return 3.047; + default: + console.warn(`missing bgm loop-point for biome "${Biome[this.biomeType]}" (=${this.biomeType})`); + return 0; } } } @@ -777,12 +782,12 @@ export class ArenaBase extends Phaser.GameObjects.Container { this.player = player; - this.base = scene.addFieldSprite(0, 0, "plains_a", null, 1); + this.base = scene.addFieldSprite(0, 0, "plains_a", undefined, 1); this.base.setOrigin(0, 0); this.props = !player ? new Array(3).fill(null).map(() => { - const ret = scene.addFieldSprite(0, 0, "plains_b", null, 1); + const ret = scene.addFieldSprite(0, 0, "plains_b", undefined, 1); ret.setOrigin(0, 0); ret.setVisible(false); return ret; diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index 4af219a60b9..ae0692da342 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -3,6 +3,8 @@ import Pokemon, { DamageResult, HitResult } from "./pokemon"; import * as Utils from "../utils"; import { BattlerIndex } from "../battle"; +type TextAndShadowArr = [ string | null, string | null ]; + export default class DamageNumberHandler { private damageNumbers: Map; @@ -24,7 +26,7 @@ export default class DamageNumberHandler { damageNumber.setOrigin(0.5, 1); damageNumber.setScale(baseScale); - let [ textColor, shadowColor ] = [ null, null ]; + let [ textColor, shadowColor ] : TextAndShadowArr = [ null, null ]; switch (result) { case HitResult.SUPER_EFFECTIVE: @@ -62,12 +64,12 @@ export default class DamageNumberHandler { this.damageNumbers.set(battlerIndex, []); } - const yOffset = this.damageNumbers.get(battlerIndex).length * -10; + const yOffset = this.damageNumbers.get(battlerIndex)!.length * -10; if (yOffset) { damageNumber.y += yOffset; } - this.damageNumbers.get(battlerIndex).push(damageNumber); + this.damageNumbers.get(battlerIndex)!.push(damageNumber); if (scene.damageNumbersMode === 1) { scene.tweens.add({ @@ -83,7 +85,7 @@ export default class DamageNumberHandler { alpha: 0, ease: "Sine.easeIn", onComplete: () => { - this.damageNumbers.get(battlerIndex).splice(this.damageNumbers.get(battlerIndex).indexOf(damageNumber), 1); + this.damageNumbers.get(battlerIndex)!.splice(this.damageNumbers.get(battlerIndex)!.indexOf(damageNumber), 1); damageNumber.destroy(true); } }); @@ -167,7 +169,7 @@ export default class DamageNumberHandler { delay: Utils.fixedInt(500), alpha: 0, onComplete: () => { - this.damageNumbers.get(battlerIndex).splice(this.damageNumbers.get(battlerIndex).indexOf(damageNumber), 1); + this.damageNumbers.get(battlerIndex)!.splice(this.damageNumbers.get(battlerIndex)!.indexOf(damageNumber), 1); damageNumber.destroy(true); } } diff --git a/src/field/pokemon-sprite-sparkle-handler.ts b/src/field/pokemon-sprite-sparkle-handler.ts index 5312dd18727..ccf6a098667 100644 --- a/src/field/pokemon-sprite-sparkle-handler.ts +++ b/src/field/pokemon-sprite-sparkle-handler.ts @@ -35,7 +35,7 @@ export default class PokemonSpriteSparkleHandler { const ratioX = s.width / width; const ratioY = s.height / height; const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE"); - if (pixel.alpha) { + if (pixel?.alpha) { const [ xOffset, yOffset ] = [ -s.originX * s.width, -s.originY * s.height]; const sparkle = (s.scene as BattleScene).addFieldSprite(((pokemon?.x || 0) + s.x + pixelX * ratioX + xOffset), ((pokemon?.y || 0) + s.y + pixelY * ratioY + yOffset), "tera_sparkle"); sparkle.pipelineData["ignoreTimeTint"] = s.pipelineData["ignoreTimeTint"]; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 365d8c545f1..aa3153f01b0 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -19,10 +19,10 @@ import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEv import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms"; import { DamagePhase, FaintPhase, LearnMovePhase, MoveEffectPhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase, ToggleDoublePositionPhase, MoveEndPhase } from "../phases"; import { BattleStat } from "../data/battle-stat"; -import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag } from "../data/battler-tags"; +import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag } from "../data/battler-tags"; import { WeatherType } from "../data/weather"; import { TempBattleStat } from "../data/temp-battle-stat"; -import { ArenaTagSide, WeakenMoveScreenTag } from "../data/arena-tag"; +import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag"; import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr } from "../data/ability"; import PokemonData from "../system/pokemon-data"; import { BattlerIndex } from "../battle"; @@ -80,8 +80,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public ivs: integer[]; public nature: Nature; public natureOverride: Nature | -1; - public moveset: PokemonMove[]; - public status: Status; + public moveset: (PokemonMove | null)[]; + public status: Status | null; public friendship: integer; public metLevel: integer; public metBiome: Biome | -1; @@ -89,8 +89,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public luck: integer; public pauseEvolutions: boolean; public pokerus: boolean; + public wildFlee: boolean; - public fusionSpecies: PokemonSpecies; + public fusionSpecies: PokemonSpecies | null; public fusionFormIndex: integer; public fusionAbilityIndex: integer; public fusionShiny: boolean; @@ -98,7 +99,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public fusionGender: Gender; public fusionLuck: integer; - private summonDataPrimer: PokemonSummonData; + private summonDataPrimer: PokemonSummonData | null; public summonData: PokemonSummonData; public battleData: PokemonBattleData; @@ -108,7 +109,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public fieldPosition: FieldPosition; public maskEnabled: boolean; - public maskSprite: Phaser.GameObjects.Sprite; + public maskSprite: Phaser.GameObjects.Sprite | null; private shinySparkle: Phaser.GameObjects.Sprite; @@ -132,6 +133,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; + this.wildFlee = false; + // Determine the ability index if (abilityIndex !== undefined) { this.abilityIndex = abilityIndex; // Use the provided ability index if it is defined @@ -172,7 +175,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.nickname = dataSource.nickname; this.natureOverride = dataSource.natureOverride !== undefined ? dataSource.natureOverride : -1; this.moveset = dataSource.moveset; - this.status = dataSource.status; + this.status = dataSource.status!; // TODO: is this bang correct? this.friendship = dataSource.friendship !== undefined ? dataSource.friendship : this.species.baseFriendship; this.metLevel = dataSource.metLevel || 5; this.luck = dataSource.luck; @@ -180,7 +183,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.metSpecies = dataSource.metSpecies ?? (this.metBiome !== -1 ? this.species.speciesId : this.species.getRootSpeciesId(true)); this.pauseEvolutions = dataSource.pauseEvolutions; this.pokerus = !!dataSource.pokerus; - this.fusionSpecies = dataSource.fusionSpecies instanceof PokemonSpecies ? dataSource.fusionSpecies : getPokemonSpecies(dataSource.fusionSpecies); + this.fusionSpecies = dataSource.fusionSpecies instanceof PokemonSpecies ? dataSource.fusionSpecies : dataSource.fusionSpecies ? getPokemonSpecies(dataSource.fusionSpecies) : null; this.fusionFormIndex = dataSource.fusionFormIndex; this.fusionAbilityIndex = dataSource.fusionAbilityIndex; this.fusionShiny = dataSource.fusionShiny; @@ -247,8 +250,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getNameToRender(useIllusion: boolean = true) { - const name: string = (!useIllusion && this.illusion.active) ? this.illusion.name : this.name; - const nickname: string = (!useIllusion && this.illusion.active) ? this.illusion.nickname : this.nickname; + const name: string = (!useIllusion && this.illusion.active) ? this.illusion.name! : this.name; + const nickname: string = (!useIllusion && this.illusion.active) ? this.illusion.nickname! : this.nickname; try { if (nickname) { return decodeURIComponent(escape(atob(nickname))); @@ -303,14 +306,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Check if this pokemon is both not fainted and allowed to be in battle. + * Check if this pokemon is both not fainted (or a fled wild pokemon) and allowed to be in battle. * This is frequently a better alternative to {@link isFainted} * @returns {boolean} True if pokemon is allowed in battle */ isAllowedInBattle(): boolean { const challengeAllowed = new Utils.BooleanHolder(true); applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed); - return !this.isFainted() && challengeAllowed.value; + return !this.isFainted() && !this.wildFlee && challengeAllowed.value; } isActive(onField?: boolean): boolean { @@ -354,7 +357,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { generateIllusion(): boolean { if (this.hasTrainer()) { const party: Pokemon[] = (this.isPlayer() ? this.scene.getParty() : this.scene.getEnemyParty()).filter(p => p.isAllowedInBattle()); - const lastPokemon: Pokemon = party.at(-1); + const lastPokemon: Pokemon = party.at(-1) || this; const speciesId = lastPokemon.species.speciesId; if ( lastPokemon === this || this.illusion.active || @@ -375,7 +378,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { gender: lastPokemon.gender, pokeball: lastPokemon.pokeball, fusionFormIndex: lastPokemon.fusionFormIndex, - fusionSpecies: lastPokemon.fusionSpecies, + fusionSpecies: lastPokemon.fusionSpecies || undefined, fusionVariant: this.fusionVariant, fusionShiny: this.fusionShiny, fusionGender: lastPokemon.fusionGender @@ -434,14 +437,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - this.name = this.illusion.name; - this.nickname = this.illusion.nickname; - this.shiny = this.illusion.shiny; - this.variant = this.illusion.variant; - this.fusionVariant = this.illusion.fusionVariant; - this.fusionShiny = this.illusion.fusionShiny; + this.name = this.illusion.name!; + this.nickname = this.illusion.nickname!; + this.shiny = this.illusion.shiny!; + this.variant = this.illusion.variant!; + this.fusionVariant = this.illusion.fusionVariant!; + this.fusionShiny = this.illusion.fusionShiny!; this.illusion = {active: false, available: false}; - if (this.isOnField) { + if (this.isOnField()) { this.scene.playSound("PRSFX- Transform"); } if (this.shiny) { @@ -465,7 +468,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ loadAssets(ignoreOverride: boolean = true, useIllusion: boolean = false): Promise { return new Promise(resolve => { - const moveIds = this.getMoveset().map(m => m.getMove().id); + const moveIds = this.getMoveset().map(m => m!.getMove().id); // TODO: is this bang correct? Promise.allSettled(moveIds.map(m => initMoveAnim(this.scene, m))) .then(() => { loadMoveAnimAssets(this.scene, moveIds); @@ -557,7 +560,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.species.forms[this.formIndex].formKey; } - getFusionFormKey(): string { + getFusionFormKey(): string | null { if (!this.fusionSpecies) { return null; } @@ -578,7 +581,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteId(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex : this.formIndex; + const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } @@ -587,7 +590,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const formIndex: integer = this.illusion.active ? this.illusion.formIndex : this.formIndex; + const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant, back); } @@ -606,7 +609,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionSpriteId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -615,7 +618,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant, back); } @@ -629,7 +632,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconAtlasKey(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex : this.formIndex; + const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconAtlasKey(formIndex, this.shiny, this.variant); } @@ -638,12 +641,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconId(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex : this.formIndex; + const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } getFusionIconId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getIconId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -651,8 +654,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the speciesForm of the illusion or not. */ getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const species: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.species : this.species; - const formIndex: integer = useIllusion && this.illusion.active ? this.illusion.formIndex : this.formIndex; + const species: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.species! : this.species; + const formIndex: integer = useIllusion && this.illusion.active ? this.illusion.formIndex! : this.formIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.speciesForm; @@ -669,14 +672,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. */ getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const fusionSpecies: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.fusionSpecies : this.fusionSpecies; - const fusionFormIndex: integer = useIllusion && this.illusion.active ? this.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionSpecies: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.fusionSpecies! : this.fusionSpecies!; + const fusionFormIndex: integer = useIllusion && this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.fusionSpeciesForm; } if (!fusionSpecies?.forms?.length || fusionFormIndex >= fusionSpecies?.forms.length) { - return fusionSpecies; + //@ts-ignore + return fusionSpecies; // TODO: I don't even know how to fix this... A complete cluster of classes involved + null } return fusionSpecies?.forms[fusionFormIndex]; } @@ -685,7 +689,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getAt(0) as Phaser.GameObjects.Sprite; } - getTintSprite(): Phaser.GameObjects.Sprite { + getTintSprite(): Phaser.GameObjects.Sprite | null { return !this.maskEnabled ? this.getAt(1) as Phaser.GameObjects.Sprite : this.maskSprite; @@ -711,7 +715,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } updateSpritePipelineData(): void { - [ this.getSprite(), this.getTintSprite() ].map(s => s.pipelineData["teraColor"] = getTypeRgb(this.getTeraType())); + [ this.getSprite(), this.getTintSprite() ].filter(s => !!s).map(s => s.pipelineData["teraColor"] = getTypeRgb(this.getTeraType())); this.updateInfo(true); } @@ -759,7 +763,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } playAnim(): void { - this.tryPlaySprite(this.getSprite(), this.getTintSprite(), this.getBattleSpriteKey()); + this.tryPlaySprite(this.getSprite(), this.getTintSprite()!, this.getBattleSpriteKey()); // TODO: is the bag correct? } getFieldPositionOffset(): [ number, number ] { @@ -992,7 +996,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { if (useIllusion && this.illusion.active) { - return this.illusion.gender; + return this.illusion.gender!; } else if (!ignoreOverride && this.summonData?.gender !== undefined) { return this.summonData.gender; } @@ -1004,7 +1008,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { if (useIllusion && this.illusion.active) { - return this.illusion.fusionGender; + return this.illusion.fusionGender!; } else if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { return this.summonData.fusionGender; } @@ -1013,7 +1017,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isShiny(useIllusion: boolean = false): boolean { if (!useIllusion && this.illusion.active) { - return this.illusion.shiny || (!!this.illusion.fusionSpecies && this.illusion.fusionShiny); + return this.illusion.shiny || (!!this.illusion.fusionSpecies && this.illusion.fusionShiny) || false; } else { return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); } @@ -1021,7 +1025,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isDoubleShiny(useIllusion: boolean = false): boolean { if (!useIllusion && this.illusion.active) { - return this.isFusion(false) && this.illusion.shiny && this.illusion.fusionShiny; + return this.isFusion(false) && this.illusion.shiny! && this.illusion.fusionShiny!; } else { return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; } @@ -1029,7 +1033,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getVariant(useIllusion: boolean = false): Variant { if (!useIllusion && this.illusion.active) { - return !this.isFusion(false) ? this.illusion.variant : Math.max(this.variant, this.fusionVariant) as Variant; + return !this.isFusion(false) ? this.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; } else { return !this.isFusion(true) ? this.variant : Math.max(this.variant, this.fusionVariant) as Variant; } @@ -1038,7 +1042,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getBaseVariant(doubleShiny: boolean): Variant { if (doubleShiny) { - return this.illusion.active ? this.illusion.variant : this.variant; + return this.illusion.active ? this.illusion.variant! : this.variant; } else { return this.getVariant(); } @@ -1057,12 +1061,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getName(illusion: boolean = false): string { - return (!illusion && this.illusion.active) ? this.illusion.name : this.name; + return (!illusion && this.illusion.active && this.illusion.name) ? this.illusion.name : this.name; } abstract isBoss(): boolean; - getMoveset(ignoreOverride?: boolean): PokemonMove[] { + getMoveset(ignoreOverride?: boolean): (PokemonMove | null)[] { const ret = !ignoreOverride && this.summonData?.moveset ? this.summonData.moveset : this.moveset; @@ -1113,7 +1117,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.metBiome === -1) { levelMoves = this.getUnlockedEggMoves().concat(levelMoves); } - return levelMoves.filter(lm => !this.moveset.some(m => m.moveId === lm)); + return levelMoves.filter(lm => !this.moveset.some(m => m?.moveId === lm)); } /** @@ -1126,6 +1130,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride?: boolean, useIllusion: boolean | "AUTO" = "AUTO"): Type[] { const types: Type[] = []; + if (includeTeraType) { const teraType = this.getTeraType(); if (teraType !== Type.UNKNOWN) { @@ -1134,7 +1139,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (!types.length || !includeTeraType) { const doIllusion: boolean = useIllusion === "AUTO" ? !forDefend : useIllusion; - if (!ignoreOverride && this.summonData?.types && (!this.illusion.active || !doIllusion)) { + if (!ignoreOverride && this.summonData?.types && this.summonData.types.length !== 0 && (!this.illusion.active || !doIllusion)) { this.summonData.types.forEach(t => types.push(t)); } else { const speciesForm = this.getSpeciesForm(ignoreOverride, doIllusion); @@ -1304,7 +1309,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } } - return (this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this)); + return (!!this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this)); } /** @@ -1433,7 +1438,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ? moveOrType : undefined; const moveType = (moveOrType instanceof Move) - ? move.type + ? move!.type // TODO: is this bang correct? : moveOrType; if (moveType === Type.STELLAR) { @@ -1450,6 +1455,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (ignoreImmunity.value) { return 1; } + + const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[]; + if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) { + return 1; + } } return getTypeDamageMultiplier(moveType, defType); @@ -1471,7 +1481,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - return multiplier; + return multiplier as TypeDamageMultiplier; } /** @@ -1514,7 +1524,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return (atkScore + defScore) * hpDiffRatio; } - getEvolution(): SpeciesFormEvolution { + getEvolution(): SpeciesFormEvolution | null { if (pokemonEvolutions.hasOwnProperty(this.species.speciesId)) { const evolutions = pokemonEvolutions[this.species.speciesId]; for (const e of evolutions) { @@ -1526,7 +1536,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - if (this.isFusion() && pokemonEvolutions.hasOwnProperty(this.fusionSpecies.speciesId)) { + if (this.isFusion() && this.fusionSpecies && pokemonEvolutions.hasOwnProperty(this.fusionSpecies.speciesId)) { const fusionEvolutions = pokemonEvolutions[this.fusionSpecies.speciesId].map(e => new FusionSpeciesFormEvolution(this.species.speciesId, e)); for (const fe of fusionEvolutions) { if (!fe.item && this.level >= fe.level && (!fe.preFormKey || this.getFusionFormKey() === fe.preFormKey)) { @@ -1736,7 +1746,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } clearFusionSpecies(): void { - this.fusionSpecies = undefined; + this.fusionSpecies = null; this.fusionFormIndex = 0; this.fusionAbilityIndex = 0; this.fusionShiny = false; @@ -1896,9 +1906,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier. // Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights double if STAB. // Status moves remain unchanged on weight, this encourages 1-2 - movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo.moveId)).map(m => [m[0], this.moveset.some(mo => mo.getMove().category !== MoveCategory.STATUS && mo.getMove().type === allMoves[m[0]].type) ? Math.ceil(Math.sqrt(m[1])) : allMoves[m[0]].category !== MoveCategory.STATUS ? Math.ceil(m[1]/Math.max(Math.pow(4, this.moveset.filter(mo => mo.getMove().power > 1).length)/8,0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1)) : m[1]]); + movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)).map(m => [m[0], this.moveset.some(mo => mo?.getMove().category !== MoveCategory.STATUS && mo?.getMove().type === allMoves[m[0]].type) ? Math.ceil(Math.sqrt(m[1])) : allMoves[m[0]].category !== MoveCategory.STATUS ? Math.ceil(m[1]/Math.max(Math.pow(4, this.moveset.filter(mo => (mo?.getMove().power!) > 1).length)/8,0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1)) : m[1]]); // TODO: is this bang correct? } else { // Non-trainer pokemon just use normal weights - movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo.moveId)); + movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)); } const totalWeight = movePool.reduce((v, m) => v + m[1], 0); let rand = Utils.randSeedInt(totalWeight); @@ -1916,7 +1926,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const move = this.getMoveset().length > moveIndex ? this.getMoveset()[moveIndex] : null; - return move?.isUsable(this, ignorePp); + return move?.isUsable(this, ignorePp)!; // TODO: is this bang correct? } showInfo(): void { @@ -1965,6 +1975,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }); } + /** + * sets if the pokemon has fled (implies it's a wild pokemon) + * @param status - boolean + */ + setWildFlee(status: boolean): void { + this.wildFlee = status; + } + updateInfo(instant?: boolean): Promise { return this.battleInfo.updateInfo(this, instant); } @@ -1999,7 +2017,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.levelExp = this.exp - getLevelTotalExp(this.level, this.species.growthRate); } - getOpponent(targetIndex: integer): Pokemon { + getOpponent(targetIndex: integer): Pokemon | null { const ret = this.getOpponents()[targetIndex]; if (ret.summonData) { return ret; @@ -2057,6 +2075,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, target, sourceMove, targetEvasionLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), TempBattleStat.ACC, userAccuracyLevel); + if (target.findTag(t => t instanceof ExposedTag)) { + targetEvasionLevel.value = Math.min(0, targetEvasionLevel.value); + } + const accuracyMultiplier = new Utils.NumberHolder(1); if (userAccuracyLevel.value !== targetEvasionLevel.value) { accuracyMultiplier.value = userAccuracyLevel.value > targetEvasionLevel.value @@ -2077,6 +2099,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { apply(source: Pokemon, move: Move): HitResult { let result: HitResult; const damage = new Utils.NumberHolder(0); + const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; const defendingSidePlayField = this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField(); const variableCategory = new Utils.IntegerHolder(move.category); @@ -2103,7 +2126,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Apply arena tags for conditional protection if (!move.checkFlag(MoveFlags.IGNORE_PROTECT, source, this) && !move.isAllyTarget()) { - const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; this.scene.arena.applyTagsForSide(ArenaTagType.QUICK_GUARD, defendingSide, cancelled, this, move.priority); this.scene.arena.applyTagsForSide(ArenaTagType.WIDE_GUARD, defendingSide, cancelled, this, move.moveTarget); this.scene.arena.applyTagsForSide(ArenaTagType.MAT_BLOCK, defendingSide, cancelled, this, move.category); @@ -2160,7 +2182,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); const bonusCrit = new Utils.BooleanHolder(false); - if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { + //@ts-ignore + if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. if (bonusCrit.value) { critLevel.value += 1; } @@ -2170,25 +2193,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } console.log(`crit stage: +${critLevel.value}`); const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critLevel.value, 3))]; - isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.randBattleSeedInt(critChance)); + isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance); if (Overrides.NEVER_CRIT_OVERRIDE) { isCritical = false; } } if (isCritical) { + const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide); const blockCrit = new Utils.BooleanHolder(false); applyAbAttrs(BlockCritAbAttr, this, null, blockCrit); - if (blockCrit.value) { + if (noCritTag || blockCrit.value) { isCritical = false; } } - const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, null, isCritical)); + const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical)); const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier); const screenMultiplier = new Utils.NumberHolder(1); if (!isCritical) { - this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, this.scene.currentBattle.double, screenMultiplier); + this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier); } const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier.value) === 0; const sourceTypes = source.getTypes(); @@ -2270,6 +2294,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isCritical = false; result = HitResult.EFFECTIVE; } + result = result!; // telling TS compiler that result is defined! if (!result) { if (!typeMultiplier.value) { @@ -2374,7 +2399,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (damage) { - const attacker = this.scene.getPokemonById(source.id); + const attacker = this.scene.getPokemonById(source.id)!; // TODO: is this bang correct? destinyTag?.lapse(attacker, BattlerTagLapseType.CUSTOM); } } @@ -2474,7 +2499,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isMax(): boolean { const maxForms = [SpeciesFormKey.GIGANTAMAX, SpeciesFormKey.GIGANTAMAX_RAPID, SpeciesFormKey.GIGANTAMAX_SINGLE, SpeciesFormKey.ETERNAMAX] as string[]; - return maxForms.includes(this.getFormKey()) || maxForms.includes(this.getFusionFormKey()); + return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!)); } addTag(tagType: BattlerTagType, turnCount: integer = 0, sourceMove?: Moves, sourceId?: integer): boolean { @@ -2484,7 +2509,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - const newTag = getBattlerTag(tagType, turnCount, sourceMove, sourceId); + const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct? const cancelled = new Utils.BooleanHolder(false); applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, newTag, cancelled); @@ -2503,18 +2528,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** @overload */ - getTag(tagType: BattlerTagType): BattlerTag; + getTag(tagType: BattlerTagType): BattlerTag | null; /** @overload */ - getTag(tagType: Constructor): T; + getTag(tagType: Constructor): T | null; - getTag(tagType: BattlerTagType | Constructor): BattlerTag { + getTag(tagType: BattlerTagType | Constructor): BattlerTag | null { if (!this.summonData) { return null; } - return tagType instanceof Function + return (tagType instanceof Function ? this.summonData.tags.find(t => t instanceof tagType) - : this.summonData.tags.find(t => t.tagType === tagType); + : this.summonData.tags.find(t => t.tagType === tagType) + )!; // TODO: is this bang correct? } findTag(tagFilter: ((tag: BattlerTag) => boolean)) { @@ -2618,7 +2644,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.getMoveHistory().push(turnMove); } - getLastXMoves(turnCount?: integer): TurnMove[] { + getLastXMoves(turnCount: integer = 0): TurnMove[] { const moveHistory = this.getMoveHistory(); return moveHistory.slice(turnCount >= 0 ? Math.max(moveHistory.length - (turnCount || 1), 0) : 0, moveHistory.length).reverse(); } @@ -2696,9 +2722,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let frameThreshold: number; sprite.anims.pause(); - tintSprite.anims.pause(); + tintSprite?.anims.pause(); - let faintCryTimer = this.scene.time.addEvent({ + let faintCryTimer : Phaser.Time.TimerEvent | null = this.scene.time.addEvent({ delay: Utils.fixedInt(delay), repeat: -1, callback: () => { @@ -2708,7 +2734,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { while (frameProgress > frameThreshold) { if (sprite.anims.duration) { sprite.anims.nextFrame(); - tintSprite.anims.nextFrame(); + tintSprite?.anims.nextFrame(); } frameProgress -= frameThreshold; } @@ -2716,7 +2742,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { rate *= 0.99; cry.setRate(rate); } else { - faintCryTimer.destroy(); + faintCryTimer?.destroy(); faintCryTimer = null; if (callback) { callback(); @@ -2775,9 +2801,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let frameThreshold: number; sprite.anims.pause(); - tintSprite.anims.pause(); + tintSprite?.anims.pause(); - let faintCryTimer = this.scene.time.addEvent({ + let faintCryTimer: Phaser.Time.TimerEvent | null = this.scene.time.addEvent({ delay: Utils.fixedInt(delay), repeat: -1, callback: () => { @@ -2787,7 +2813,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { while (frameProgress > frameThreshold) { if (sprite.anims.duration) { sprite.anims.nextFrame(); - tintSprite.anims.nextFrame(); + tintSprite?.anims.nextFrame(); } frameProgress -= frameThreshold; } @@ -2804,7 +2830,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { fusionCry.setRate(rate); } if ((!cry || cry.pendingRemove) && (!fusionCry || fusionCry.pendingRemove)) { - faintCryTimer.destroy(); + faintCryTimer?.destroy(); faintCryTimer = null; if (callback) { callback(); @@ -2835,7 +2861,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.gender !== Gender.GENDERLESS && pokemon.gender === (this.gender === Gender.MALE ? Gender.FEMALE : Gender.MALE); } - canSetStatus(effect: StatusEffect, quiet: boolean = false, overrideStatus: boolean = false, sourcePokemon: Pokemon = null): boolean { + canSetStatus(effect: StatusEffect | undefined, quiet: boolean = false, overrideStatus: boolean = false, sourcePokemon: Pokemon | null = null): boolean { if (effect !== StatusEffect.FAINT) { if (overrideStatus ? this.status?.effect === effect : this.status) { return false; @@ -2886,7 +2912,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } break; case StatusEffect.FREEZE: - if (this.isOfType(Type.ICE) || [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(this.scene?.arena.weather?.weatherType)) { + if (this.isOfType(Type.ICE) || (this.scene?.arena?.weather?.weatherType &&[WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(this.scene.arena.weather.weatherType))) { return false; } break; @@ -2910,7 +2936,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return true; } - trySetStatus(effect: StatusEffect, asPhase: boolean = false, sourcePokemon: Pokemon = null, cureTurn: integer = 0, sourceText: string = null): boolean { + trySetStatus(effect: StatusEffect | undefined, asPhase: boolean = false, sourcePokemon: Pokemon | null = null, cureTurn: integer | null = 0, sourceText: string | null = null): boolean { if (!this.canSetStatus(effect, asPhase, false, sourcePokemon)) { return false; } @@ -2924,7 +2950,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (asPhase) { - this.scene.unshiftPhase(new ObtainStatusEffectPhase(this.scene, this.getBattlerIndex(), effect, cureTurn, sourceText, sourcePokemon)); + this.scene.unshiftPhase(new ObtainStatusEffectPhase(this.scene, this.getBattlerIndex(), effect, cureTurn, sourceText!, sourcePokemon!)); // TODO: are these bangs correct? return true; } @@ -2952,6 +2978,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } + statusCureTurn = statusCureTurn!; // tell TS compiler it's defined + effect = effect!; // If `effect` is undefined then `trySetStatus()` will have already returned early via the `canSetStatus()` call this.status = new Status(effect, 0, statusCureTurn?.value); if (effect !== StatusEffect.FAINT) { @@ -2972,7 +3000,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!revive && lastStatus === StatusEffect.FAINT) { return; } - this.status = undefined; + this.status = null; if (lastStatus === StatusEffect.SLEEP) { this.setFrameRate(12); if (this.getTag(BattlerTagType.NIGHTMARE)) { @@ -3040,16 +3068,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { setFrameRate(frameRate: integer) { this.scene.anims.get(this.getBattleSpriteKey()).frameRate = frameRate; this.getSprite().play(this.getBattleSpriteKey()); - this.getTintSprite().play(this.getBattleSpriteKey()); + this.getTintSprite()?.play(this.getBattleSpriteKey()); } tint(color: number, alpha?: number, duration?: integer, ease?: string) { const tintSprite = this.getTintSprite(); - tintSprite.setTintFill(color); - tintSprite.setVisible(true); + tintSprite?.setTintFill(color); + tintSprite?.setVisible(true); if (duration) { - tintSprite.setAlpha(0); + tintSprite?.setAlpha(0); this.scene.tweens.add({ targets: tintSprite, @@ -3058,7 +3086,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ease: ease || "Linear" }); } else { - tintSprite.setAlpha(alpha); + tintSprite?.setAlpha(alpha); } } @@ -3072,32 +3100,32 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { duration: duration, ease: ease || "Linear", onComplete: () => { - tintSprite.setVisible(false); - tintSprite.setAlpha(1); + tintSprite?.setVisible(false); + tintSprite?.setAlpha(1); } }); } else { - tintSprite.setVisible(false); - tintSprite.setAlpha(1); + tintSprite?.setVisible(false); + tintSprite?.setAlpha(1); } } enableMask() { if (!this.maskEnabled) { this.maskSprite = this.getTintSprite(); - this.maskSprite.setVisible(true); - this.maskSprite.setPosition(this.x * this.parentContainer.scale + this.parentContainer.x, + this.maskSprite?.setVisible(true); + this.maskSprite?.setPosition(this.x * this.parentContainer.scale + this.parentContainer.x, this.y * this.parentContainer.scale + this.parentContainer.y); - this.maskSprite.setScale(this.getSpriteScale() * this.parentContainer.scale); + this.maskSprite?.setScale(this.getSpriteScale() * this.parentContainer.scale); this.maskEnabled = true; } } disableMask() { if (this.maskEnabled) { - this.maskSprite.setVisible(false); - this.maskSprite.setPosition(0, 0); - this.maskSprite.setScale(this.getSpriteScale()); + this.maskSprite?.setVisible(false); + this.maskSprite?.setPosition(0, 0); + this.maskSprite?.setScale(this.getSpriteScale()); this.maskSprite = null; this.maskEnabled = false; } @@ -3112,7 +3140,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { updateFusionPalette(ignoreOveride?: boolean): void { if (!this.getFusionSpeciesForm(ignoreOveride)) { - [ this.getSprite(), this.getTintSprite() ].map(s => { + [ this.getSprite(), this.getTintSprite() ].filter(s => !!s).map(s => { s.pipelineData[`spriteColors${ignoreOveride && this.summonData?.speciesForm ? "Base" : ""}`] = []; s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData?.speciesForm ? "Base" : ""}`] = []; }); @@ -3148,9 +3176,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const frame = [ sourceFrame, sourceBackFrame, fusionFrame, fusionBackFrame ][c]; canv.width = frame.width; canv.height = frame.height; - context.drawImage([ sourceImage, sourceBackImage, fusionImage, fusionBackImage ][c], frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); - const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); - pixelData.push(imageData.data); + + if (context) { + context.drawImage([ sourceImage, sourceBackImage, fusionImage, fusionBackImage ][c], frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); + const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); + pixelData.push(imageData.data); + } }); for (let f = 0; f < 2; f++) { @@ -3170,7 +3201,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const color = Utils.rgbaToInt([r, g, b, a]); if (variantColorSet.has(color)) { const mappedPixel = variantColorSet.get(color); - [ r, g, b, a ] = mappedPixel; + if (mappedPixel) { + [ r, g, b, a ] = mappedPixel; + } } } if (!spriteColors.find(c => c[0] === r && c[1] === g && c[2] === b)) { @@ -3182,7 +3215,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const fusionSpriteColors = JSON.parse(JSON.stringify(spriteColors)); - const pixelColors = []; + const pixelColors: number[] = []; for (let f = 0; f < 2; f++) { for (let i = 0; i < pixelData[f].length; i += 4) { const total = pixelData[f].slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0); @@ -3193,7 +3226,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - const fusionPixelColors = []; + const fusionPixelColors : number[] = []; for (let f = 0; f < 2; f++) { const variantColors = variantColorCache[!f ? fusionSpriteKey : fusionBackSpriteKey]; const variantColorSet = new Map(); @@ -3212,7 +3245,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const color = Utils.rgbaToInt([r, g, b, a]); if (variantColorSet.has(color)) { const mappedPixel = variantColorSet.get(color); - [ r, g, b, a ] = mappedPixel; + if (mappedPixel) { + [ r, g, b, a ] = mappedPixel; + } } } fusionPixelColors.push(argbFromRgba({ r, g, b, a })); @@ -3232,9 +3267,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { Math.random = originalRandom; + paletteColors = paletteColors!; // tell TS compiler that paletteColors is defined! + fusionPaletteColors = fusionPaletteColors!; // TS compiler that fusionPaletteColors is defined! const [ palette, fusionPalette ] = [ paletteColors, fusionPaletteColors ] .map(paletteColors => { - let keys = Array.from(paletteColors.keys()).sort((a: integer, b: integer) => paletteColors.get(a) < paletteColors.get(b) ? 1 : -1); + let keys = Array.from(paletteColors.keys()).sort((a: integer, b: integer) => paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1); let rgbaColors: Map; let hsvColors: Map; @@ -3247,19 +3284,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { map.set(k, Object.values(rgbaFromArgb(k))); return map; }, new Map()); hsvColors = Array.from(rgbaColors.keys()).reduce((map: Map, k: number) => { - const rgb = rgbaColors.get(k).slice(0, 3); + const rgb = rgbaColors.get(k)!.slice(0, 3); map.set(k, Utils.rgbToHsv(rgb[0], rgb[1], rgb[2])); return map; }, new Map()); for (let c = keys.length - 1; c >= 0; c--) { - const hsv = hsvColors.get(keys[c]); + const hsv = hsvColors.get(keys[c])!; for (let c2 = 0; c2 < c; c2++) { - const hsv2 = hsvColors.get(keys[c2]); + const hsv2 = hsvColors.get(keys[c2])!; const diff = Math.abs(hsv[0] - hsv2[0]); if (diff < 30 || diff >= 330) { if (mappedColors.has(keys[c])) { - mappedColors.get(keys[c]).push(keys[c2]); + mappedColors.get(keys[c])!.push(keys[c2]); } else { mappedColors.set(keys[c], [ keys[c2] ]); } @@ -3269,10 +3306,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } mappedColors.forEach((values: integer[], key: integer) => { - const keyColor = rgbaColors.get(key); - const valueColors = values.map(v => rgbaColors.get(v)); + const keyColor = rgbaColors.get(key)!; + const valueColors = values.map(v => rgbaColors.get(v)!); const color = keyColor.slice(0); - let count = paletteColors.get(key); + let count = paletteColors.get(key)!; for (const value of values) { const valueCount = paletteColors.get(value); if (!valueCount) { @@ -3282,10 +3319,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } for (let c = 0; c < 3; c++) { - color[c] *= (paletteColors.get(key) / count); + color[c] *= (paletteColors.get(key)! / count); values.forEach((value: integer, i: integer) => { if (paletteColors.has(value)) { - const valueCount = paletteColors.get(value); + const valueCount = paletteColors.get(value)!; color[c] += valueColors[i][c] * (valueCount / count); } }); @@ -3303,7 +3340,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { paletteColors.set(argbFromRgba({ r: color[0], g: color[1], b: color[2], a: color[3] }), count); }); - keys = Array.from(paletteColors.keys()).sort((a: integer, b: integer) => paletteColors.get(a) < paletteColors.get(b) ? 1 : -1); + keys = Array.from(paletteColors.keys()).sort((a: integer, b: integer) => paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1); } while (mappedColors.size); return keys.map(c => Object.values(rgbaFromArgb(c))); @@ -3334,7 +3371,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - [ this.getSprite(), this.getTintSprite() ].map(s => { + [ this.getSprite(), this.getTintSprite() ].filter(s => !!s).map(s => { s.pipelineData[`spriteColors${ignoreOveride && this.summonData?.speciesForm ? "Base" : ""}`] = spriteColors; s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData?.speciesForm ? "Base" : ""}`] = fusionSpriteColors; }); @@ -3398,7 +3435,7 @@ export default interface Pokemon { export class PlayerPokemon extends Pokemon { public compatibleTms: Moves[]; - constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender: Gender, shiny: boolean, variant: Variant, ivs: integer[], nature: Nature, dataSource: Pokemon | PokemonData) { + constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData) { super(scene, 106, 148, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource); if (Overrides.STATUS_OVERRIDE) { @@ -3414,7 +3451,7 @@ export class PlayerPokemon extends Pokemon { } if (!dataSource) { - this.generateAndPopulateMoveset(); + this.moveset = []; } this.generateCompatibleTms(); } @@ -3502,11 +3539,11 @@ export class PlayerPokemon extends Pokemon { addFriendship(friendship: integer): void { const starterSpeciesId = this.species.getRootSpeciesId(); - const fusionStarterSpeciesId = this.isFusion() ? this.fusionSpecies.getRootSpeciesId() : 0; + const fusionStarterSpeciesId = this.isFusion() && this.fusionSpecies ? this.fusionSpecies.getRootSpeciesId() : 0; const starterData = [ this.scene.gameData.starterData[starterSpeciesId], fusionStarterSpeciesId ? this.scene.gameData.starterData[fusionStarterSpeciesId] : null - ].filter(d => d); + ].filter(d => !!d); const amount = new Utils.IntegerHolder(friendship); const starterAmount = new Utils.IntegerHolder(Math.floor(friendship * (this.scene.gameMode.isClassic && friendship > 0 ? 2 : 1) / (fusionStarterSpeciesId ? 2 : 1))); if (amount.value > 0) { @@ -3570,7 +3607,10 @@ export class PlayerPokemon extends Pokemon { }); } - getPossibleEvolution(evolution: SpeciesFormEvolution): Promise { + getPossibleEvolution(evolution: SpeciesFormEvolution | null): Promise { + if (!evolution) { + return new Promise(resolve => resolve(this)); + } return new Promise(resolve => { const evolutionSpecies = getPokemonSpecies(evolution.speciesId); const isFusion = evolution instanceof FusionSpeciesFormEvolution; @@ -3591,7 +3631,10 @@ export class PlayerPokemon extends Pokemon { }); } - evolve(evolution: SpeciesFormEvolution, preEvolution: PokemonSpeciesForm): Promise { + evolve(evolution: SpeciesFormEvolution | null, preEvolution: PokemonSpeciesForm): Promise { + if (!evolution) { + return new Promise(resolve => resolve()); + } return new Promise(resolve => { this.pauseEvolutions = false; // Handles Nincada evolving into Ninjask + Shedinja @@ -3603,7 +3646,7 @@ export class PlayerPokemon extends Pokemon { this.fusionSpecies = getPokemonSpecies(evolution.speciesId); } if (evolution.preFormKey !== null) { - const formIndex = Math.max((!isFusion ? this.species : this.fusionSpecies).forms.findIndex(f => f.formKey === evolution.evoFormKey), 0); + const formIndex = Math.max((!isFusion || !this.fusionSpecies ? this.species : this.fusionSpecies).forms.findIndex(f => f.formKey === evolution.evoFormKey), 0); if (!isFusion) { this.formIndex = formIndex; } else { @@ -3659,10 +3702,10 @@ export class PlayerPokemon extends Pokemon { const isFusion = evolution instanceof FusionSpeciesFormEvolution; const evoSpecies = (!isFusion ? this.species : this.fusionSpecies); - if (evoSpecies.speciesId === Species.NINCADA && evolution.speciesId === Species.NINJASK) { + if (evoSpecies?.speciesId === Species.NINCADA && evolution.speciesId === Species.NINJASK) { const newEvolution = pokemonEvolutions[evoSpecies.speciesId][1]; - if (newEvolution.condition.predicate(this)) { + if (newEvolution.condition?.predicate(this)) { const newPokemon = this.scene.addPlayerPokemon(this.species, this.level, this.abilityIndex, this.formIndex, undefined, this.shiny, this.variant, this.ivs, this.nature); newPokemon.natureOverride = this.natureOverride; newPokemon.passive = this.passive; @@ -3758,7 +3801,7 @@ export class PlayerPokemon extends Pokemon { if (!this.isFainted()) { // If this Pokemon hasn't fainted, make sure the HP wasn't set over the new maximum this.hp = Math.min(this.hp, this.stats[Stat.HP]); - this.status = getRandomStatus(this.status, pokemon.status); // Get a random valid status between the two + this.status = getRandomStatus(this.status!, pokemon.status!); // Get a random valid status between the two // TODO: are the bangs correct? } else if (!pokemon.isFainted()) { // If this Pokemon fainted but the other hasn't, make sure the HP wasn't set to zero this.hp = Math.max(this.hp, 1); @@ -3783,7 +3826,7 @@ export class PlayerPokemon extends Pokemon { this.scene.removePartyMemberModifiers(fusedPartyMemberIndex); this.scene.getParty().splice(fusedPartyMemberIndex, 1)[0]; const newPartyMemberIndex = this.scene.getParty().indexOf(this); - pokemon.getMoveset(true).map(m => this.scene.unshiftPhase(new LearnMovePhase(this.scene, newPartyMemberIndex, m.getMove().id))); + pokemon.getMoveset(true).map(m => this.scene.unshiftPhase(new LearnMovePhase(this.scene, newPartyMemberIndex, m!.getMove().id))); // TODO: is the bang correct? pokemon.destroy(); this.updateFusionPalette(); resolve(); @@ -3803,9 +3846,9 @@ export class PlayerPokemon extends Pokemon { /** Returns a deep copy of this Pokemon's moveset array */ copyMoveset(): PokemonMove[] { - const newMoveset = []; + const newMoveset : PokemonMove[] = []; this.moveset.forEach(move => - newMoveset.push(new PokemonMove(move.moveId, 0, move.ppUp, move.virtual))); + newMoveset.push(new PokemonMove(move!.moveId, 0, move!.ppUp, move!.virtual))); // TODO: are those bangs correct? return newMoveset; } @@ -3819,9 +3862,9 @@ export class EnemyPokemon extends Pokemon { /** To indicate of the instance was populated with a dataSource -> e.g. loaded & populated from session data */ public readonly isPopulatedFromDataSource: boolean; - constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean, dataSource: PokemonData) { + constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean, dataSource?: PokemonData) { super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource?.formIndex, - dataSource?.gender, dataSource ? dataSource.shiny : false, dataSource ? dataSource.variant : undefined, null, dataSource ? dataSource.nature : undefined, dataSource); + dataSource?.gender, dataSource ? dataSource.shiny : false, dataSource ? dataSource.variant : undefined, undefined, dataSource ? dataSource.nature : undefined, dataSource); this.trainerSlot = trainerSlot; this.isPopulatedFromDataSource = !!dataSource; // if a dataSource is provided, then it was populated from dataSource @@ -3854,7 +3897,7 @@ export class EnemyPokemon extends Pokemon { let speciesId = species.speciesId; while ((prevolution = pokemonPrevolutions[speciesId])) { const evolution = pokemonEvolutions[prevolution].find(pe => pe.speciesId === speciesId && (!pe.evoFormKey || pe.evoFormKey === this.getFormKey())); - if (evolution.condition?.enforceFunc) { + if (evolution?.condition?.enforceFunc) { evolution.condition.enforceFunc(this); } speciesId = prevolution; @@ -3930,7 +3973,7 @@ export class EnemyPokemon extends Pokemon { getNextMove(): QueuedMove { // If this Pokemon has a move already queued, return it. const queuedMove = this.getMoveQueue().length - ? this.getMoveset().find(m => m.moveId === this.getMoveQueue()[0].move) + ? this.getMoveset().find(m => m?.moveId === this.getMoveQueue()[0].move) : null; if (queuedMove) { if (queuedMove.isUsable(this, this.getMoveQueue()[0].ignorePP)) { @@ -3942,24 +3985,24 @@ export class EnemyPokemon extends Pokemon { } // Filter out any moves this Pokemon cannot use - const movePool = this.getMoveset().filter(m => m.isUsable(this)); + const movePool = this.getMoveset().filter(m => m?.isUsable(this)); // If no moves are left, use Struggle. Otherwise, continue with move selection if (movePool.length) { // If there's only 1 move in the move pool, use it. if (movePool.length === 1) { - return { move: movePool[0].moveId, targets: this.getNextTargets(movePool[0].moveId) }; + return { move: movePool[0]!.moveId, targets: this.getNextTargets(movePool[0]!.moveId) }; // TODO: are the bangs correct? } // If a move is forced because of Encore, use it. const encoreTag = this.getTag(EncoreTag) as EncoreTag; if (encoreTag) { - const encoreMove = movePool.find(m => m.moveId === encoreTag.moveId); + const encoreMove = movePool.find(m => m?.moveId === encoreTag.moveId); if (encoreMove) { return { move: encoreMove.moveId, targets: this.getNextTargets(encoreMove.moveId) }; } } switch (this.aiType) { case AiType.RANDOM: // No enemy should spawn with this AI type in-game - const moveId = movePool[this.scene.randBattleSeedInt(movePool.length)].moveId; + const moveId = movePool[this.scene.randBattleSeedInt(movePool.length)]!.moveId; // TODO: is the bang correct? return { move: moveId, targets: this.getNextTargets(moveId) }; case AiType.SMART_RANDOM: case AiType.SMART: @@ -3969,9 +4012,9 @@ export class EnemyPokemon extends Pokemon { * For more information on how benefit scores are calculated, see `docs/enemy-ai.md`. */ const moveScores = movePool.map(() => 0); - const moveTargets = Object.fromEntries(movePool.map(m => [ m.moveId, this.getNextTargets(m.moveId) ])); + const moveTargets = Object.fromEntries(movePool.map(m => [ m!.moveId, this.getNextTargets(m!.moveId) ])); // TODO: are those bangs correct? for (const m in movePool) { - const pokemonMove = movePool[m]; + const pokemonMove = movePool[m]!; // TODO: is the bang correct? const move = pokemonMove.getMove(); let moveScore = moveScores[m]; @@ -4052,8 +4095,8 @@ export class EnemyPokemon extends Pokemon { r++; } } - console.log(movePool.map(m => m.getName()), moveScores, r, sortedMovePool.map(m => m.getName())); - return { move: sortedMovePool[r].moveId, targets: moveTargets[sortedMovePool[r].moveId] }; + console.log(movePool.map(m => m!.getName()), moveScores, r, sortedMovePool.map(m => m!.getName())); // TODO: are those bangs correct? + return { move: sortedMovePool[r]!.moveId, targets: moveTargets[sortedMovePool[r]!.moveId] }; } } @@ -4116,7 +4159,7 @@ export class EnemyPokemon extends Pokemon { } const thresholds: integer[] = []; - let totalWeight: integer; + let totalWeight: integer = 0; targetWeights.reduce((total: integer, w: integer) => { total += w; thresholds.push(total); @@ -4130,7 +4173,7 @@ export class EnemyPokemon extends Pokemon { * is greater than that random number. */ const randValue = this.scene.randBattleSeedInt(totalWeight); - let targetIndex: integer; + let targetIndex: integer = 0; thresholds.every((t, i) => { if (randValue >= t) { @@ -4306,7 +4349,7 @@ export class EnemyPokemon extends Pokemon { addToParty(pokeballType: PokeballType) { const party = this.scene.getParty(); - let ret: PlayerPokemon = null; + let ret: PlayerPokemon | null = null; if (party.length < 6) { this.pokeball = pokeballType; @@ -4440,15 +4483,15 @@ export class PokemonSummonData { public abilitySuppressed: boolean = false; public abilitiesApplied: Abilities[] = []; - public speciesForm: PokemonSpeciesForm; + public speciesForm: PokemonSpeciesForm | null; public fusionSpeciesForm: PokemonSpeciesForm; public ability: Abilities = Abilities.NONE; public gender: Gender; public fusionGender: Gender; public stats: integer[]; - public moveset: PokemonMove[]; + public moveset: (PokemonMove | null)[]; // If not initialized this value will not be populated from save data. - public types: Type[] = null; + public types: Type[] = []; } export class PokemonBattleData { diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 107dfbe6831..1348749d964 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -121,7 +121,7 @@ export default class Trainer extends Phaser.GameObjects.Container { // Determine the title to include based on the configuration and includeTitle flag. let title = includeTitle && this.config.title ? this.config.title : null; - const evilTeamTitles = ["grunt", "admin", "sage"]; + const evilTeamTitles = ["grunt"]; if (this.name === "" && evilTeamTitles.some(t => name.toLocaleLowerCase().includes(t))) { // This is a evil team grunt so we localize it by only using the "name" as the title title = i18next.t(`trainerClasses:${name.toLowerCase().replace(/\s/g, "_")}`); @@ -165,6 +165,8 @@ export default class Trainer extends Phaser.GameObjects.Container { name = i18next.t(`trainerNames:${this.config.nameDouble.toLowerCase().replace(/\s/g, "_")}`); } + console.log(title ? `${title} ${name}` : name); + // Return the formatted name, including the title if it is set. return title ? `${title} ${name}` : name; } @@ -206,7 +208,7 @@ export default class Trainer extends Phaser.GameObjects.Container { } getPartyLevels(waveIndex: integer): integer[] { - const ret = []; + const ret: number[] = []; const partyTemplate = this.getPartyTemplate(); const difficultyWaveIndex = this.scene.gameMode.getWaveForDifficulty(waveIndex); @@ -255,7 +257,7 @@ export default class Trainer extends Phaser.GameObjects.Container { genPartyMember(index: integer): EnemyPokemon { const battle = this.scene.currentBattle; - const level = battle.enemyLevels[index]; + const level = battle.enemyLevels?.[index]!; // TODO: is this bang correct? let ret: EnemyPokemon; @@ -288,7 +290,7 @@ export default class Trainer extends Phaser.GameObjects.Container { } // Create an empty species pool (which will be set to one of the species pools based on the index) - let newSpeciesPool = []; + let newSpeciesPool: Species[] = []; let useNewSpeciesPool = false; // If we are in a double battle of named trainers, we need to use alternate species pools (generate half the party from each trainer) @@ -313,7 +315,7 @@ export default class Trainer extends Phaser.GameObjects.Container { return !species.some(s => AlreadyUsedSpecies.includes(s)); } return !AlreadyUsedSpecies.includes(species); - }); + }).flat(); // Filter out the species that are already in the enemy party from the partner trainer species pool const speciesPoolPartnerFiltered = speciesPoolPartner.filter(species => { @@ -322,7 +324,7 @@ export default class Trainer extends Phaser.GameObjects.Container { return !species.some(s => AlreadyUsedSpecies.includes(s)); } return !AlreadyUsedSpecies.includes(species); - }); + }).flat(); // If the index is even, use the species pool for the main trainer (that way he only uses his own pokemon in battle) @@ -368,7 +370,7 @@ export default class Trainer extends Phaser.GameObjects.Container { ret = this.scene.addEnemyPokemon(species, level, !this.isDouble() || !(index % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); }, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : this.scene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8)); - return ret; + return ret!; // TODO: is this bang correct? } @@ -479,7 +481,7 @@ export default class Trainer extends Phaser.GameObjects.Container { if (maxScorePartyMemberIndexes.length > 1) { let rand: integer; this.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length), this.scene.currentBattle.turn << 2); - return maxScorePartyMemberIndexes[rand]; + return maxScorePartyMemberIndexes[rand!]; } return maxScorePartyMemberIndexes[0]; @@ -497,6 +499,9 @@ export default class Trainer extends Phaser.GameObjects.Container { return 0.45; case PartyMemberStrength.STRONGER: return 0.375; + default: + console.warn("getPartyMemberModifierChanceMultiplier not defined. Using default 0"); + return 0; } } diff --git a/src/game-mode.ts b/src/game-mode.ts index 010cae03e5e..2475cb5cfb4 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -167,7 +167,7 @@ export class GameMode implements GameModeConfig { } } - getOverrideSpecies(waveIndex: integer): PokemonSpecies { + getOverrideSpecies(waveIndex: integer): PokemonSpecies | null { if (this.isDaily && this.isWaveFinal(waveIndex)) { const allFinalBossSpecies = allSpecies.filter(s => (s.subLegendary || s.legendary || s.mythical) && s.baseTotal >= 600 && s.speciesId !== Species.ETERNATUS && s.speciesId !== Species.ARCEUS); @@ -210,7 +210,7 @@ export class GameMode implements GameModeConfig { * @returns true if waveIndex is a multiple of 50 in Endless */ isEndlessBoss(waveIndex: integer): boolean { - return waveIndex % 50 && + return !!(waveIndex % 50) && (this.modeId === GameModes.ENDLESS || this.modeId === GameModes.SPLICED_ENDLESS); } @@ -267,6 +267,8 @@ export class GameMode implements GameModeConfig { return 5000; case GameModes.DAILY: return 2500; + default: + return 0; } } diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index 834ca233942..331d491b807 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -154,7 +154,7 @@ export class InputsController { }); if (typeof this.scene.input.gamepad !== "undefined") { - this.scene.input.gamepad.on("connected", function (thisGamepad) { + this.scene.input.gamepad?.on("connected", function (thisGamepad) { if (!thisGamepad) { return; } @@ -163,23 +163,23 @@ export class InputsController { this.onReconnect(thisGamepad); }, this); - this.scene.input.gamepad.on("disconnected", function (thisGamepad) { + this.scene.input.gamepad?.on("disconnected", function (thisGamepad) { this.onDisconnect(thisGamepad); // when a gamepad is disconnected }, this); // Check to see if the gamepad has already been setup by the browser - this.scene.input.gamepad.refreshPads(); - if (this.scene.input.gamepad.total) { + this.scene.input.gamepad?.refreshPads(); + if (this.scene.input.gamepad?.total) { this.refreshGamepads(); for (const thisGamepad of this.gamepads) { this.scene.input.gamepad.emit("connected", thisGamepad); } } - this.scene.input.gamepad.on("down", this.gamepadButtonDown, this); - this.scene.input.gamepad.on("up", this.gamepadButtonUp, this); - this.scene.input.keyboard.on("keydown", this.keyboardKeyDown, this); - this.scene.input.keyboard.on("keyup", this.keyboardKeyUp, this); + this.scene.input.gamepad?.on("down", this.gamepadButtonDown, this); + this.scene.input.gamepad?.on("up", this.gamepadButtonUp, this); + this.scene.input.keyboard?.on("keydown", this.keyboardKeyDown, this); + this.scene.input.keyboard?.on("keyup", this.keyboardKeyUp, this); } this.touchControls = new TouchControl(this.scene); } @@ -338,9 +338,9 @@ export class InputsController { */ refreshGamepads(): void { // Sometimes, gamepads are undefined. For some reason. - this.gamepads = this.scene.input.gamepad.gamepads.filter(function (el) { + this.gamepads = this.scene.input.gamepad?.gamepads.filter(function (el) { return el !== null; - }); + })!; // TODO: is this bang correct? for (const [index, thisGamepad] of this.gamepads.entries()) { thisGamepad.index = index; // Overwrite the gamepad index, in case we had undefined gamepads earlier diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 5275411055e..c00112318c8 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -453,8 +453,8 @@ export class LoadingScene extends SceneBase { // videos do not need to be preloaded intro.loadURL("images/intro_dark.mp4", true); if (mobile) { - intro.video.setAttribute("webkit-playsinline", "webkit-playsinline"); - intro.video.setAttribute("playsinline", "playsinline"); + intro.video?.setAttribute("webkit-playsinline", "webkit-playsinline"); + intro.video?.setAttribute("playsinline", "playsinline"); } intro.play(); }); diff --git a/src/locales/ca-ES/ability-trigger.ts b/src/locales/ca-ES/ability-trigger.ts new file mode 100644 index 00000000000..ce41a964922 --- /dev/null +++ b/src/locales/ca-ES/ability-trigger.ts @@ -0,0 +1,63 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const abilityTriggers: SimpleTranslationEntries = { + "blockRecoilDamage": "{{pokemonName}}'s {{abilityName}}\nprotected it from recoil!", + "badDreams": "{{pokemonName}} is tormented!", + "costar": "{{pokemonName}} copied {{allyName}}'s stat changes!", + "iceFaceAvoidedDamage": "{{pokemonName}} avoided\ndamage with {{abilityName}}!", + "perishBody": "{{pokemonName}}'s {{abilityName}}\nwill faint both pokemon in 3 turns!", + "poisonHeal": "{{pokemonName}}'s {{abilityName}}\nrestored its HP a little!", + "trace": "{{pokemonName}} copied {{targetName}}'s\n{{abilityName}}!", + "windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!", + "quickDraw": "{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", + "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", + "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", + "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", + "postDefendDisguise": "{{pokemonNameWithAffix}}'s disguise was busted!", + "moveImmunity": "It doesn't affect {{pokemonNameWithAffix}}!", + "reverseDrain": "{{pokemonNameWithAffix}} sucked up the liquid ooze!", + "postDefendTypeChange": "{{pokemonNameWithAffix}}'s {{abilityName}}\nmade it the {{typeName}} type!", + "postDefendContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", + "postDefendAbilitySwap": "{{pokemonNameWithAffix}} swapped\nabilities with its target!", + "postDefendAbilityGive": "{{pokemonNameWithAffix}} gave its target\n{{abilityName}}!", + "postDefendMoveDisable": "{{pokemonNameWithAffix}}'s {{moveName}}\nwas disabled!", + "pokemonTypeChange": "{{pokemonNameWithAffix}} transformed into the {{moveType}} type!", + "postAttackStealHeldItem": "{{pokemonNameWithAffix}} stole\n{{defenderName}}'s {{stolenItemType}}!", + "postDefendStealHeldItem": "{{pokemonNameWithAffix}} stole\n{{attackerName}}'s {{stolenItemType}}!", + "copyFaintedAllyAbility": "{{pokemonNameWithAffix}}'s {{abilityName}} was taken over!", + "intimidateImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}} prevented it from being Intimidated!", + "postSummonAllyHeal": "{{pokemonNameWithAffix}} drank down all the\nmatcha that {{pokemonName}} made!", + "postSummonClearAllyStats": "{{pokemonNameWithAffix}}'s stat changes\nwere removed!", + "postSummonTransform": "{{pokemonNameWithAffix}} transformed\ninto {{targetName}}!", + "protectStat": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents lowering its {{statName}}!", + "statusEffectImmunityWithName": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents {{statusEffectName}}!", + "statusEffectImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents status problems!", + "battlerTagImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents {{battlerTagName}}!", + "forewarn": "{{pokemonNameWithAffix}} was forewarned about {{moveName}}!", + "frisk": "{{pokemonNameWithAffix}} frisked {{opponentName}}'s {{opponentAbilityName}}!", + "postWeatherLapseHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", + "postWeatherLapseDamage": "{{pokemonNameWithAffix}} is hurt\nby its {{abilityName}}!", + "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}} harvested one {{berryName}}!", + "postTurnHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", + "fetchBall": "{{pokemonNameWithAffix}} found a\n{{pokeballName}}!", + "healFromBerryUse": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP!", + "arenaTrap": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents switching!", + "postBattleLoot": "{{pokemonNameWithAffix}} picked up\n{{itemName}}!", + "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", + "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", + "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", + "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", + "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", + "postSummonTeravolt": "{{pokemonNameWithAffix}} is radiating a bursting aura!", + "postSummonDarkAura": "{{pokemonNameWithAffix}} is radiating a Dark Aura!", + "postSummonFairyAura": "{{pokemonNameWithAffix}} is radiating a Fairy Aura!", + "postSummonNeutralizingGas": "{{pokemonNameWithAffix}}'s Neutralizing Gas filled the area!", + "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} has two Abilities!", + "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} has two Abilities!", + "postSummonVesselOfRuin": "{{pokemonNameWithAffix}}'s Vessel of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "postSummonSwordOfRuin": "{{pokemonNameWithAffix}}'s Sword of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}'s Tablets of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}'s Beads of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "preventBerryUse": "{{pokemonNameWithAffix}} is too\nnervous to eat berries!", +} as const; diff --git a/src/locales/ca-ES/ability.ts b/src/locales/ca-ES/ability.ts new file mode 100644 index 00000000000..7e81f90afff --- /dev/null +++ b/src/locales/ca-ES/ability.ts @@ -0,0 +1,1244 @@ +import { AbilityTranslationEntries } from "#app/interfaces/locales.js"; + +export const ability: AbilityTranslationEntries = { + stench: { + name: "Stench", + description: "By releasing stench when attacking, this Pokémon may cause the target to flinch.", + }, + drizzle: { + name: "Drizzle", + description: "The Pokémon makes it rain when it enters a battle.", + }, + speedBoost: { + name: "Speed Boost", + description: "Its Speed stat is boosted every turn.", + }, + battleArmor: { + name: "Battle Armor", + description: "Hard armor protects the Pokémon from critical hits.", + }, + sturdy: { + name: "Sturdy", + description: "It cannot be knocked out with one hit. One-hit KO moves cannot knock it out, either.", + }, + damp: { + name: "Damp", + description: "Prevents the use of explosive moves, such as Self-Destruct, by dampening its surroundings.", + }, + limber: { + name: "Limber", + description: "Its limber body protects the Pokémon from paralysis.", + }, + sandVeil: { + name: "Sand Veil", + description: "Boosts the Pokémon's evasiveness in a sandstorm.", + }, + static: { + name: "Static", + description: "The Pokémon is charged with static electricity, so contact with it may cause paralysis.", + }, + voltAbsorb: { + name: "Volt Absorb", + description: "Restores HP if hit by an Electric-type move instead of taking damage.", + }, + waterAbsorb: { + name: "Water Absorb", + description: "Restores HP if hit by a Water-type move instead of taking damage.", + }, + oblivious: { + name: "Oblivious", + description: "The Pokémon is oblivious, and that keeps it from being infatuated or falling for taunts.", + }, + cloudNine: { + name: "Cloud Nine", + description: "Eliminates the effects of weather.", + }, + compoundEyes: { + name: "Compound Eyes", + description: "The Pokémon's compound eyes boost its accuracy.", + }, + insomnia: { + name: "Insomnia", + description: "The Pokémon is suffering from insomnia and cannot fall asleep.", + }, + colorChange: { + name: "Color Change", + description: "The Pokémon's type becomes the type of the move used on it.", + }, + immunity: { + name: "Immunity", + description: "The immune system of the Pokémon prevents it from getting poisoned.", + }, + flashFire: { + name: "Flash Fire", + description: "Powers up the Pokémon's Fire-type moves if it's hit by one.", + }, + shieldDust: { + name: "Shield Dust", + description: "This Pokémon's dust blocks the additional effects of attacks taken.", + }, + ownTempo: { + name: "Own Tempo", + description: "This Pokémon has its own tempo, and that prevents it from becoming confused.", + }, + suctionCups: { + name: "Suction Cups", + description: "This Pokémon uses suction cups to stay in one spot to negate all moves and items that force switching out.", + }, + intimidate: { + name: "Intimidate", + description: "The Pokémon intimidates opposing Pokémon upon entering battle, lowering their Attack stat.", + }, + shadowTag: { + name: "Shadow Tag", + description: "This Pokémon steps on the opposing Pokémon's shadow to prevent it from escaping.", + }, + roughSkin: { + name: "Rough Skin", + description: "This Pokémon inflicts damage with its rough skin to the attacker on contact.", + }, + wonderGuard: { + name: "Wonder Guard", + description: "Its mysterious power only lets supereffective moves hit the Pokémon.", + }, + levitate: { + name: "Levitate", + description: "By floating in the air, the Pokémon receives full immunity to all Ground-type moves.", + }, + effectSpore: { + name: "Effect Spore", + description: "Contact with the Pokémon may inflict poison, sleep, or paralysis on its attacker.", + }, + synchronize: { + name: "Synchronize", + description: "The attacker will receive the same status condition if it inflicts a burn, poison, or paralysis to the Pokémon.", + }, + clearBody: { + name: "Clear Body", + description: "Prevents other Pokémon's moves or Abilities from lowering the Pokémon's stats.", + }, + naturalCure: { + name: "Natural Cure", + description: "All status conditions heal when the Pokémon switches out.", + }, + lightningRod: { + name: "Lightning Rod", + description: "The Pokémon draws in all Electric-type moves. Instead of being hit by Electric-type moves, it boosts its Sp. Atk.", + }, + sereneGrace: { + name: "Serene Grace", + description: "Boosts the likelihood of additional effects occurring when attacking.", + }, + swiftSwim: { + name: "Swift Swim", + description: "Boosts the Pokémon's Speed stat in rain.", + }, + chlorophyll: { + name: "Chlorophyll", + description: "Boosts the Pokémon's Speed stat in harsh sunlight.", + }, + illuminate: { + name: "Illuminate", + description: "By illuminating its surroundings, the Pokémon raises the likelihood of meeting wild Pokémon and prevents its accuracy from being lowered.", + }, + trace: { + name: "Trace", + description: "When it enters a battle, the Pokémon copies an opposing Pokémon's Ability.", + }, + hugePower: { + name: "Huge Power", + description: "Doubles the Pokémon's Attack stat.", + }, + poisonPoint: { + name: "Poison Point", + description: "Contact with the Pokémon may poison the attacker.", + }, + innerFocus: { + name: "Inner Focus", + description: "The Pokémon's intensely focused, and that protects the Pokémon from flinching.", + }, + magmaArmor: { + name: "Magma Armor", + description: "The Pokémon is covered with hot magma, which prevents the Pokémon from becoming frozen.", + }, + waterVeil: { + name: "Water Veil", + description: "The Pokémon is covered with a water veil, which prevents the Pokémon from getting a burn.", + }, + magnetPull: { + name: "Magnet Pull", + description: "Prevents Steel-type Pokémon from escaping using its magnetic force.", + }, + soundproof: { + name: "Soundproof", + description: "Soundproofing gives the Pokémon full immunity to all sound-based moves.", + }, + rainDish: { + name: "Rain Dish", + description: "The Pokémon gradually regains HP in rain.", + }, + sandStream: { + name: "Sand Stream", + description: "The Pokémon summons a sandstorm when it enters a battle.", + }, + pressure: { + name: "Pressure", + description: "By putting pressure on the opposing Pokémon, it raises their PP usage.", + }, + thickFat: { + name: "Thick Fat", + description: "The Pokémon is protected by a layer of thick fat, which halves the damage taken from Fire- and Ice-type moves.", + }, + earlyBird: { + name: "Early Bird", + description: "The Pokémon awakens from sleep twice as fast as other Pokémon.", + }, + flameBody: { + name: "Flame Body", + description: "Contact with the Pokémon may burn the attacker.", + }, + runAway: { + name: "Run Away", + description: "Enables a sure getaway from wild Pokémon.", + }, + keenEye: { + name: "Keen Eye", + description: "Keen eyes prevent other Pokémon from lowering this Pokémon's accuracy.", + }, + hyperCutter: { + name: "Hyper Cutter", + description: "The Pokémon's proud of its powerful pincers. They prevent other Pokémon from lowering its Attack stat.", + }, + pickup: { + name: "Pickup", + description: "The Pokémon may pick up the item an opposing Pokémon held during a battle.", + }, + truant: { + name: "Truant", + description: "The Pokémon can't use a move if it had used a move on the previous turn.", + }, + hustle: { + name: "Hustle", + description: "Boosts the Attack stat, but lowers accuracy.", + }, + cuteCharm: { + name: "Cute Charm", + description: "Contact with the Pokémon may cause infatuation.", + }, + plus: { + name: "Plus", + description: "Boosts the Sp. Atk stat of the Pokémon if an ally with the Plus or Minus Ability is also in battle.", + }, + minus: { + name: "Minus", + description: "Boosts the Sp. Atk stat of the Pokémon if an ally with the Plus or Minus Ability is also in battle.", + }, + forecast: { + name: "Forecast", + description: "The Pokémon transforms with the weather to change its type to Water, Fire, or Ice.", + }, + stickyHold: { + name: "Sticky Hold", + description: "Items held by the Pokémon are stuck fast and cannot be removed by other Pokémon.", + }, + shedSkin: { + name: "Shed Skin", + description: "The Pokémon may heal its own status conditions by shedding its skin.", + }, + guts: { + name: "Guts", + description: "It's so gutsy that having a status condition boosts the Pokémon's Attack stat.", + }, + marvelScale: { + name: "Marvel Scale", + description: "The Pokémon's marvelous scales boost the Defense stat if it has a status condition.", + }, + liquidOoze: { + name: "Liquid Ooze", + description: "The oozed liquid has a strong stench, which damages attackers using any draining move.", + }, + overgrow: { + name: "Overgrow", + description: "Powers up Grass-type moves when the Pokémon's HP is low.", + }, + blaze: { + name: "Blaze", + description: "Powers up Fire-type moves when the Pokémon's HP is low.", + }, + torrent: { + name: "Torrent", + description: "Powers up Water-type moves when the Pokémon's HP is low.", + }, + swarm: { + name: "Swarm", + description: "Powers up Bug-type moves when the Pokémon's HP is low.", + }, + rockHead: { + name: "Rock Head", + description: "Protects the Pokémon from recoil damage.", + }, + drought: { + name: "Drought", + description: "Turns the sunlight harsh when the Pokémon enters a battle.", + }, + arenaTrap: { + name: "Arena Trap", + description: "Prevents opposing Pokémon from fleeing.", + }, + vitalSpirit: { + name: "Vital Spirit", + description: "The Pokémon is full of vitality, and that prevents it from falling asleep.", + }, + whiteSmoke: { + name: "White Smoke", + description: "The Pokémon is protected by its white smoke, which prevents other Pokémon from lowering its stats.", + }, + purePower: { + name: "Pure Power", + description: "Using its pure power, the Pokémon doubles its Attack stat.", + }, + shellArmor: { + name: "Shell Armor", + description: "A hard shell protects the Pokémon from critical hits.", + }, + airLock: { + name: "Air Lock", + description: "Eliminates the effects of weather.", + }, + tangledFeet: { + name: "Tangled Feet", + description: "Raises evasiveness if the Pokémon is confused.", + }, + motorDrive: { + name: "Motor Drive", + description: "Boosts its Speed stat if hit by an Electric-type move instead of taking damage.", + }, + rivalry: { + name: "Rivalry", + description: "Becomes competitive and deals more damage to Pokémon of the same gender, but deals less to Pokémon of the opposite gender.", + }, + steadfast: { + name: "Steadfast", + description: "The Pokémon's determination boosts the Speed stat each time the Pokémon flinches.", + }, + snowCloak: { + name: "Snow Cloak", + description: "Boosts the Pokémon's evasiveness in snow.", + }, + gluttony: { + name: "Gluttony", + description: "Makes the Pokémon eat a held Berry when its HP drops to half or less, which is sooner than usual.", + }, + angerPoint: { + name: "Anger Point", + description: "The Pokémon is angered when it takes a critical hit, and that maxes its Attack stat.", + }, + unburden: { + name: "Unburden", + description: "Boosts the Speed stat if the Pokémon's held item is used or lost.", + }, + heatproof: { + name: "Heatproof", + description: "The heatproof body of the Pokémon halves the damage from Fire-type moves that hit it.", + }, + simple: { + name: "Simple", + description: "The stat changes the Pokémon receives are doubled.", + }, + drySkin: { + name: "Dry Skin", + description: "Restores HP in rain or when hit by Water-type moves. Reduces HP in harsh sunlight, and increases the damage received from Fire-type moves.", + }, + download: { + name: "Download", + description: "Compares an opposing Pokémon's Defense and Sp. Def stats before raising its own Attack or Sp. Atk stat—whichever will be more effective.", + }, + ironFist: { + name: "Iron Fist", + description: "Powers up punching moves.", + }, + poisonHeal: { + name: "Poison Heal", + description: "Restores HP if the Pokémon is poisoned instead of losing HP.", + }, + adaptability: { + name: "Adaptability", + description: "Powers up moves of the same type as the Pokémon.", + }, + skillLink: { + name: "Skill Link", + description: "Maximizes the number of times multistrike moves hit.", + }, + hydration: { + name: "Hydration", + description: "Heals status conditions if it's raining.", + }, + solarPower: { + name: "Solar Power", + description: "Boosts the Sp. Atk stat in harsh sunlight, but HP decreases every turn.", + }, + quickFeet: { + name: "Quick Feet", + description: "Boosts the Speed stat if the Pokémon has a status condition.", + }, + normalize: { + name: "Normalize", + description: "All the Pokémon's moves become Normal type. The power of those moves is boosted a little.", + }, + sniper: { + name: "Sniper", + description: "Powers up moves if they become critical hits when attacking.", + }, + magicGuard: { + name: "Magic Guard", + description: "The Pokémon only takes damage from attacks.", + }, + noGuard: { + name: "No Guard", + description: "The Pokémon employs no-guard tactics to ensure incoming and outgoing attacks always land.", + }, + stall: { + name: "Stall", + description: "The Pokémon moves after all other Pokémon do.", + }, + technician: { + name: "Technician", + description: "Powers up the Pokémon's weaker moves.", + }, + leafGuard: { + name: "Leaf Guard", + description: "Prevents status conditions in harsh sunlight.", + }, + klutz: { + name: "Klutz", + description: "The Pokémon can't use any held items.", + }, + moldBreaker: { + name: "Mold Breaker", + description: "Moves can be used on the target regardless of its Abilities.", + }, + superLuck: { + name: "Super Luck", + description: "The Pokémon is so lucky that the critical-hit ratios of its moves are boosted.", + }, + aftermath: { + name: "Aftermath", + description: "Damages the attacker if it contacts the Pokémon with a finishing hit.", + }, + anticipation: { + name: "Anticipation", + description: "The Pokémon can sense an opposing Pokémon's dangerous moves.", + }, + forewarn: { + name: "Forewarn", + description: "When it enters a battle, the Pokémon can tell one of the moves an opposing Pokémon has.", + }, + unaware: { + name: "Unaware", + description: "When attacking, the Pokémon ignores the target Pokémon's stat changes.", + }, + tintedLens: { + name: "Tinted Lens", + description: "The Pokémon can use \"not very effective\" moves to deal regular damage.", + }, + filter: { + name: "Filter", + description: "Reduces the power of supereffective attacks taken.", + }, + slowStart: { + name: "Slow Start", + description: "For five turns, the Pokémon's Attack and Speed stats are halved.", + }, + scrappy: { + name: "Scrappy", + description: "The Pokémon can hit Ghost-type Pokémon with Normal- and Fighting-type moves.", + }, + stormDrain: { + name: "Storm Drain", + description: "Draws in all Water-type moves. Instead of being hit by Water-type moves, it boosts its Sp. Atk.", + }, + iceBody: { + name: "Ice Body", + description: "The Pokémon gradually regains HP in snow.", + }, + solidRock: { + name: "Solid Rock", + description: "Reduces the power of supereffective attacks taken.", + }, + snowWarning: { + name: "Snow Warning", + description: "The Pokémon makes it snow when it enters a battle.", + }, + honeyGather: { + name: "Honey Gather", + description: "The Pokémon gathers Honey after a battle. The Honey is then sold for money.", + }, + frisk: { + name: "Frisk", + description: "When it enters a battle, the Pokémon can check an opposing Pokémon's Ability.", + }, + reckless: { + name: "Reckless", + description: "Powers up moves that have recoil damage.", + }, + multitype: { + name: "Multitype", + description: "Changes the Pokémon's type to match the Plate or Z-Crystal it holds.", + }, + flowerGift: { + name: "Flower Gift", + description: "Boosts the Attack and Sp. Def stats of itself and allies in harsh sunlight.", + }, + badDreams: { + name: "Bad Dreams", + description: "Reduces the HP of sleeping opposing Pokémon.", + }, + pickpocket: { + name: "Pickpocket", + description: "Steals an item from an attacker that made direct contact.", + }, + sheerForce: { + name: "Sheer Force", + description: "Removes additional effects to increase the power of moves when attacking.", + }, + contrary: { + name: "Contrary", + description: "Makes stat changes have an opposite effect.", + }, + unnerve: { + name: "Unnerve", + description: "Unnerves opposing Pokémon and makes them unable to eat Berries.", + }, + defiant: { + name: "Defiant", + description: "Boosts the Pokémon's Attack stat sharply when its stats are lowered.", + }, + defeatist: { + name: "Defeatist", + description: "Halves the Pokémon's Attack and Sp. Atk stats when its HP becomes half or less.", + }, + cursedBody: { + name: "Cursed Body", + description: "May disable a move used on the Pokémon.", + }, + healer: { + name: "Healer", + description: "Sometimes heals an ally's status condition.", + }, + friendGuard: { + name: "Friend Guard", + description: "Reduces damage done to allies.", + }, + weakArmor: { + name: "Weak Armor", + description: "Physical attacks to the Pokémon lower its Defense stat but sharply raise its Speed stat.", + }, + heavyMetal: { + name: "Heavy Metal", + description: "Doubles the Pokémon's weight.", + }, + lightMetal: { + name: "Light Metal", + description: "Halves the Pokémon's weight.", + }, + multiscale: { + name: "Multiscale", + description: "Reduces the amount of damage the Pokémon takes while its HP is full.", + }, + toxicBoost: { + name: "Toxic Boost", + description: "Powers up physical attacks when the Pokémon is poisoned.", + }, + flareBoost: { + name: "Flare Boost", + description: "Powers up special attacks when the Pokémon is burned.", + }, + harvest: { + name: "Harvest", + description: "May create another Berry after one is used.", + }, + telepathy: { + name: "Telepathy", + description: "Anticipates an ally's attack and dodges it.", + }, + moody: { + name: "Moody", + description: "Raises one stat sharply and lowers another every turn.", + }, + overcoat: { + name: "Overcoat", + description: "Protects the Pokémon from things like sand, hail, and powder.", + }, + poisonTouch: { + name: "Poison Touch", + description: "May poison a target when the Pokémon makes contact.", + }, + regenerator: { + name: "Regenerator", + description: "Restores a little HP when withdrawn from battle.", + }, + bigPecks: { + name: "Big Pecks", + description: "Protects the Pokémon from Defense-lowering effects.", + }, + sandRush: { + name: "Sand Rush", + description: "Boosts the Pokémon's Speed stat in a sandstorm.", + }, + wonderSkin: { + name: "Wonder Skin", + description: "Makes status moves more likely to miss.", + }, + analytic: { + name: "Analytic", + description: "Boosts move power when the Pokémon moves last.", + }, + illusion: { + name: "Illusion", + description: "Comes out disguised as the Pokémon in the party's last spot.", + }, + imposter: { + name: "Imposter", + description: "The Pokémon transforms itself into the Pokémon it's facing.", + }, + infiltrator: { + name: "Infiltrator", + description: "Passes through the opposing Pokémon's barrier, substitute, and the like and strikes.", + }, + mummy: { + name: "Mummy", + description: "Contact with the Pokémon changes the attacker's Ability to Mummy.", + }, + moxie: { + name: "Moxie", + description: "The Pokémon shows moxie, and that boosts the Attack stat after knocking out any Pokémon.", + }, + justified: { + name: "Justified", + description: "Being hit by a Dark-type move boosts the Attack stat of the Pokémon, for justice.", + }, + rattled: { + name: "Rattled", + description: "Intimidate or being hit by a Dark-, Ghost-, or Bug-type move will scare the Pokémon and boost its Speed stat.", + }, + magicBounce: { + name: "Magic Bounce", + description: "Reflects status moves instead of getting hit by them.", + }, + sapSipper: { + name: "Sap Sipper", + description: "Boosts the Attack stat if hit by a Grass-type move instead of taking damage.", + }, + prankster: { + name: "Prankster", + description: "Gives priority to a status move.", + }, + sandForce: { + name: "Sand Force", + description: "Boosts the power of Rock-, Ground-, and Steel-type moves in a sandstorm.", + }, + ironBarbs: { + name: "Iron Barbs", + description: "Inflicts damage on the attacker upon contact with iron barbs.", + }, + zenMode: { + name: "Zen Mode", + description: "Changes the Pokémon's shape when HP is half or less.", + }, + victoryStar: { + name: "Victory Star", + description: "Boosts the accuracy of its allies and itself.", + }, + turboblaze: { + name: "Turboblaze", + description: "Moves can be used on the target regardless of its Abilities.", + }, + teravolt: { + name: "Teravolt", + description: "Moves can be used on the target regardless of its Abilities.", + }, + aromaVeil: { + name: "Aroma Veil", + description: "Protects itself and its allies from attacks that limit their move choices.", + }, + flowerVeil: { + name: "Flower Veil", + description: "Ally Grass-type Pokémon are protected from status conditions and the lowering of their stats.", + }, + cheekPouch: { + name: "Cheek Pouch", + description: "Restores HP as well when the Pokémon eats a Berry.", + }, + protean: { + name: "Protean", + description: "Changes the Pokémon's type to the type of the move it's about to use.", + }, + furCoat: { + name: "Fur Coat", + description: "Halves the damage from physical moves.", + }, + magician: { + name: "Magician", + description: "The Pokémon steals the held item of a Pokémon it hits with a move.", + }, + bulletproof: { + name: "Bulletproof", + description: "Protects the Pokémon from some ball and bomb moves.", + }, + competitive: { + name: "Competitive", + description: "Boosts the Sp. Atk stat sharply when a stat is lowered.", + }, + strongJaw: { + name: "Strong Jaw", + description: "The Pokémon's strong jaw boosts the power of its biting moves.", + }, + refrigerate: { + name: "Refrigerate", + description: "Normal-type moves become Ice-type moves. The power of those moves is boosted a little.", + }, + sweetVeil: { + name: "Sweet Veil", + description: "Prevents itself and ally Pokémon from falling asleep.", + }, + stanceChange: { + name: "Stance Change", + description: "The Pokémon changes its form to Blade Forme when it uses an attack move and changes to Shield Forme when it uses King's Shield.", + }, + galeWings: { + name: "Gale Wings", + description: "Gives priority to Flying-type moves when the Pokémon's HP is full.", + }, + megaLauncher: { + name: "Mega Launcher", + description: "Powers up aura and pulse moves.", + }, + grassPelt: { + name: "Grass Pelt", + description: "Boosts the Pokémon's Defense stat on Grassy Terrain.", + }, + symbiosis: { + name: "Symbiosis", + description: "The Pokémon passes its item to an ally that has used up an item.", + }, + toughClaws: { + name: "Tough Claws", + description: "Powers up moves that make direct contact.", + }, + pixilate: { + name: "Pixilate", + description: "Normal-type moves become Fairy-type moves. The power of those moves is boosted a little.", + }, + gooey: { + name: "Gooey", + description: "Contact with the Pokémon lowers the attacker's Speed stat.", + }, + aerilate: { + name: "Aerilate", + description: "Normal-type moves become Flying-type moves. The power of those moves is boosted a little.", + }, + parentalBond: { + name: "Parental Bond", + description: "Parent and child each attacks.", + }, + darkAura: { + name: "Dark Aura", + description: "Powers up each Pokémon's Dark-type moves.", + }, + fairyAura: { + name: "Fairy Aura", + description: "Powers up each Pokémon's Fairy-type moves.", + }, + auraBreak: { + name: "Aura Break", + description: "The effects of \"Aura\" Abilities are reversed to lower the power of affected moves.", + }, + primordialSea: { + name: "Primordial Sea", + description: "The Pokémon changes the weather to nullify Fire-type attacks.", + }, + desolateLand: { + name: "Desolate Land", + description: "The Pokémon changes the weather to nullify Water-type attacks.", + }, + deltaStream: { + name: "Delta Stream", + description: "The Pokémon changes the weather to eliminate all of the Flying type's weaknesses.", + }, + stamina: { + name: "Stamina", + description: "Boosts the Defense stat when hit by an attack.", + }, + wimpOut: { + name: "Wimp Out", + description: "The Pokémon cowardly switches out when its HP becomes half or less.", + }, + emergencyExit: { + name: "Emergency Exit", + description: "The Pokémon, sensing danger, switches out when its HP becomes half or less.", + }, + waterCompaction: { + name: "Water Compaction", + description: "Boosts the Pokémon's Defense stat sharply when hit by a Water-type move.", + }, + merciless: { + name: "Merciless", + description: "The Pokémon's attacks become critical hits if the target is poisoned.", + }, + shieldsDown: { + name: "Shields Down", + description: "When its HP becomes half or less, the Pokémon's shell breaks and it becomes aggressive.", + }, + stakeout: { + name: "Stakeout", + description: "Doubles the damage dealt to the target's replacement if the target switches out.", + }, + waterBubble: { + name: "Water Bubble", + description: "Lowers the power of Fire-type moves done to the Pokémon and prevents the Pokémon from getting a burn.", + }, + steelworker: { + name: "Steelworker", + description: "Powers up Steel-type moves.", + }, + berserk: { + name: "Berserk", + description: "Boosts the Pokémon's Sp. Atk stat when it takes a hit that causes its HP to become half or less.", + }, + slushRush: { + name: "Slush Rush", + description: "Boosts the Pokémon's Speed stat in snow.", + }, + longReach: { + name: "Long Reach", + description: "The Pokémon uses its moves without making contact with the target.", + }, + liquidVoice: { + name: "Liquid Voice", + description: "All sound-based moves become Water-type moves.", + }, + triage: { + name: "Triage", + description: "Gives priority to a healing move.", + }, + galvanize: { + name: "Galvanize", + description: "Normal-type moves become Electric-type moves. The power of those moves is boosted a little.", + }, + surgeSurfer: { + name: "Surge Surfer", + description: "Doubles the Pokémon's Speed stat on Electric Terrain.", + }, + schooling: { + name: "Schooling", + description: "When it has a lot of HP, the Pokémon forms a powerful school. It stops schooling when its HP is low.", + }, + disguise: { + name: "Disguise", + description: "Once per battle, the shroud that covers the Pokémon can protect it from an attack.", + }, + battleBond: { + name: "Battle Bond", + description: "Defeating an opposing Pokémon strengthens the Pokémon's bond with its Trainer, and it becomes Ash-Greninja. Water Shuriken gets more powerful.", + }, + powerConstruct: { + name: "Power Construct", + description: "Other Cells gather to aid when its HP becomes half or less. Then the Pokémon changes its form to Complete Forme.", + }, + corrosion: { + name: "Corrosion", + description: "The Pokémon can poison the target even if it's a Steel or Poison type.", + }, + comatose: { + name: "Comatose", + description: "It's always drowsing and will never wake up. It can attack without waking up.", + }, + queenlyMajesty: { + name: "Queenly Majesty", + description: "Its majesty pressures the opposing Pokémon, making it unable to attack using priority moves.", + }, + innardsOut: { + name: "Innards Out", + description: "Damages the attacker landing the finishing hit by the amount equal to its last HP.", + }, + dancer: { + name: "Dancer", + description: "When another Pokémon uses a dance move, it can use a dance move following it regardless of its Speed.", + }, + battery: { + name: "Battery", + description: "Powers up ally Pokémon's special moves.", + }, + fluffy: { + name: "Fluffy", + description: "Halves the damage taken from moves that make direct contact, but doubles that of Fire-type moves.", + }, + dazzling: { + name: "Dazzling", + description: "Surprises the opposing Pokémon, making it unable to attack using priority moves.", + }, + soulHeart: { + name: "Soul-Heart", + description: "Boosts its Sp. Atk stat every time a Pokémon faints.", + }, + tanglingHair: { + name: "Tangling Hair", + description: "Contact with the Pokémon lowers the attacker's Speed stat.", + }, + receiver: { + name: "Receiver", + description: "The Pokémon copies the Ability of a defeated ally.", + }, + powerOfAlchemy: { + name: "Power of Alchemy", + description: "The Pokémon copies the Ability of a defeated ally.", + }, + beastBoost: { + name: "Beast Boost", + description: "The Pokémon boosts its most proficient stat each time it knocks out a Pokémon.", + }, + rksSystem: { + name: "RKS System", + description: "Changes the Pokémon's type to match the memory disc it holds.", + }, + electricSurge: { + name: "Electric Surge", + description: "Turns the ground into Electric Terrain when the Pokémon enters a battle.", + }, + psychicSurge: { + name: "Psychic Surge", + description: "Turns the ground into Psychic Terrain when the Pokémon enters a battle.", + }, + mistySurge: { + name: "Misty Surge", + description: "Turns the ground into Misty Terrain when the Pokémon enters a battle.", + }, + grassySurge: { + name: "Grassy Surge", + description: "Turns the ground into Grassy Terrain when the Pokémon enters a battle.", + }, + fullMetalBody: { + name: "Full Metal Body", + description: "Prevents other Pokémon's moves or Abilities from lowering the Pokémon's stats.", + }, + shadowShield: { + name: "Shadow Shield", + description: "Reduces the amount of damage the Pokémon takes while its HP is full.", + }, + prismArmor: { + name: "Prism Armor", + description: "Reduces the power of supereffective attacks taken.", + }, + neuroforce: { + name: "Neuroforce", + description: "Powers up moves that are super effective.", + }, + intrepidSword: { + name: "Intrepid Sword", + description: "Boosts the Pokémon's Attack stat when the Pokémon enters a battle.", + }, + dauntlessShield: { + name: "Dauntless Shield", + description: "Boosts the Pokémon's Defense stat when the Pokémon enters a battle.", + }, + libero: { + name: "Libero", + description: "Changes the Pokémon's type to the type of the move it's about to use.", + }, + ballFetch: { + name: "Ball Fetch", + description: "The Pokémon will fetch the Poké Ball from the first failed throw of the battle.", + }, + cottonDown: { + name: "Cotton Down", + description: "When the Pokémon is hit by an attack, it scatters cotton fluff around and lowers the Speed stat of all Pokémon except itself.", + }, + propellerTail: { + name: "Propeller Tail", + description: "Ignores the effects of opposing Pokémon's Abilities and moves that draw in moves.", + }, + mirrorArmor: { + name: "Mirror Armor", + description: "Bounces back only the stat-lowering effects that the Pokémon receives.", + }, + gulpMissile: { + name: "Gulp Missile", + description: "When the Pokémon uses Surf or Dive, it will come back with prey. When it takes damage, it will spit out the prey to attack.", + }, + stalwart: { + name: "Stalwart", + description: "Ignores the effects of opposing Pokémon's Abilities and moves that draw in moves.", + }, + steamEngine: { + name: "Steam Engine", + description: "Boosts the Pokémon's Speed stat drastically if hit by a Fire- or Water-type move.", + }, + punkRock: { + name: "Punk Rock", + description: "Boosts the power of sound-based moves. The Pokémon also takes half the damage from these kinds of moves.", + }, + sandSpit: { + name: "Sand Spit", + description: "The Pokémon creates a sandstorm when it's hit by an attack.", + }, + iceScales: { + name: "Ice Scales", + description: "The Pokémon is protected by ice scales, which halve the damage taken from special moves.", + }, + ripen: { + name: "Ripen", + description: "Ripens Berries and doubles their effect.", + }, + iceFace: { + name: "Ice Face", + description: "The Pokémon's ice head can take a physical attack as a substitute, but the attack also changes the Pokémon's appearance. The ice will be restored when it hails.", + }, + powerSpot: { + name: "Power Spot", + description: "Just being next to the Pokémon powers up moves.", + }, + mimicry: { + name: "Mimicry", + description: "Changes the Pokémon's type depending on the terrain.", + }, + screenCleaner: { + name: "Screen Cleaner", + description: "When the Pokémon enters a battle, the effects of Light Screen, Reflect, and Aurora Veil are nullified for both opposing and ally Pokémon.", + }, + steelySpirit: { + name: "Steely Spirit", + description: "Powers up ally Pokémon's Steel-type moves.", + }, + perishBody: { + name: "Perish Body", + description: "When hit by a move that makes direct contact, the Pokémon and the attacker will faint after three turns unless they switch out of battle.", + }, + wanderingSpirit: { + name: "Wandering Spirit", + description: "The Pokémon exchanges Abilities with a Pokémon that hits it with a move that makes direct contact.", + }, + gorillaTactics: { + name: "Gorilla Tactics", + description: "Boosts the Pokémon's Attack stat but only allows the use of the first selected move.", + }, + neutralizingGas: { + name: "Neutralizing Gas", + description: "If the Pokémon with Neutralizing Gas is in the battle, the effects of all Pokémon's Abilities will be nullified or will not be triggered.", + }, + pastelVeil: { + name: "Pastel Veil", + description: "Protects the Pokémon and its ally Pokémon from being poisoned.", + }, + hungerSwitch: { + name: "Hunger Switch", + description: "The Pokémon changes its form, alternating between its Full Belly Mode and Hangry Mode after the end of each turn.", + }, + quickDraw: { + name: "Quick Draw", + description: "Enables the Pokémon to move first occasionally.", + }, + unseenFist: { + name: "Unseen Fist", + description: "If the Pokémon uses moves that make direct contact, it can attack the target even if the target protects itself.", + }, + curiousMedicine: { + name: "Curious Medicine", + description: "When the Pokémon enters a battle, it scatters medicine from its shell, which removes all stat changes from allies.", + }, + transistor: { + name: "Transistor", + description: "Powers up Electric-type moves.", + }, + dragonsMaw: { + name: "Dragon's Maw", + description: "Powers up Dragon-type moves.", + }, + chillingNeigh: { + name: "Chilling Neigh", + description: "When the Pokémon knocks out a target, it utters a chilling neigh, which boosts its Attack stat.", + }, + grimNeigh: { + name: "Grim Neigh", + description: "When the Pokémon knocks out a target, it utters a terrifying neigh, which boosts its Sp. Atk stat.", + }, + asOneGlastrier: { + name: "As One", + description: "This Ability combines the effects of both Calyrex's Unnerve Ability and Glastrier's Chilling Neigh Ability.", + }, + asOneSpectrier: { + name: "As One", + description: "This Ability combines the effects of both Calyrex's Unnerve Ability and Spectrier's Grim Neigh Ability.", + }, + lingeringAroma: { + name: "Lingering Aroma", + description: "Contact with the Pokémon changes the attacker's Ability to Lingering Aroma.", + }, + seedSower: { + name: "Seed Sower", + description: "Turns the ground into Grassy Terrain when the Pokémon is hit by an attack.", + }, + thermalExchange: { + name: "Thermal Exchange", + description: "Boosts the Attack stat when the Pokémon is hit by a Fire-type move. The Pokémon also cannot be burned.", + }, + angerShell: { + name: "Anger Shell", + description: "When an attack causes its HP to drop to half or less, the Pokémon gets angry. This lowers its Defense and Sp. Def stats but boosts its Attack, Sp. Atk, and Speed stats.", + }, + purifyingSalt: { + name: "Purifying Salt", + description: "The Pokémon's pure salt protects it from status conditions and halves the damage taken from Ghost-type moves.", + }, + wellBakedBody: { + name: "Well-Baked Body", + description: "The Pokémon takes no damage when hit by Fire-type moves. Instead, its Defense stat is sharply boosted.", + }, + windRider: { + name: "Wind Rider", + description: "Boosts the Pokémon's Attack stat if Tailwind takes effect or if the Pokémon is hit by a wind move. The Pokémon also takes no damage from wind moves.", + }, + guardDog: { + name: "Guard Dog", + description: "Boosts the Pokémon's Attack stat if intimidated. Moves and items that would force the Pokémon to switch out also fail to work.", + }, + rockyPayload: { + name: "Rocky Payload", + description: "Powers up Rock-type moves.", + }, + windPower: { + name: "Wind Power", + description: "The Pokémon becomes charged when it is hit by a wind move, boosting the power of the next Electric-type move the Pokémon uses.", + }, + zeroToHero: { + name: "Zero to Hero", + description: "The Pokémon transforms into its Hero Form when it switches out.", + }, + commander: { + name: "Commander", + description: "When the Pokémon enters a battle, it goes inside the mouth of an ally Dondozo if one is on the field. The Pokémon then issues commands from there.", + }, + electromorphosis: { + name: "Electromorphosis", + description: "The Pokémon becomes charged when it takes damage, boosting the power of the next Electric-type move the Pokémon uses.", + }, + protosynthesis: { + name: "Protosynthesis", + description: "Boosts the Pokémon's most proficient stat in harsh sunlight or if the Pokémon is holding Booster Energy.", + }, + quarkDrive: { + name: "Quark Drive", + description: "Boosts the Pokémon's most proficient stat on Electric Terrain or if the Pokémon is holding Booster Energy.", + }, + goodAsGold: { + name: "Good as Gold", + description: "A body of pure, solid gold gives the Pokémon full immunity to other Pokémon's status moves.", + }, + vesselOfRuin: { + name: "Vessel of Ruin", + description: "The power of the Pokémon's ruinous vessel lowers the Sp. Atk stats of all Pokémon except itself.", + }, + swordOfRuin: { + name: "Sword of Ruin", + description: "The power of the Pokémon's ruinous sword lowers the Defense stats of all Pokémon except itself.", + }, + tabletsOfRuin: { + name: "Tablets of Ruin", + description: "The power of the Pokémon's ruinous wooden tablets lowers the Attack stats of all Pokémon except itself.", + }, + beadsOfRuin: { + name: "Beads of Ruin", + description: "The power of the Pokémon's ruinous beads lowers the Sp. Def stats of all Pokémon except itself.", + }, + orichalcumPulse: { + name: "Orichalcum Pulse", + description: "Turns the sunlight harsh when the Pokémon enters a battle. The ancient pulse thrumming through the Pokémon also boosts its Attack stat in harsh sunlight.", + }, + hadronEngine: { + name: "Hadron Engine", + description: "Turns the ground into Electric Terrain when the Pokémon enters a battle. The futuristic engine within the Pokémon also boosts its Sp. Atk stat on Electric Terrain.", + }, + opportunist: { + name: "Opportunist", + description: "If an opponent's stat is boosted, the Pokémon seizes the opportunity to boost the same stat for itself.", + }, + cudChew: { + name: "Cud Chew", + description: "When the Pokémon eats a Berry, it will regurgitate that Berry at the end of the next turn and eat it one more time.", + }, + sharpness: { + name: "Sharpness", + description: "Powers up slicing moves.", + }, + supremeOverlord: { + name: "Supreme Overlord", + description: "When the Pokémon enters a battle, its Attack and Sp. Atk stats are slightly boosted for each of the allies in its party that have already been defeated.", + }, + costar: { + name: "Costar", + description: "When the Pokémon enters a battle, it copies an ally's stat changes.", + }, + toxicDebris: { + name: "Toxic Debris", + description: "Scatters poison spikes at the feet of the opposing team when the Pokémon takes damage from physical moves.", + }, + armorTail: { + name: "Armor Tail", + description: "The mysterious tail covering the Pokémon's head makes opponents unable to use priority moves against the Pokémon or its allies.", + }, + earthEater: { + name: "Earth Eater", + description: "If hit by a Ground-type move, the Pokémon has its HP restored instead of taking damage.", + }, + myceliumMight: { + name: "Mycelium Might", + description: "The Pokémon will always act more slowly when using status moves, but these moves will be unimpeded by the Ability of the target.", + }, + mindsEye: { + name: "Mind's Eye", + description: "The Pokémon ignores changes to opponents' evasiveness, its accuracy can't be lowered, and it can hit Ghost types with Normal- and Fighting-type moves.", + }, + supersweetSyrup: { + name: "Supersweet Syrup", + description: "A sickly sweet scent spreads across the field the first time the Pokémon enters a battle, lowering the evasiveness of opposing Pokémon.", + }, + hospitality: { + name: "Hospitality", + description: "When the Pokémon enters a battle, it showers its ally with hospitality, restoring a small amount of the ally's HP.", + }, + toxicChain: { + name: "Toxic Chain", + description: "The power of the Pokémon's toxic chain may badly poison any target the Pokémon hits with a move.", + }, + embodyAspectTeal: { + name: "Embody Aspect", + description: "The Pokémon's heart fills with memories, causing the Teal Mask to shine and the Pokémon's Speed stat to be boosted.", + }, + embodyAspectWellspring: { + name: "Embody Aspect", + description: "The Pokémon's heart fills with memories, causing the Wellspring Mask to shine and the Pokémon's Sp. Def stat to be boosted.", + }, + embodyAspectHearthflame: { + name: "Embody Aspect", + description: "The Pokémon's heart fills with memories, causing the Hearthflame Mask to shine and the Pokémon's Attack stat to be boosted.", + }, + embodyAspectCornerstone: { + name: "Embody Aspect", + description: "The Pokémon's heart fills with memories, causing the Cornerstone Mask to shine and the Pokémon's Defense stat to be boosted.", + }, + teraShift: { + name: "Tera Shift", + description: "When the Pokémon enters a battle, it absorbs the energy around itself and transforms into its Terastal Form.", + }, + teraShell: { + name: "Tera Shell", + description: "The Pokémon's shell contains the powers of each type. All damage-dealing moves that hit the Pokémon when its HP is full will not be very effective.", + }, + teraformZero: { + name: "Teraform Zero", + description: "When Terapagos changes into its Stellar Form, it uses its hidden powers to eliminate all effects of weather and terrain, reducing them to zero.", + }, + poisonPuppeteer: { + name: "Poison Puppeteer", + description: "Pokémon poisoned by Pecharunt's moves will also become confused.", + }, +} as const; diff --git a/src/locales/ca-ES/achv.ts b/src/locales/ca-ES/achv.ts new file mode 100644 index 00000000000..a05c8b814ab --- /dev/null +++ b/src/locales/ca-ES/achv.ts @@ -0,0 +1,278 @@ +import { AchievementTranslationEntries } from "#app/interfaces/locales.js"; + +// Achievement translations for the when the player character is male +export const PGMachv: AchievementTranslationEntries = { + "Achievements": { + name: "Achievements", + }, + "Locked": { + name: "Locked", + }, + + "MoneyAchv": { + description: "Accumulate a total of ₽{{moneyAmount}}", + }, + "10K_MONEY": { + name: "Money Haver", + }, + "100K_MONEY": { + name: "Rich", + }, + "1M_MONEY": { + name: "Millionaire", + }, + "10M_MONEY": { + name: "One Percenter", + }, + + "DamageAchv": { + description: "Inflict {{damageAmount}} damage in one hit", + }, + "250_DMG": { + name: "Hard Hitter", + }, + "1000_DMG": { + name: "Harder Hitter", + }, + "2500_DMG": { + name: "That's a Lotta Damage!", + }, + "10000_DMG": { + name: "One Punch Man", + }, + + "HealAchv": { + description: "Heal {{healAmount}} {{HP}} at once with a move, ability, or held item", + }, + "250_HEAL": { + name: "Novice Healer", + }, + "1000_HEAL": { + name: "Big Healer", + }, + "2500_HEAL": { + name: "Cleric", + }, + "10000_HEAL": { + name: "Recovery Master", + }, + + "LevelAchv": { + description: "Level up a Pokémon to Lv{{level}}", + }, + "LV_100": { + name: "But Wait, There's More!", + }, + "LV_250": { + name: "Elite", + }, + "LV_1000": { + name: "To Go Even Further Beyond", + }, + + "RibbonAchv": { + description: "Accumulate a total of {{ribbonAmount}} Ribbons", + }, + "10_RIBBONS": { + name: "Pokémon League Champion", + }, + "25_RIBBONS": { + name: "Great League Champion", + }, + "50_RIBBONS": { + name: "Ultra League Champion", + }, + "75_RIBBONS": { + name: "Rogue League Champion", + }, + "100_RIBBONS": { + name: "Master League Champion", + }, + + "TRANSFER_MAX_BATTLE_STAT": { + name: "Teamwork", + description: "Baton pass to another party member with at least one stat maxed out", + }, + "MAX_FRIENDSHIP": { + name: "Friendmaxxing", + description: "Reach max friendship on a Pokémon", + }, + "MEGA_EVOLVE": { + name: "Megamorph", + description: "Mega evolve a Pokémon", + }, + "GIGANTAMAX": { + name: "Absolute Unit", + description: "Gigantamax a Pokémon", + }, + "TERASTALLIZE": { + name: "STAB Enthusiast", + description: "Terastallize a Pokémon", + }, + "STELLAR_TERASTALLIZE": { + name: "The Hidden Type", + description: "Stellar Terastallize a Pokémon", + }, + "SPLICE": { + name: "Infinite Fusion", + description: "Splice two Pokémon together with DNA Splicers", + }, + "MINI_BLACK_HOLE": { + name: "A Hole Lot of Items", + description: "Acquire a Mini Black Hole", + }, + "CATCH_MYTHICAL": { + name: "Mythical", + description: "Catch a mythical Pokémon", + }, + "CATCH_SUB_LEGENDARY": { + name: "(Sub-)Legendary", + description: "Catch a sub-legendary Pokémon", + }, + "CATCH_LEGENDARY": { + name: "Legendary", + description: "Catch a legendary Pokémon", + }, + "SEE_SHINY": { + name: "Shiny", + description: "Find a shiny Pokémon in the wild", + }, + "SHINY_PARTY": { + name: "That's Dedication", + description: "Have a full party of shiny Pokémon", + }, + "HATCH_MYTHICAL": { + name: "Mythical Egg", + description: "Hatch a mythical Pokémon from an egg", + }, + "HATCH_SUB_LEGENDARY": { + name: "Sub-Legendary Egg", + description: "Hatch a sub-legendary Pokémon from an egg", + }, + "HATCH_LEGENDARY": { + name: "Legendary Egg", + description: "Hatch a legendary Pokémon from an egg", + }, + "HATCH_SHINY": { + name: "Shiny Egg", + description: "Hatch a shiny Pokémon from an egg", + }, + "HIDDEN_ABILITY": { + name: "Hidden Potential", + description: "Catch a Pokémon with a hidden ability", + }, + "PERFECT_IVS": { + name: "Certificate of Authenticity", + description: "Get perfect IVs on a Pokémon", + }, + "CLASSIC_VICTORY": { + name: "Undefeated", + description: "Beat the game in classic mode", + }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Bring Your Child To Work Day", + description: "Beat the game in Classic Mode with at least one unevolved party member." + }, + + "MONO_GEN_ONE": { + name: "The Original Rival", + description: "Complete the generation one only challenge.", + }, + "MONO_GEN_TWO": { + name: "Generation 1.5", + description: "Complete the generation two only challenge.", + }, + "MONO_GEN_THREE": { + name: "Too much water?", + description: "Complete the generation three only challenge.", + }, + "MONO_GEN_FOUR": { + name: "Is she really the hardest?", + description: "Complete the generation four only challenge.", + }, + "MONO_GEN_FIVE": { + name: "All Original", + description: "Complete the generation five only challenge.", + }, + "MONO_GEN_SIX": { + name: "Almost Royalty", + description: "Complete the generation six only challenge.", + }, + "MONO_GEN_SEVEN": { + name: "Only Technically", + description: "Complete the generation seven only challenge.", + }, + "MONO_GEN_EIGHT": { + name: "A Champion Time!", + description: "Complete the generation eight only challenge.", + }, + "MONO_GEN_NINE": { + name: "She was going easy on you", + description: "Complete the generation nine only challenge.", + }, + + "MonoType": { + description: "Complete the {{type}} monotype challenge.", + }, + "MONO_NORMAL": { + name: "Extra Ordinary", + }, + "MONO_FIGHTING": { + name: "I Know Kung Fu", + }, + "MONO_FLYING": { + name: "Angry Birds", + }, + "MONO_POISON": { + name: "Kanto's Favourite", + }, + "MONO_GROUND": { + name: "Forecast: Earthquakes", + }, + "MONO_ROCK": { + name: "Brock Hard", + }, + "MONO_BUG": { + name: "You Like Jazz?", + }, + "MONO_GHOST": { + name: "Who You Gonna Call?", + }, + "MONO_STEEL": { + name: "Iron Giant", + }, + "MONO_FIRE": { + name: "I Cast Fireball!", + }, + "MONO_WATER": { + name: "When It Rains, It Pours", + }, + "MONO_GRASS": { + name: "Can't Touch This", + }, + "MONO_ELECTRIC": { + name: "Aim For The Horn!", + }, + "MONO_PSYCHIC": { + name: "Big Brain Energy", + }, + "MONO_ICE": { + name: "Walking On Thin Ice", + }, + "MONO_DRAGON": { + name: "Pseudo-Legend Club", + }, + "MONO_DARK": { + name: "It's Just A Phase", + }, + "MONO_FAIRY": { + name: "Hey! Listen!", + }, + "FRESH_START": { + name: "First Try!", + description: "Complete the Fresh Start challenge." + } +} as const; + +// Achievement translations for the when the player character is female (it for now uses the same translations as the male version) +export const PGFachv: AchievementTranslationEntries = PGMachv; diff --git a/src/locales/ca-ES/arena-flyout.ts b/src/locales/ca-ES/arena-flyout.ts new file mode 100644 index 00000000000..8a31d37b10c --- /dev/null +++ b/src/locales/ca-ES/arena-flyout.ts @@ -0,0 +1,49 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const arenaFlyout: SimpleTranslationEntries = { + // Title + "activeBattleEffects": "Active Battle Effects", + "player": "Player", + "neutral": "Neutral", + "enemy": "Enemy", + + // WeatherType + "sunny": "Sunny", + "rain": "Rain", + "sandstorm": "Sandstorm", + "hail": "Hail", + "snow": "Snow", + "fog": "Fog", + "heavyRain": "Heavy Rain", + "harshSun": "Harsh Sun", + "strongWinds": "Strong Winds", + + // TerrainType + "misty": "Misty Terrain", + "electric": "Electric Terrain", + "grassy": "Grassy Terrain", + "psychic": "Psychic Terrain", + + // ArenaTagType + "mudSport": "Mud Sport", + "waterSport": "Water Sport", + "spikes": "Spikes", + "toxicSpikes": "Toxic Spikes", + "mist": "Mist", + "futureSight": "Future Sight", + "doomDesire": "Doom Desire", + "wish": "Wish", + "stealthRock": "Stealth Rock", + "stickyWeb": "Sticky Web", + "trickRoom": "Trick Room", + "gravity": "Gravity", + "reflect": "Reflect", + "lightScreen": "Light Screen", + "auroraVeil": "Aurora Veil", + "quickGuard": "Quick Guard", + "wideGuard": "Wide Guard", + "matBlock": "Mat Block", + "craftyShield": "Crafty Shield", + "tailwind": "Tailwind", + "happyHour": "Happy Hour", +}; diff --git a/src/locales/ca-ES/arena-tag.ts b/src/locales/ca-ES/arena-tag.ts new file mode 100644 index 00000000000..22612795308 --- /dev/null +++ b/src/locales/ca-ES/arena-tag.ts @@ -0,0 +1,53 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const arenaTag: SimpleTranslationEntries = { + "yourTeam": "your team", + "opposingTeam": "the opposing team", + "arenaOnRemove": "{{moveName}}'s effect wore off.", + "arenaOnRemovePlayer": "{{moveName}}'s effect wore off\non your side.", + "arenaOnRemoveEnemy": "{{moveName}}'s effect wore off\non the foe's side.", + "mistOnAdd": "{{pokemonNameWithAffix}}'s team became\nshrouded in mist!", + "mistApply": "The mist prevented\nthe lowering of stats!", + "reflectOnAdd": "Reflect reduced the damage of physical moves.", + "reflectOnAddPlayer": "Reflect reduced the damage of physical moves on your side.", + "reflectOnAddEnemy": "Reflect reduced the damage of physical moves on the foe's side.", + "lightScreenOnAdd": "Light Screen reduced the damage of special moves.", + "lightScreenOnAddPlayer": "Light Screen reduced the damage of special moves on your side.", + "lightScreenOnAddEnemy": "Light Screen reduced the damage of special moves on the foe's side.", + "auroraVeilOnAdd": "Aurora Veil reduced the damage of moves.", + "auroraVeilOnAddPlayer": "Aurora Veil reduced the damage of moves on your side.", + "auroraVeilOnAddEnemy": "Aurora Veil reduced the damage of moves on the foe's side.", + "conditionalProtectOnAdd": "{{moveName}} protected team!", + "conditionalProtectOnAddPlayer": "{{moveName}} protected your team!", + "conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!", + "conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!", + "matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!", + "noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!", + "noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!", + "noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!", + "wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!", + "mudSportOnAdd": "Electricity's power was weakened!", + "mudSportOnRemove": "The effects of Mud Sport\nhave faded.", + "waterSportOnAdd": "Fire's power was weakened!", + "waterSportOnRemove": "The effects of Water Sport\nhave faded.", + "spikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!", + "spikesActivateTrap": "{{pokemonNameWithAffix}} is hurt\nby the spikes!", + "toxicSpikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!", + "toxicSpikesActivateTrapPoison": "{{pokemonNameWithAffix}} absorbed the {{moveName}}!", + "stealthRockOnAdd": "Pointed stones float in the air\naround {{opponentDesc}}!", + "stealthRockActivateTrap": "Pointed stones dug into\n{{pokemonNameWithAffix}}!", + "stickyWebOnAdd": "A {{moveName}} has been laid out on the ground around the opposing team!", + "stickyWebActivateTrap": "The opposing {{pokemonName}} was caught in a sticky web!", + "trickRoomOnAdd": "{{pokemonNameWithAffix}} twisted\nthe dimensions!", + "trickRoomOnRemove": "The twisted dimensions\nreturned to normal!", + "gravityOnAdd": "Gravity intensified!", + "gravityOnRemove": "Gravity returned to normal!", + "tailwindOnAdd": "The Tailwind blew from behind team!", + "tailwindOnAddPlayer": "The Tailwind blew from behind\nyour team!", + "tailwindOnAddEnemy": "The Tailwind blew from behind\nthe opposing team!", + "tailwindOnRemove": "Team's Tailwind petered out!", + "tailwindOnRemovePlayer": "Your team's Tailwind petered out!", + "tailwindOnRemoveEnemy": "The opposing team's Tailwind petered out!", + "happyHourOnAdd": "Everyone is caught up in the happy atmosphere!", + "happyHourOnRemove": "The atmosphere returned to normal.", +} as const; diff --git a/src/locales/ca-ES/battle-info.ts b/src/locales/ca-ES/battle-info.ts new file mode 100644 index 00000000000..f24dad46c6c --- /dev/null +++ b/src/locales/ca-ES/battle-info.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleInfo: SimpleTranslationEntries = { + "generation": "Generation {{generation}}", +} as const; diff --git a/src/locales/ca-ES/battle-message-ui-handler.ts b/src/locales/ca-ES/battle-message-ui-handler.ts new file mode 100644 index 00000000000..34ca72276f5 --- /dev/null +++ b/src/locales/ca-ES/battle-message-ui-handler.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleMessageUiHandler: SimpleTranslationEntries = { + "ivBest": "Best", + "ivFantastic": "Fantastic", + "ivVeryGood": "Very Good", + "ivPrettyGood": "Pretty Good", + "ivDecent": "Decent", + "ivNoGood": "No Good", +} as const; diff --git a/src/locales/ca-ES/battle.ts b/src/locales/ca-ES/battle.ts new file mode 100644 index 00000000000..12a0f2c99c6 --- /dev/null +++ b/src/locales/ca-ES/battle.ts @@ -0,0 +1,159 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battle: SimpleTranslationEntries = { + "bossAppeared": "{{bossName}} appeared.", + "trainerAppeared": "{{trainerName}}\nwould like to battle!", + "trainerAppearedDouble": "{{trainerName}}\nwould like to battle!", + "trainerSendOut": "{{trainerName}} sent out\n{{pokemonName}}!", + "singleWildAppeared": "A wild {{pokemonName}} appeared!", + "multiWildAppeared": "A wild {{pokemonName1}}\nand {{pokemonName2}} appeared!", + "playerComeBack": "Come back, {{pokemonName}}!", + "trainerComeBack": "{{trainerName}} withdrew {{pokemonName}}!", + "playerGo": "Go! {{pokemonName}}!", + "trainerGo": "{{trainerName}} sent out {{pokemonName}}!", + "switchQuestion": "Will you switch\n{{pokemonName}}?", + "trainerDefeated": "You defeated\n{{trainerName}}!", + "moneyWon": "You got\n₽{{moneyAmount}} for winning!", + "moneyPickedUp": "You picked up ₽{{moneyAmount}}!", + "pokemonCaught": "{{pokemonName}} was caught!", + "addedAsAStarter": "{{pokemonName}} has been\nadded as a starter!", + "partyFull": "Your party is full.\nRelease a Pokémon to make room for {{pokemonName}}?", + "pokemon": "Pokémon", + "sendOutPokemon": "Go! {{pokemonName}}!", + "hitResultCriticalHit": "A critical hit!", + "hitResultSuperEffective": "It's super effective!", + "hitResultNotVeryEffective": "It's not very effective…", + "hitResultNoEffect": "It doesn't affect {{pokemonName}}!", + "hitResultOneHitKO": "It's a one-hit KO!", + "attackFailed": "But it failed!", + "attackMissed": "{{pokemonNameWithAffix}} avoided the attack!", + "attackHitsCount": "Hit {{count}} time(s)!", + "rewardGain": "You received\n{{modifierName}}!", + "expGain": "{{pokemonName}} gained\n{{exp}} EXP. Points!", + "levelUp": "{{pokemonName}} grew to\nLv. {{level}}!", + "learnMove": "{{pokemonName}} learned\n{{moveName}}!", + "learnMovePrompt": "{{pokemonName}} wants to learn the\nmove {{moveName}}.", + "learnMoveLimitReached": "However, {{pokemonName}} already\nknows four moves.", + "learnMoveReplaceQuestion": "Should a move be forgotten and\nreplaced with {{moveName}}?", + "learnMoveStopTeaching": "Stop trying to teach\n{{moveName}}?", + "learnMoveNotLearned": "{{pokemonName}} did not learn the\nmove {{moveName}}.", + "learnMoveForgetQuestion": "Which move should be forgotten?", + "learnMoveForgetSuccess": "{{pokemonName}} forgot how to\nuse {{moveName}}.", + "countdownPoof": "@d{32}1, @d{15}2, and@d{15}… @d{15}… @d{15}… @d{15}@s{pb_bounce_1}Poof!", + "learnMoveAnd": "And…", + "levelCapUp": "The level cap\nhas increased to {{levelCap}}!", + "moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.", + "moveNoPP": "There's no PP left for\nthis move!", + "moveDisabled": "{{moveName}} is disabled!", + "noPokeballForce": "An unseen force\nprevents using Poké Balls.", + "noPokeballTrainer": "You can't catch\nanother trainer's Pokémon!", + "noPokeballMulti": "You can only throw a Poké Ball\nwhen there is one Pokémon remaining!", + "noPokeballStrong": "The target Pokémon is too strong to be caught!\nYou need to weaken it first!", + "noEscapeForce": "An unseen force\nprevents escape.", + "noEscapeTrainer": "You can't run\nfrom a trainer battle!", + "noEscapePokemon": "{{pokemonName}}'s {{moveName}}\nprevents {{escapeVerb}}!", + "runAwaySuccess": "You got away safely!", + "runAwayCannotEscape": "You can't escape!", + "escapeVerbSwitch": "switching", + "escapeVerbFlee": "fleeing", + "notDisabled": "{{pokemonName}}'s {{moveName}} is disabled\nno more!", + "turnEndHpRestore": "{{pokemonName}}'s HP was restored.", + "hpIsFull": "{{pokemonName}}'s\nHP is full!", + "skipItemQuestion": "Are you sure you want to skip taking an item?", + "eggHatching": "Oh?", + "ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?", + "wildPokemonWithAffix": "Wild {{pokemonName}}", + "foePokemonWithAffix": "Foe {{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}} used {{moveName}}!", + "drainMessage": "{{pokemonName}} had its\nenergy drained!", + "regainHealth": "{{pokemonName}} regained\nhealth!", + "stealEatBerry": "{{pokemonName}} stole and ate\n{{targetName}}'s {{berryName}}!", + "ppHealBerry": "{{pokemonNameWithAffix}} restored PP to its move {{moveName}}\nusing its {{berryName}}!", + "hpHealBerry": "{{pokemonNameWithAffix}} restored its health using\nits {{berryName}}!", + "fainted": "{{pokemonNameWithAffix}} fainted!", + "statsAnd": "and", + "stats": "Stats", + "statRose_one": "{{pokemonNameWithAffix}}'s {{stats}} rose!", + "statRose_other": "{{pokemonNameWithAffix}}'s {{stats}} rose!", + "statSharplyRose_one": "{{pokemonNameWithAffix}}'s {{stats}} sharply rose!", + "statSharplyRose_other": "{{pokemonNameWithAffix}}'s {{stats}} sharply rose!", + "statRoseDrastically_one": "{{pokemonNameWithAffix}}'s {{stats}} rose drastically!", + "statRoseDrastically_other": "{{pokemonNameWithAffix}}'s {{stats}} rose drastically!", + "statWontGoAnyHigher_one": "{{pokemonNameWithAffix}}'s {{stats}} won't go any higher!", + "statWontGoAnyHigher_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any higher!", + "statFell_one": "{{pokemonNameWithAffix}}'s {{stats}} fell!", + "statFell_other": "{{pokemonNameWithAffix}}'s {{stats}} fell!", + "statHarshlyFell_one": "{{pokemonNameWithAffix}}'s {{stats}} harshly fell!", + "statHarshlyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} harshly fell!", + "statSeverelyFell_one": "{{pokemonNameWithAffix}}'s {{stats}} severely fell!", + "statSeverelyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} severely fell!", + "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}'s {{stats}} won't go any lower!", + "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any lower!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", + "retryBattle": "Would you like to retry from the start of the battle?", + "unlockedSomething": "{{unlockedThing}}\nhas been unlocked.", + "congratulations": "Congratulations!", + "beatModeFirstTime": "{{speciesName}} beat {{gameMode}} Mode for the first time!\nYou received {{newModifier}}!", + "ppReduced": "It reduced the PP of {{targetName}}'s\n{{moveName}} by {{reduction}}!", + "battlerTagsRechargingLapse": "{{pokemonNameWithAffix}} must\nrecharge!", + "battlerTagsTrappedOnAdd": "{{pokemonNameWithAffix}} can no\nlonger escape!", + "battlerTagsTrappedOnRemove": "{{pokemonNameWithAffix}} was freed\nfrom {{moveName}}!", + "battlerTagsFlinchedLapse": "{{pokemonNameWithAffix}} flinched!", + "battlerTagsConfusedOnAdd": "{{pokemonNameWithAffix}} became\nconfused!", + "battlerTagsConfusedOnRemove": "{{pokemonNameWithAffix}} snapped\nout of confusion!", + "battlerTagsConfusedOnOverlap": "{{pokemonNameWithAffix}} is\nalready confused!", + "battlerTagsConfusedLapse": "{{pokemonNameWithAffix}} is\nconfused!", + "battlerTagsConfusedLapseHurtItself": "It hurt itself in its\nconfusion!", + "battlerTagsDestinyBondLapseIsBoss": "{{pokemonNameWithAffix}} is unaffected\nby the effects of Destiny Bond.", + "battlerTagsDestinyBondLapse": "{{pokemonNameWithAffix}} took\n{{pokemonNameWithAffix2}} down with it!", + "battlerTagsInfatuatedOnAdd": "{{pokemonNameWithAffix}} fell in love\nwith {{sourcePokemonName}}!", + "battlerTagsInfatuatedOnOverlap": "{{pokemonNameWithAffix}} is\nalready in love!", + "battlerTagsInfatuatedLapse": "{{pokemonNameWithAffix}} is in love\nwith {{sourcePokemonName}}!", + "battlerTagsInfatuatedLapseImmobilize": "{{pokemonNameWithAffix}} is\nimmobilized by love!", + "battlerTagsInfatuatedOnRemove": "{{pokemonNameWithAffix}} got over\nits infatuation.", + "battlerTagsSeededOnAdd": "{{pokemonNameWithAffix}} was seeded!", + "battlerTagsSeededLapse": "{{pokemonNameWithAffix}}'s health is\nsapped by Leech Seed!", + "battlerTagsSeededLapseShed": "{{pokemonNameWithAffix}}'s Leech Seed\nsucked up the liquid ooze!", + "battlerTagsNightmareOnAdd": "{{pokemonNameWithAffix}} began\nhaving a Nightmare!", + "battlerTagsNightmareOnOverlap": "{{pokemonNameWithAffix}} is\nalready locked in a Nightmare!", + "battlerTagsNightmareLapse": "{{pokemonNameWithAffix}} is locked\nin a Nightmare!", + "battlerTagsEncoreOnAdd": "{{pokemonNameWithAffix}} got\nan Encore!", + "battlerTagsEncoreOnRemove": "{{pokemonNameWithAffix}}'s Encore\nended!", + "battlerTagsHelpingHandOnAdd": "{{pokemonNameWithAffix}} is ready to\nhelp {{pokemonName}}!", + "battlerTagsIngrainLapse": "{{pokemonNameWithAffix}} absorbed\nnutrients with its roots!", + "battlerTagsIngrainOnTrap": "{{pokemonNameWithAffix}} planted its roots!", + "battlerTagsAquaRingOnAdd": "{{pokemonNameWithAffix}} surrounded\nitself with a veil of water!", + "battlerTagsAquaRingLapse": "{{moveName}} restored\n{{pokemonName}}'s HP!", + "battlerTagsDrowsyOnAdd": "{{pokemonNameWithAffix}} grew drowsy!", + "battlerTagsDamagingTrapLapse": "{{pokemonNameWithAffix}} is hurt\nby {{moveName}}!", + "battlerTagsBindOnTrap": "{{pokemonNameWithAffix}} was squeezed by\n{{sourcePokemonName}}'s {{moveName}}!", + "battlerTagsWrapOnTrap": "{{pokemonNameWithAffix}} was Wrapped\nby {{sourcePokemonName}}!", + "battlerTagsVortexOnTrap": "{{pokemonNameWithAffix}} was trapped\nin the vortex!", + "battlerTagsClampOnTrap": "{{sourcePokemonNameWithAffix}} Clamped\n{{pokemonName}}!", + "battlerTagsSandTombOnTrap": "{{pokemonNameWithAffix}} became trapped\nby {{moveName}}!", + "battlerTagsMagmaStormOnTrap": "{{pokemonNameWithAffix}} became trapped\nby swirling magma!", + "battlerTagsSnapTrapOnTrap": "{{pokemonNameWithAffix}} got trapped\nby a snap trap!", + "battlerTagsThunderCageOnTrap": "{{sourcePokemonNameWithAffix}} trapped\n{{pokemonNameWithAffix}}!", + "battlerTagsInfestationOnTrap": "{{pokemonNameWithAffix}} has been afflicted \nwith an infestation by {{sourcePokemonNameWithAffix}}!", + "battlerTagsProtectedOnAdd": "{{pokemonNameWithAffix}}\nprotected itself!", + "battlerTagsProtectedLapse": "{{pokemonNameWithAffix}}\nprotected itself!", + "battlerTagsEnduringOnAdd": "{{pokemonNameWithAffix}} braced\nitself!", + "battlerTagsEnduringLapse": "{{pokemonNameWithAffix}} endured\nthe hit!", + "battlerTagsSturdyLapse": "{{pokemonNameWithAffix}} endured\nthe hit!", + "battlerTagsPerishSongLapse": "{{pokemonNameWithAffix}}'s perish count fell to {{turnCount}}.", + "battlerTagsCenterOfAttentionOnAdd": "{{pokemonNameWithAffix}} became the center\nof attention!", + "battlerTagsTruantLapse": "{{pokemonNameWithAffix}} is\nloafing around!", + "battlerTagsSlowStartOnAdd": "{{pokemonNameWithAffix}} can't\nget it going!", + "battlerTagsSlowStartOnRemove": "{{pokemonNameWithAffix}} finally\ngot its act together!", + "battlerTagsHighestStatBoostOnAdd": "{{pokemonNameWithAffix}}'s {{statName}}\nwas heightened!", + "battlerTagsHighestStatBoostOnRemove": "The effects of {{pokemonNameWithAffix}}'s\n{{abilityName}} wore off!", + "battlerTagsMagnetRisenOnAdd": "{{pokemonNameWithAffix}} levitated with electromagnetism!", + "battlerTagsMagnetRisenOnRemove": "{{pokemonNameWithAffix}}'s electromagnetism wore off!", + "battlerTagsCritBoostOnAdd": "{{pokemonNameWithAffix}} is getting\npumped!", + "battlerTagsCritBoostOnRemove": "{{pokemonNameWithAffix}} relaxed.", + "battlerTagsSaltCuredOnAdd": "{{pokemonNameWithAffix}} is being salt cured!", + "battlerTagsSaltCuredLapse": "{{pokemonNameWithAffix}} is hurt by {{moveName}}!", + "battlerTagsCursedOnAdd": "{{pokemonNameWithAffix}} cut its own HP and put a curse on the {{pokemonName}}!", + "battlerTagsCursedLapse": "{{pokemonNameWithAffix}} is afflicted by the Curse!", + "battlerTagsStockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!" +} as const; diff --git a/src/locales/ca-ES/battler-tags.ts b/src/locales/ca-ES/battler-tags.ts new file mode 100644 index 00000000000..1d897c70f3d --- /dev/null +++ b/src/locales/ca-ES/battler-tags.ts @@ -0,0 +1,12 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battlerTags: SimpleTranslationEntries = { + "trappedDesc": "trapping", + "flinchedDesc": "flinching", + "confusedDesc": "confusion", + "infatuatedDesc": "infatuation", + "seedDesc": "seeding", + "nightmareDesc": "nightmares", + "ingrainDesc": "roots", + "drowsyDesc": "drowsiness", +} as const; diff --git a/src/locales/ca-ES/berry.ts b/src/locales/ca-ES/berry.ts new file mode 100644 index 00000000000..3c4930b1591 --- /dev/null +++ b/src/locales/ca-ES/berry.ts @@ -0,0 +1,48 @@ +import { BerryTranslationEntries } from "#app/interfaces/locales"; + +export const berry: BerryTranslationEntries = { + "SITRUS": { + name: "Sitrus Berry", + effect: "Restores 25% HP if HP is below 50%", + }, + "LUM": { + name: "Lum Berry", + effect: "Cures any non-volatile status condition and confusion", + }, + "ENIGMA": { + name: "Enigma Berry", + effect: "Restores 25% HP if hit by a super effective move", + }, + "LIECHI": { + name: "Liechi Berry", + effect: "Raises Attack if HP is below 25%", + }, + "GANLON": { + name: "Ganlon Berry", + effect: "Raises Defense if HP is below 25%", + }, + "PETAYA": { + name: "Petaya Berry", + effect: "Raises Sp. Atk if HP is below 25%", + }, + "APICOT": { + name: "Apicot Berry", + effect: "Raises Sp. Def if HP is below 25%", + }, + "SALAC": { + name: "Salac Berry", + effect: "Raises Speed if HP is below 25%", + }, + "LANSAT": { + name: "Lansat Berry", + effect: "Raises critical hit ratio if HP is below 25%", + }, + "STARF": { + name: "Starf Berry", + effect: "Sharply raises a random stat if HP is below 25%", + }, + "LEPPA": { + name: "Leppa Berry", + effect: "Restores 10 PP to a move if its PP reaches 0", + }, +} as const; diff --git a/src/locales/ca-ES/bgm-name.ts b/src/locales/ca-ES/bgm-name.ts new file mode 100644 index 00000000000..be9a8f621c7 --- /dev/null +++ b/src/locales/ca-ES/bgm-name.ts @@ -0,0 +1,146 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const bgmName: SimpleTranslationEntries = { + "music": "Music: ", + "missing_entries" : "{{name}}", + "battle_kanto_champion": "B2W2 Kanto Champion Battle", + "battle_johto_champion": "B2W2 Johto Champion Battle", + "battle_hoenn_champion_g5": "B2W2 Hoenn Champion Battle", + "battle_hoenn_champion_g6": "ORAS Hoenn Champion Battle", + "battle_sinnoh_champion": "B2W2 Sinnoh Champion Battle", + "battle_champion_alder": "BW Unova Champion Battle", + "battle_champion_iris": "B2W2 Unova Champion Battle", + "battle_kalos_champion": "XY Kalos Champion Battle", + "battle_alola_champion": "USUM Alola Champion Battle", + "battle_galar_champion": "SWSH Galar Champion Battle", + "battle_champion_geeta": "SV Champion Geeta Battle", + "battle_champion_nemona": "SV Champion Nemona Battle", + "battle_champion_kieran": "SV Champion Kieran Battle", + "battle_hoenn_elite": "ORAS Elite Four Battle", + "battle_unova_elite": "BW Elite Four Battle", + "battle_kalos_elite": "XY Elite Four Battle", + "battle_alola_elite": "SM Elite Four Battle", + "battle_galar_elite": "SWSH League Tournament Battle", + "battle_paldea_elite": "SV Elite Four Battle", + "battle_bb_elite": "SV BB League Elite Four Battle", + "battle_final_encounter": "PMD RTDX Rayquaza's Domain", + "battle_final": "BW Ghetsis Battle", + "battle_kanto_gym": "B2W2 Kanto Gym Battle", + "battle_johto_gym": "B2W2 Johto Gym Battle", + "battle_hoenn_gym": "B2W2 Hoenn Gym Battle", + "battle_sinnoh_gym": "B2W2 Sinnoh Gym Battle", + "battle_unova_gym": "BW Unova Gym Battle", + "battle_kalos_gym": "XY Kalos Gym Battle", + "battle_galar_gym": "SWSH Galar Gym Battle", + "battle_paldea_gym": "SV Paldea Gym Battle", + "battle_legendary_kanto": "XY Kanto Legendary Battle", + "battle_legendary_raikou": "HGSS Raikou Battle", + "battle_legendary_entei": "HGSS Entei Battle", + "battle_legendary_suicune": "HGSS Suicune Battle", + "battle_legendary_lugia": "HGSS Lugia Battle", + "battle_legendary_ho_oh": "HGSS Ho-oh Battle", + "battle_legendary_regis_g5": "B2W2 Legendary Titan Battle", + "battle_legendary_regis_g6": "ORAS Legendary Titan Battle", + "battle_legendary_gro_kyo": "ORAS Groudon & Kyogre Battle", + "battle_legendary_rayquaza": "ORAS Rayquaza Battle", + "battle_legendary_deoxys": "ORAS Deoxys Battle", + "battle_legendary_lake_trio": "ORAS Lake Guardians Battle", + "battle_legendary_sinnoh": "ORAS Sinnoh Legendary Battle", + "battle_legendary_dia_pal": "ORAS Dialga & Palkia Battle", + "battle_legendary_giratina": "ORAS Giratina Battle", + "battle_legendary_arceus": "HGSS Arceus Battle", + "battle_legendary_unova": "BW Unova Legendary Battle", + "battle_legendary_kyurem": "BW Kyurem Battle", + "battle_legendary_res_zek": "BW Reshiram & Zekrom Battle", + "battle_legendary_xern_yvel": "XY Xerneas & Yveltal Battle", + "battle_legendary_tapu": "SM Tapu Battle", + "battle_legendary_sol_lun": "SM Solgaleo & Lunala Battle", + "battle_legendary_ub": "SM Ultra Beast Battle", + "battle_legendary_dusk_dawn": "USUM Dusk Mane & Dawn Wings Necrozma Battle", + "battle_legendary_ultra_nec": "USUM Ultra Necrozma Battle", + "battle_legendary_zac_zam": "SWSH Zacian & Zamazenta Battle", + "battle_legendary_glas_spec": "SWSH Glastrier & Spectrier Battle", + "battle_legendary_calyrex": "SWSH Calyrex Battle", + "battle_legendary_birds_galar": "SWSH Galarian Legendary Birds Battle", + "battle_legendary_ruinous": "SV Treasures of Ruin Battle", + "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", + "battle_legendary_loyal_three": "SV Loyal Three Battle", + "battle_legendary_ogerpon": "SV Ogerpon Battle", + "battle_legendary_terapagos": "SV Terapagos Battle", + "battle_legendary_pecharunt": "SV Pecharunt Battle", + "battle_rival": "BW Rival Battle", + "battle_rival_2": "BW N Battle", + "battle_rival_3": "BW Final N Battle", + "battle_trainer": "BW Trainer Battle", + "battle_wild": "BW Wild Battle", + "battle_wild_strong": "BW Strong Wild Battle", + "end_summit": "PMD RTDX Sky Tower Summit", + "battle_rocket_grunt": "HGSS Team Rocket Battle", + "battle_aqua_magma_grunt": "ORAS Team Aqua & Magma Battle", + "battle_galactic_grunt": "BDSP Team Galactic Battle", + "battle_plasma_grunt": "BW Team Plasma Battle", + "battle_flare_grunt": "XY Team Flare Battle", + "battle_rocket_boss": "USUM Giovanni Battle", + "battle_aqua_magma_boss": "ORAS Archie & Maxie Battle", + "battle_galactic_boss": "BDSP Cyrus Battle", + "battle_plasma_boss": "B2W2 Ghetsis Battle", + "battle_flare_boss": "XY Lysandre Battle", + + // Biome Music + "abyss": "PMD EoS Dark Crater", + "badlands": "PMD EoS Barren Valley", + "beach": "PMD EoS Drenched Bluff", + "cave": "PMD EoS Sky Peak Cave", + "construction_site": "PMD EoS Boulder Quarry", + "desert": "PMD EoS Northern Desert", + "dojo": "PMD EoS Marowak Dojo", + "end": "PMD RTDX Sky Tower", + "factory": "PMD EoS Concealed Ruins", + "fairy_cave": "PMD EoS Star Cave", + "forest": "PMD EoS Dusk Forest", + "grass": "PMD EoS Apple Woods", + "graveyard": "PMD EoS Mystifying Forest", + "ice_cave": "PMD EoS Vast Ice Mountain", + "island": "PMD EoS Craggy Coast", + "jungle": "Lmz - Jungle", // The composer thinks about a more creative name + "laboratory": "Firel - Laboratory", // The composer thinks about a more creative name + "lake": "PMD EoS Crystal Cave", + "meadow": "PMD EoS Sky Peak Forest", + "metropolis": "Firel - Metropolis", // The composer thinks about a more creative name + "mountain": "PMD EoS Mt. Horn", + "plains": "PMD EoS Sky Peak Prairie", + "power_plant": "PMD EoS Far Amp Plains", + "ruins": "PMD EoS Deep Sealed Ruin", + "sea": "Andr06 - Marine Mystique", // Name defined by the composer + "seabed": "Firel - Seabed", // The composer thinks about a more creative name + "slum": "Andr06 - Sneaky Snom", // Name defined by the composer + "snowy_forest": "PMD EoS Sky Peak Snowfield", + "space": "Firel - Aether", + "swamp": "PMD EoS Surrounded Sea", + "tall_grass": "PMD EoS Foggy Forest", + "temple": "PMD EoS Aegis Cave", + "town": "PMD EoS Random Dungeon Theme 3", + "volcano": "PMD EoS Steam Cave", + "wasteland": "PMD EoS Hidden Highland", + + // Encounter + "encounter_ace_trainer": "BW Trainers' Eyes Meet (Ace Trainer)", + "encounter_backpacker": "BW Trainers' Eyes Meet (Backpacker)", + "encounter_clerk": "BW Trainers' Eyes Meet (Clerk)", + "encounter_cyclist": "BW Trainers' Eyes Meet (Cyclist)", + "encounter_lass": "BW Trainers' Eyes Meet (Lass)", + "encounter_parasol_lady": "BW Trainers' Eyes Meet (Parasol Lady)", + "encounter_pokefan": "BW Trainers' Eyes Meet (Poke Fan)", + "encounter_psychic": "BW Trainers' Eyes Meet (Psychic)", + "encounter_rich": "BW Trainers' Eyes Meet (Gentleman)", + "encounter_rival": "BW Cheren", + "encounter_roughneck": "BW Trainers' Eyes Meet (Roughneck)", + "encounter_scientist": "BW Trainers' Eyes Meet (Scientist)", + "encounter_twins": "BW Trainers' Eyes Meet (Twins)", + "encounter_youngster": "BW Trainers' Eyes Meet (Youngster)", + + // Other + "heal": "BW Pokémon Heal", + "menu": "PMD EoS Welcome to the World of Pokémon!", + "title": "PMD EoS Top Menu Theme", +} as const; diff --git a/src/locales/ca-ES/biome.ts b/src/locales/ca-ES/biome.ts new file mode 100644 index 00000000000..d3f34c021d4 --- /dev/null +++ b/src/locales/ca-ES/biome.ts @@ -0,0 +1,40 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const biome: SimpleTranslationEntries = { + "unknownLocation": "Somewhere you can\'t remember", + "TOWN": "Town", + "PLAINS": "Plains", + "GRASS": "Grassy Field", + "TALL_GRASS": "Tall Grass", + "METROPOLIS": "Metropolis", + "FOREST": "Forest", + "SEA": "Sea", + "SWAMP": "Swamp", + "BEACH": "Beach", + "LAKE": "Lake", + "SEABED": "Seabed", + "MOUNTAIN": "Mountain", + "BADLANDS": "Badlands", + "CAVE": "Cave", + "DESERT": "Desert", + "ICE_CAVE": "Ice Cave", + "MEADOW": "Meadow", + "POWER_PLANT": "Power Plant", + "VOLCANO": "Volcano", + "GRAVEYARD": "Graveyard", + "DOJO": "Dojo", + "FACTORY": "Factory", + "RUINS": "Ancient Ruins", + "WASTELAND": "Wasteland", + "ABYSS": "Abyss", + "SPACE": "Space", + "CONSTRUCTION_SITE": "Construction Site", + "JUNGLE": "Jungle", + "FAIRY_CAVE": "Fairy Cave", + "TEMPLE": "Temple", + "SLUM": "Slum", + "SNOWY_FOREST": "Snowy Forest", + "ISLAND": "Island", + "LABORATORY": "Laboratory", + "END": "???", +} as const; diff --git a/src/locales/ca-ES/challenges.ts b/src/locales/ca-ES/challenges.ts new file mode 100644 index 00000000000..e3302876201 --- /dev/null +++ b/src/locales/ca-ES/challenges.ts @@ -0,0 +1,32 @@ +import { TranslationEntries } from "#app/interfaces/locales.js"; + +export const challenges: TranslationEntries = { + "title": "Challenge Modifiers", + "illegalEvolution": "{{pokemon}} changed into an ineligble pokémon\nfor this challenge!", + "singleGeneration": { + "name": "Mono Gen", + "desc": "You can only use Pokémon from Generation {{gen}}.", + "desc_default": "You can only use Pokémon from the chosen generation.", + "gen_1": "one", + "gen_2": "two", + "gen_3": "three", + "gen_4": "four", + "gen_5": "five", + "gen_6": "six", + "gen_7": "seven", + "gen_8": "eight", + "gen_9": "nine", + }, + "singleType": { + "name": "Mono Type", + "desc": "You can only use Pokémon with the {{type}} type.", + "desc_default": "You can only use Pokémon of the chosen type." + //types in pokemon-info + }, + "freshStart": { + "name": "Fresh Start", + "desc": "You can only use the original starters, and only as if you had just started PokéRogue.", + "value.0": "Off", + "value.1": "On", + } +} as const; diff --git a/src/locales/ca-ES/command-ui-handler.ts b/src/locales/ca-ES/command-ui-handler.ts new file mode 100644 index 00000000000..c4c65db0aa0 --- /dev/null +++ b/src/locales/ca-ES/command-ui-handler.ts @@ -0,0 +1,9 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const commandUiHandler: SimpleTranslationEntries = { + "fight": "Fight", + "ball": "Ball", + "pokemon": "Pokémon", + "run": "Run", + "actionMessage": "What will\n{{pokemonName}} do?", +} as const; diff --git a/src/locales/ca-ES/common.ts b/src/locales/ca-ES/common.ts new file mode 100644 index 00000000000..750322e1f09 --- /dev/null +++ b/src/locales/ca-ES/common.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const common: SimpleTranslationEntries = { + "start": "Start", + "luckIndicator": "Luck:", + "shinyOnHover": "Shiny", + "commonShiny": "Common", + "rareShiny": "Rare", + "epicShiny": "Epic", +} as const; diff --git a/src/locales/ca-ES/config.ts b/src/locales/ca-ES/config.ts new file mode 100644 index 00000000000..831ab5d99f5 --- /dev/null +++ b/src/locales/ca-ES/config.ts @@ -0,0 +1,116 @@ +import { common } from "./common.js"; +import { settings } from "./settings.js"; +import { ability } from "./ability"; +import { abilityTriggers } from "./ability-trigger"; +import { arenaFlyout } from "./arena-flyout"; +import { arenaTag } from "./arena-tag"; +import { PGFachv, PGMachv } from "./achv"; +import { battle } from "./battle"; +import { battleInfo } from "./battle-info"; +import { battleMessageUiHandler } from "./battle-message-ui-handler"; +import { battlerTags } from "./battler-tags"; +import { berry } from "./berry"; +import { bgmName } from "./bgm-name"; +import { biome } from "./biome"; +import { challenges } from "./challenges"; +import { commandUiHandler } from "./command-ui-handler"; +import { + PGFbattleSpecDialogue, + PGFdialogue, + PGFdoubleBattleDialogue, + PGFmiscDialogue, + PGMbattleSpecDialogue, + PGMdialogue, + PGMdoubleBattleDialogue, + PGMmiscDialogue +} from "./dialogue"; +import { egg } from "./egg"; +import { fightUiHandler } from "./fight-ui-handler"; +import { filterBar } from "./filter-bar"; +import { gameMode } from "./game-mode"; +import { gameStatsUiHandler } from "./game-stats-ui-handler"; +import { growth } from "./growth"; +import { menu } from "./menu"; +import { menuUiHandler } from "./menu-ui-handler"; +import { modifier } from "./modifier"; +import { modifierType } from "./modifier-type"; +import { move } from "./move"; +import { nature } from "./nature"; +import { partyUiHandler } from "./party-ui-handler"; +import { pokeball } from "./pokeball"; +import { pokemon } from "./pokemon"; +import { pokemonForm, battlePokemonForm } from "./pokemon-form"; +import { pokemonInfo } from "./pokemon-info"; +import { pokemonInfoContainer } from "./pokemon-info-container"; +import { pokemonSummary } from "./pokemon-summary"; +import { saveSlotSelectUiHandler } from "./save-slot-select-ui-handler"; +import { splashMessages } from "./splash-messages"; +import { starterSelectUiHandler } from "./starter-select-ui-handler"; +import { statusEffect } from "./status-effect"; +import { titles, trainerClasses, trainerNames } from "./trainers"; +import { tutorial } from "./tutorial"; +import { voucher } from "./voucher"; +import { terrain, weather } from "./weather"; +import { modifierSelectUiHandler } from "./modifier-select-ui-handler"; +import { moveTriggers } from "./move-trigger"; + +export const caESConfig = { + ability: ability, + abilityTriggers: abilityTriggers, + arenaFlyout: arenaFlyout, + arenaTag: arenaTag, + battle: battle, + battleInfo: battleInfo, + battleMessageUiHandler: battleMessageUiHandler, + battlePokemonForm: battlePokemonForm, + battlerTags: battlerTags, + berry: berry, + bgmName: bgmName, + biome: biome, + challenges: challenges, + commandUiHandler: commandUiHandler, + common: common, + PGMachv: PGMachv, + PGFachv: PGFachv, + PGMdialogue: PGMdialogue, + PGFdialogue: PGFdialogue, + PGMbattleSpecDialogue: PGMbattleSpecDialogue, + PGFbattleSpecDialogue: PGFbattleSpecDialogue, + PGMmiscDialogue: PGMmiscDialogue, + PGFmiscDialogue: PGFmiscDialogue, + PGMdoubleBattleDialogue: PGMdoubleBattleDialogue, + PGFdoubleBattleDialogue: PGFdoubleBattleDialogue, + egg: egg, + fightUiHandler: fightUiHandler, + filterBar: filterBar, + gameMode: gameMode, + gameStatsUiHandler: gameStatsUiHandler, + growth: growth, + menu: menu, + menuUiHandler: menuUiHandler, + modifier: modifier, + modifierType: modifierType, + move: move, + nature: nature, + pokeball: pokeball, + pokemon: pokemon, + pokemonForm: pokemonForm, + pokemonInfo: pokemonInfo, + pokemonInfoContainer: pokemonInfoContainer, + pokemonSummary: pokemonSummary, + saveSlotSelectUiHandler: saveSlotSelectUiHandler, + settings: settings, + splashMessages: splashMessages, + starterSelectUiHandler: starterSelectUiHandler, + statusEffect: statusEffect, + terrain: terrain, + titles: titles, + trainerClasses: trainerClasses, + trainerNames: trainerNames, + tutorial: tutorial, + voucher: voucher, + weather: weather, + partyUiHandler: partyUiHandler, + modifierSelectUiHandler: modifierSelectUiHandler, + moveTriggers: moveTriggers +}; diff --git a/src/locales/ca-ES/dialogue.ts b/src/locales/ca-ES/dialogue.ts new file mode 100644 index 00000000000..e783ea14006 --- /dev/null +++ b/src/locales/ca-ES/dialogue.ts @@ -0,0 +1,2920 @@ +import { DialogueTranslationEntries, SimpleTranslationEntries } from "#app/interfaces/locales"; + +// Dialogue of the NPCs in the game when the player character is male (or unset) +export const PGMdialogue: DialogueTranslationEntries = { + "youngster": { + "encounter": { + 1: "Hey, wanna battle?", + 2: "Are you a new trainer too?", + 3: "Hey, I haven't seen you before. Let's battle!", + 4: "I just lost, so I'm trying to find more Pokémon.\nWait! You look weak! Come on, let's battle!", + 5: "Have we met or not? I don't really remember. Well, I guess it's nice to meet you anyway!", + 6: "All right! Let's go!", + 7: "All right! Here I come! I'll show you my power!", + 8: "Haw haw haw... I'll show you how hawesome my Pokémon are!", + 9: "No need to waste time saying hello. Bring it on whenever you're ready!", + 10: "Don't let your guard down, or you may be crying when a kid beats you.", + 11: "I've raised my Pokémon with great care. You're not allowed to hurt them!", + 12: "Glad you made it! It won't be an easy job from here.", + 13: "The battles continue forever! Welcome to the world with no end!" + }, + "victory": { + 1: "Wow! You're strong!", + 2: "I didn't stand a chance, huh?", + 3: "I'll find you again when I'm older and beat you!", + 4: "Ugh. I don't have any more Pokémon.", + 5: "No way… NO WAY! How could I lose again…", + 6: "No! I lost!", + 7: "Whoa! You are incredible! I'm amazed and surprised!", + 8: "Could it be… How… My Pokémon and I are the strongest, though…", + 9: "I won't lose next time! Let's battle again sometime!", + 10: "Sheesh! Can't you see that I'm just a kid! It wasn't fair of you to go all out like that!", + 11: "Your Pokémon are more amazing! Trade with me!", + 12: "I got a little carried away earlier, but what job was I talking about?", + 13: "Ahaha! There it is! That's right! You're already right at home in this world!" + } + }, + "lass": { + "encounter": { + 1: "Let's have a battle, shall we?", + 2: "You look like a new trainer. Let's have a battle!", + 3: "I don't recognize you. How about a battle?", + 4: "Let's have a fun Pokémon battle!", + 5: "I'll show you the ropes of how to really use Pokémon!", + 6: "A serious battle starts from a serious beginning! Are you sure you're ready?", + 7: "You're only young once. And you only get one shot at a given battle. Soon, you'll be nothing but a memory.", + 8: "You'd better go easy on me, OK? Though I'll be seriously fighting!", + 9: "School is boring. I've got nothing to do. Yawn. I'm only battling to kill the time." + }, + "victory": { + 1: "That was impressive! I've got a lot to learn.", + 2: "I didn't think you'd beat me that bad…", + 3: "I hope we get to have a rematch some day.", + 4: "That was pretty amazingly fun! You've totally exhausted me…", + 5: "You actually taught me a lesson! You're pretty amazing!", + 6: "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", + 7: "I don't need memories like this. Deleting memory…", + 8: "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", + 9: "I'm actually getting tired of battling… There's gotta be something new to do…" + } + }, + "breeder": { + "encounter": { + 1: "Obedient Pokémon, selfish Pokémon… Pokémon have unique characteristics.", + 2: "Even though my upbringing and behavior are poor, I've raised my Pokémon well.", + 3: "Hmm, do you discipline your Pokémon? Pampering them too much is no good.", + }, + "victory": { + 1: "It is important to nurture and train each Pokémon's characteristics.", + 2: "Unlike my diabolical self, these are some good Pokémon.", + 3: "Too much praise can spoil both Pokémon and people.", + }, + "defeat": { + 1: "You should not get angry at your Pokémon, even if you lose a battle.", + 2: "Right? Pretty good Pokémon, huh? I'm suited to raising things.", + 3: "No matter how much you love your Pokémon, you still have to discipline them when they misbehave." + } + }, + "breeder_female": { + "encounter": { + 1: "Pokémon never betray you. They return all the love you give them.", + 2: "Shall I give you a tip for training good Pokémon?", + 3: "I have raised these very special Pokémon using a special method." + }, + "victory": { + 1: "Ugh… It wasn't supposed to be like this. Did I administer the wrong blend?", + 2: "How could that happen to my Pokémon… What are you feeding your Pokémon?", + 3: "If I lose, that tells you I was just killing time. It doesn't damage my ego at all." + }, + "defeat": { + 1: "This proves my Pokémon have accepted my love.", + 2: "The real trick behind training good Pokémon is catching good Pokémon.", + 3: "Pokémon will be strong or weak depending on how you raise them." + } + }, + "fisherman": { + "encounter": { + 1: "Aack! You made me lose a bite!\nWhat are you going to do about it?", + 2: "Go away! You're scaring the Pokémon!", + 3: "Let's see if you can reel in a victory!", + }, + "victory": { + 1: "Just forget about it.", + 2: "Next time, I'll be reelin' in the triumph!", + 3: "Guess I underestimated the currents this time.", + }, + }, + "fisherman_female": { + "encounter": { + 1: "Woah! I've hooked a big one!", + 2: "Line's in, ready to reel in success!", + 3: "Ready to make waves!" + }, + "victory": { + 1: "I'll be back with a stronger hook.", + 2: "I'll reel in victory next time.", + 3: "I'm just sharpening my hooks for the comeback!" + }, + }, + "swimmer": { + "encounter": { + 1: "Time to dive in!", + 2: "Let's ride the waves of victory!", + 3: "Ready to make a splash!", + }, + "victory": { + 1: "Drenched in defeat!", + 2: "A wave of defeat!", + 3: "Back to shore, I guess.", + }, + }, + "backpacker": { + "encounter": { + 1: "Pack up, game on!", + 2: "Let's see if you can keep pace!", + 3: "Gear up, challenger!", + 4: "I've spent 20 years trying to find myself… But where am I?" + }, + "victory": { + 1: "Tripped up this time!", + 2: "Oh, I think I'm lost.", + 3: "Dead end!", + 4: "Wait up a second! Hey! Don't you know who I am?" + }, + }, + "ace_trainer": { + "encounter": { + 1: "You seem quite confident.", + 2: "Your Pokémon… Show them to me…", + 3: "Because I'm an Ace Trainer, people think I'm strong.", + 4: "Are you aware of what it takes to be an Ace Trainer?" + }, + "victory": { + 1: "Yes… You have good Pokémon…", + 2: "What?! But I'm a battling genius!", + 3: "Of course, you are the main character!", + 4: "OK! OK! You could be an Ace Trainer!" + }, + "defeat": { + 1: "I am devoting my body and soul to Pokémon battles!", + 2: "All within my expectations… Nothing to be surprised about…", + 3: "I thought I'd grow up to be a frail person who looked like they would break if you squeezed them too hard.", + 4: "Of course I'm strong and don't lose. It's important that I win gracefully." + } + }, + "parasol_lady": { + "encounter": { + 1: "Time to grace the battlefield with elegance and poise!", + }, + "victory": { + 1: "My elegance remains unbroken!", + } + }, + "twins": { + "encounter": { + 1: "Get ready, because when we team up, it's double the trouble!", + 2: "Two hearts, one strategy – let's see if you can keep up with our twin power!", + 3: "Hope you're ready for double trouble, because we're about to bring the heat!" + }, + "victory": { + 1: "We may have lost this round, but our bond remains unbreakable!", + 2: "Our twin spirit won't be dimmed for long.", + 3: "We'll come back stronger as a dynamic duo!" + }, + "defeat": { + 1: "Twin power reigns supreme!", + 2: "Two hearts, one triumph!", + 3: "Double the smiles, double the victory dance!" + } + }, + "cyclist": { + "encounter": { + 1: "Get ready to eat my dust!", + 2: "Gear up, challenger! I'm about to leave you in the dust!", + 3: "Pedal to the metal, let's see if you can keep pace!" + }, + "victory": { + 1: "Spokes may be still, but determination pedals on.", + 2: "Outpaced!", + 3: "The road to victory has many twists and turns yet to explore." + }, + }, + "black_belt": { + "encounter": { + 1: "I praise your courage in challenging me! For I am the one with the strongest kick!", + 2: "Oh, I see. Would you like to be cut to pieces? Or do you prefer the role of punching bag?" + }, + "victory": { + 1: "Oh. The Pokémon did the fighting. My strong kick didn't help a bit.", + 2: "Hmmm… If I was going to lose anyway, I was hoping to get totally messed up in the process." + }, + }, + "battle_girl": { + "encounter": { + 1: "You don't have to try to impress me. You can lose against me.", + }, + "victory": { + 1: "It's hard to say good-bye, but we are running out of time…", + }, + }, + "hiker": { + "encounter": { + 1: "My middle-age spread has given me as much gravitas as the mountains I hike!", + 2: "I inherited this big-boned body from my parents… I'm like a living mountain range…", + }, + "victory": { + 1: "At least I cannot lose when it comes to BMI!", + 2: "It's not enough… It's never enough. My bad cholesterol isn't high enough…" + }, + }, + "ranger": { + "encounter": { + 1: "When I am surrounded by nature, most other things cease to matter.", + 2: "When I'm living without nature in my life, sometimes I'll suddenly feel an anxiety attack coming on." + }, + "victory": { + 1: "It doesn't matter to the vastness of nature whether I win or lose…", + 2: "Something like this is pretty trivial compared to the stifling feelings of city life." + }, + "defeat": { + 1: "I won the battle. But victory is nothing compared to the vastness of nature…", + 2: "I'm sure how you feel is not so bad if you compare it to my anxiety attacks…" + } + }, + "scientist": { + "encounter": { + 1: "My research will lead this world to peace and joy.", + }, + "victory": { + 1: "I am a genius… I am not supposed to lose against someone like you…", + }, + }, + "school_kid": { + "encounter": { + 1: "…Heehee. I'm confident in my calculations and analysis.", + 2: "I'm gaining as much experience as I can because I want to be a Gym Leader someday." + }, + "victory": { + 1: "Ohhhh… Calculation and analysis are perhaps no match for chance…", + 2: "Even difficult, trying experiences have their purpose, I suppose." + } + }, + "artist": { + "encounter": { + 1: "I used to be popular, but now I am all washed up.", + }, + "victory": { + 1: "As times change, values also change. I realized that too late.", + }, + }, + "guitarist": { + "encounter": { + 1: "Get ready to feel the rhythm of defeat as I strum my way to victory!", + }, + "victory": { + 1: "Silenced for now, but my melody of resilience will play on.", + }, + }, + "worker": { + "encounter": { + 1: "It bothers me that people always misunderstand me. I'm a lot more pure than everyone thinks.", + }, + "victory": { + 1: "I really don't want my skin to burn, so I want to stay in the shade while I work.", + }, + }, + "worker_female": { + "encounter": { + 1: `It bothers me that people always misunderstand me. + $I'm a lot more pure than everyone thinks.` + }, + "victory": { + 1: "I really don't want my skin to burn, so I want to stay in the shade while I work." + }, + "defeat": { + 1: "My body and mind aren't necessarily always in sync." + } + }, + "worker_double": { + "encounter": { + 1: "I'll show you we can break you. We've been training in the field!", + }, + "victory": { + 1: "How strange… How could this be… I shouldn't have been outmuscled.", + }, + }, + "hex_maniac": { + "encounter": { + 1: "I normally only ever listen to classical music, but if I lose, I think I shall try a bit of new age!", + 2: "I grow stronger with each tear I cry." + }, + "victory": { + 1: "Is this the dawning of the age of Aquarius?", + 2: "Now I can get even stronger. I grow with every grudge." + }, + "defeat": { + 1: "New age simply refers to twentieth century classical composers, right?", + 2: "Don't get hung up on sadness or frustration. You can use your grudges to motivate yourself." + } + }, + "psychic": { + "encounter": { + 1: "Hi! Focus!", + }, + "victory": { + 1: "Eeeeek!", + }, + }, + "officer": { + "encounter": { + 1: "Brace yourself, because justice is about to be served!", + 2: "Ready to uphold the law and serve justice on the battlefield!" + }, + "victory": { + 1: "The weight of justice feels heavier than ever…", + 2: "The shadows of defeat linger in the precinct." + } + }, + "beauty": { + "encounter": { + 1: "My last ever battle… That's the way I'd like us to view this match…", + }, + "victory": { + 1: "It's been fun… Let's have another last battle again someday…", + }, + }, + "baker": { + "encounter": { + 1: "Hope you're ready to taste defeat!" + }, + "victory": { + 1: "I'll bake a comeback." + }, + }, + "biker": { + "encounter": { + 1: "Time to rev up and leave you in the dust!" + }, + "victory": { + 1: "I'll tune up for the next race." + }, + }, + "firebreather": { + "encounter": { + 1: "My flames shall devour you!", + 2: "My soul is on fire. I'll show you how hot it burns!", + 3: "Step right up and take a look!" + }, + "victory": { + 1: "I burned down to ashes...", + 2: "Yow! That's hot!", + 3: "Ow! I scorched the tip of my nose!" + }, + }, + "sailor": { + "encounter": { + 1: "Matey, you're walking the plank if you lose!", + 2: "Come on then! My sailor's pride is at stake!", + 3: "Ahoy there! Are you seasick?" + }, + "victory": { + 1: "Argh! Beaten by a kid!", + 2: "Your spirit sank me!", + 3: "I think it's me that's seasick..." + }, + }, + + "archer": { + "encounter": { + 1: "Before you go any further, let's see how you far against us, Team Rocket!", + 2: "I have received reports that your skills are not insignificant. Let's see if they are true.", + 3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." + }, + "victory": { + 1: "What a blunder!", + 2: "With my current skills, I was not up to the task after all.", + 3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." + }, + }, + "ariana": { + "encounter": { + 1: `Hold it right there! We can't someone on the loose." + $It's harmful to Team Rocket's pride, you see.`, + 2: `I don't know or care if what I'm doing is right or wrong... + $I just put my faith in Giovanni and do as I am told`, + 3: "Your trip ends here. I'm going to take you down!" + }, + "victory": { + 1: `Tch, you really are strong. It's too bad. + $If you were to join Team Rocket, you could become an Executive.`, + 2: "I... I'm shattered...", + 3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" + }, + }, + "proton": { + "encounter": { + 1: "What do you want? If you interrupt our work, don't expect any mercy!", + 2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket… + $I strongly urge you not to interfere with our business!`, + 3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" + }, + "victory": { + 1: "The fortress came down!", + 2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", + 3: "I am defeated… But I will not forget this!" + }, + }, + + "petrel": { + "encounter": { + 1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. + $The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni? + $I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, + 2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + 3: "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + 1: "OK, OK. I'll tell you where he is.", + 2: "I… I couldn't do a thing… Giovanni, please forgive me…", + 3: "No, I can't let this affect me. I have to inform the others…" + }, + }, + "tabitha": { + "encounter": { + 1: "Hehehe! So you've come all the way here! But you're too late!", + 2: `Hehehe... Got here already, did you? We underestimated you! But this is it! + $I'm a cut above the Grunts you've seen so far. I'm not stalling for time. + $I'm going to pulverize you!`, + 3: "I'm going to give you a little taste of pain! Resign yourself to it!" + }, + "victory": { + 1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! + $If you get lost now, you won't have to face a sound whipping!`, + 2: "Hehehe... So, I lost, too...", + 3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." + }, + }, + "courtney": { + "encounter": { + 1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", + 2: "... Well then...Deleting...", + 3: "...Ha. ...Analyzing... ...Hah♪" + }, + "victory": { + 1: "... ...Change...the world.", + 2: `As anticipated. Unanticipated. You. Target lock...completed. + $Commencing...experiment. You. Forever. Aha... ♪`, + 3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" + }, + }, + "shelly": { + "encounter": { + 1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? + $You're either absolutely fearless, simply ignorant, or both! + $You're so cute, you're disgusting! I'll put you down`, + 2: "What's this? Who's this spoiled brat?", + 3: "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + 1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. + $We'll have to pull out. But this isn't the last you'll see of Team Aqua! + $We have other plans! Don't you forget it!`, + 2: "Ahhh?! Did I go too easy on you?!", + 3: `Uh. Are you telling me you've upped your game even more during the fight? + $You're a brat with a bright future… My Pokémon and I don't have any strength left to fight… + $Go on… Go and be destroyed by Archie.` + }, + }, + "matt": { + "encounter": { + 1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", + 2: "Oho! You! You're that funny kid!", + 3: "What are you doing here? Did you follow us?" + }, + "victory": { + 1: "All right then, until the Boss has time for you, I'll be your opponent!", + 2: `I can feel it! I can feel it, all right! The strength coming offa you! + $More! I still want more! But looks like we're outta time...`, + 3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" + }, + }, + "mars": { + "encounter": { + 1: "I'm Mars, one of Team Galactic's top Commanders.", + 2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + 3: "Feeling nervous? You should be!" + }, + "victory": { + 1: "This can't be happening! How did I lose?!", + 2: "You have some skill, I'll give you that.", + 3: "Defeated... This was a costly mistake." + } + }, + "jupiter": { + "encounter": { + 1: "Jupiter, Commander of Team Galactic, at your service.", + 2: "Resistance is futile. Team Galactic will prevail!", + 3: "You're trembling... scared already?" + }, + "victory": { + 1: "No way... I lost?!", + 2: "Impressive, you've got guts!", + 3: "Losing like this... How embarrassing." + } + }, + "saturn": { + "encounter": { + 1: "I am Saturn, Commander of Team Galactic.", + 2: "Our mission is absolute. Any hindrance will be obliterated!", + 3: "Is that fear I see in your eyes?" + }, + "victory": { + 1: "Impossible... Defeated by you?!", + 2: "You have proven yourself a worthy adversary.", + 3: "Bestowed in defeat... This is unacceptable." + }}, + "zinzolin": { + "encounter": { + 1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", + 2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", + 3: "You're an impressive Trainer to have made it this far. But it ends here." + }, + "victory": { + 1: "Ghetsis... I have failed you...", + 2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", + 3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } + }, + "rood": { + "encounter": { + 1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + 2: "Oh, this icy wind... I never thought I'd have to fight here!", + 3: "You are a remarkable Trainer to have made it this far. But this is where it ends." + }, + "victory": { + 1: "Ghetsis... I have failed my mission...", + 2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", + 3: "Hm. You are a talented Trainer, but unfortunately not talented enough." + } + }, + "xerosic": { + "encounter": { + 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", + 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", + 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" + }, + "victory": { + 1: "Ah, you're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You did it! To the victor go the spoils!", + 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + 1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + 2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + 3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + 1: "You're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You've done well. Victory is yours.", + 3: "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, + "rocket_grunt": { + "encounter": { + 1: "Se prepara pra encrenca!", + 2: "We're pulling a big job here! Get lost, kid!", + 3: "Hand over your Pokémon, or face the wrath of Team Rocket!", + 4: "You're about to experience the true terror of Team Rocket!", + 5: "Hey, kid! Me am a Team Rocket member kind of guy!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "Equipe Rocket decolando de novo!", + 2: "Oh no! I dropped the Lift Key!", + 3: "I blew it!", + 4: "My associates won't stand for this!", + 5: "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" //Use of wrong grammar is deliberate. + }, + }, + "magma_grunt": { + "encounter": { + 1: "Se você se meter com a Equipe Magma, não teremos piedade!", + 2: "You'd better not interfere with our plans! We're making the world a better place!", + 3: "You're in the way! Team Magma has no time for kids like you!", + 4: "I hope you brought marshmallows because things are about to heat up!", + 5: "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" + }, + "victory": { + 1: "Ahn? Eu perdi?!", + 2: "I can't believe I lost! I even skipped lunch for this", + 3: "No way! You're just a kid!", + 4: "Urrrgh... I should've ducked into our hideout right away...", + 5: "You beat me... Do you think the boss will dock my pay for this?" + }, + }, + "aqua_grunt": { + "encounter": { + 1: "Não pegamos leve com quem se mete com a Equipe Aqua, nem mesmo crianças!", + 2: "Grrr... You've got some nerve meddling with Team Aqua!", + 3: "You're about to get soaked! And not just from my water Pokémon!", + 4: "We, Team Aqua, exist for the good of all!", + 5: "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" + }, + "victory": { + 1: "Tá de brincadeira!", + 2: "Arrgh, I didn't count on being meddled with by some meddling kid!", + 3: "I lost?! Guess I'll have to swim back to the hideout now...", + 4: "Oh, man, what a disaster... The boss is going to be furious...", + 5: "You beat me... Do you think the boss will make me walk the plank for this?" + }, + }, + "galactic_grunt": { + "encounter": { + 1: "Não mexe com a Equipe Galáctica!", + 2: "Witness the power of our technology and the future we envision!", + 3: "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + 4: "Get ready to lose!", + 5: "Hope you're ready for a cosmic beatdown!" + }, + "victory": { + 1: "Fui amassado...", + 2: "This setback means nothing in the grand scheme.", + 3: "Our plans are bigger than this defeat.", + 4: "How?!", + 5: "Note to self: practice Pokémon battling, ASAP." + }, + }, + "plasma_grunt": { + "encounter": { + 1: "Não toleramos pessoas que pensam diferente de nós!", + 2: "If I win against you, release your Pokémon!", + 3: "If you get in the way of Team Plasma, I'll take care of you!", + 4: "Team Plasma will liberate Pokémon from selfish humans like you!", + 5: "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." + }, + "victory": { + 1: "Plasmaaaaaaaaa!", + 2: "How could I lose...", + 3: "...What a weak Pokémon, I'll just have to go steal some better ones!", + 4: "Great plans are always interrupted.", + 5: "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" + }, + }, + "flare_grunt": { + "encounter": { + 1: "Your Pokémon are no match for the elegance of Team Flare.", + 2: "Hope you brought your sunglasses, because things are about to get bright!", + 3: "Team Flare will cleanse the world of imperfection!", + 4: "Prepare to face the brilliance of Team Flare!", + 5: "Fashion is most important to us!" + }, + "victory": { + 1: "The future doesn't look bright for me.", + 2: "Perhaps there's more to battling than I thought. Back to the drawing board.", + 3: "Gahh?! I lost?!", + 4: "Even in defeat, Team Flare's elegance shines through.", + 5: "You may have beaten me, but when I lose, I go out in style!" + }, + }, + "rocket_boss_giovanni_1": { + "encounter": { + 1: "So! I must say, I am impressed you got here!" + }, + "victory": { + 1: "WHAT! This cannot be!" + }, + "defeat": { + 1: "Mark my words. Not being able to measure your own strength shows that you are still a child." + } + }, + "rocket_boss_giovanni_2": { + "encounter": { + 1: "My old associates need me... Are you going to get in my way?" + }, + "victory": { + 1: "How is this possible...? The precious dream of Team Rocket has become little more than an illusion..." + }, + "defeat": { + 1: "Team Rocket will be reborn again, and I will rule the world!" + } + }, + "magma_boss_maxie_1": { + "encounter": { + 1: "I will bury you by my own hand. I hope you appreciate this honor!" + }, + "victory": { + 1: "Ugh! You are... quite capable...\nI fell behind, but only by an inch..." + }, + "defeat": { + 1: "Team Magma will prevail!" + } + }, + "magma_boss_maxie_2": { + "encounter": { + 1: `You are the final obstacle remaining between me and my goals. + $Brace yourself for my ultimate attack! Fuhahaha!` + }, + "victory": { + 1: "This... This is not.. Ngh..." + }, + "defeat": { + 1: "And now... I will transform this planet to a land ideal for humanity." + } + }, + "aqua_boss_archie_1": { + "encounter": { + 1: "I'm the leader of Team Aqua, so I'm afraid it's the rope's end for you." + }, + "victory": { + 1: "Let's meet again somewhere. I'll be sure to remember that face." + }, + "defeat": { + 1: "Brilliant! My team won't hold back now!" + } + }, + "aqua_boss_archie_2": { + "encounter": { + 1: "I've been waiting so long for this day to come.\nThis is the true power of my team!" + }, + "victory": { + 1: "Like I figured..." + }, + "defeat": { + 1: "I'll return everything in this world to its original, pure state!!" + } + }, + "galactic_boss_cyrus_1": { + "encounter": { + 1: `You were compelled to come here by such vacuous sentimentality. + $I will make you regret paying heed to your heart!` + }, + "victory": { + 1: "Interesting. And quite curious." + }, + "defeat": { + 1: "I will create my new world..." + } + }, + "galactic_boss_cyrus_2": { + "encounter": { + 1: `So we meet again. It seems our fates have become intertwined. + $But here and now, I will finally break that bond!` + }, + "victory": { + 1: "How? How? HOW?!" + }, + "defeat": { + 1: "Farewell." + } + }, + "plasma_boss_ghetsis_1": { + "encounter": { + 1: "I won't allow anyone to stop me! No matter who does what!" + }, + "victory": { + 1: "How can this be? I'm the creator of Team Plasma! I'm perfect!" + }, + "defeat": { + 1: "I am the perfect ruler of a perfect new world! Mwa ha ha!" + } + }, + "plasma_boss_ghetsis_2": { + "encounter": { + 1: "Come now! I want to see your face at the moment you lose all hope!" + }, + "victory": { + 1: "My calculations... No! My careful schemes! The world should be mine!" + }, + "defeat": { + 1: "Kyurem! Use Absofusion!" + } + }, + "flare_boss_lysandre_1": { + "encounter": { + 1: "Do you want to stop me? Show me in battle." + }, + "victory": { + 1: "You are here to stop me. But I ask you to wait. " + }, + "defeat": { + 1: "Pokemon...Shall no longer exist." + } + }, + "flare_boss_lysandre_2": { + "encounter": { + 1: "The future you want, or the future I want... Let us see which one is more deserving, shall we?" + }, + "victory": { + 1: "Whaugh!" + }, + "defeat": { + 1: "Fools with no vision will continue to befoul this beautiful world." + } + }, + "brock": { + "encounter": { + 1: "My expertise on Rock-type Pokémon will take you down! Come on!", + 2: "My rock-hard willpower will overwhelm you!", + 3: "Allow me to show you the true strength of my Pokémon!" + }, + "victory": { + 1: "Your Pokémon's strength have overcome my rock-hard defenses!", + 2: "The world is huge! I'm glad to have had a chance to battle you.", + 3: "Perhaps I should go back to pursuing my dream as a Pokémon Breeder…" + }, + "defeat": { + 1: "The best offense is a good defense!\nThat's my way of doing things!", + 2: "Come study rocks with me next time to better learn how to fight them!", + 3: "Hah, all my traveling around the regions is paying off!" + } + }, + "misty": { + "encounter": { + 1: "My policy is an all out offensive with Water-type Pokémon!", + 2: "Hiya, I'll show you the strength of my aquatic Pokémon!", + 3: "My dream was to go on a journey and battle powerful trainers…\nWill you be a sufficient challenge?" + }, + "victory": { + 1: "You really are strong… I'll admit that you are skilled…", + 2: "Grrr… You know you just got lucky, right?!", + 3: "Wow, you're too much! I can't believe you beat me!" + }, + "defeat": { + 1: "Was the mighty Misty too much for you?", + 2: "I hope you saw my Pokémon's elegant swimming techniques!", + 3: "Your Pokémon were no match for my pride and joys!" + } + }, + "lt_surge": { + "encounter": { + 1: "My Electric Pokémon saved me during the war! I'll show you how!", + 2: "Ten-hut! I'll shock you into surrender!", + 3: "I'll zap you just like I do to all my enemies in battle!" + }, + "victory": { + 1: "Whoa! Your team's the real deal, kid!", + 2: "Aaargh, you're strong! Even my electric tricks lost against you.", + 3: "That was an absolutely shocking loss!" + }, + "defeat": { + 1: "Oh yeah! When it comes to Electric-type Pokémon, I'm number one in the world!", + 2: "Hahaha! That was an electrifying battle, kid!", + 3: "A Pokémon battle is war, and I have showed you first-hand combat!" + } + }, + "erika": { + "encounter": { + 1: "Ah, the weather is lovely here…\nOh, a battle? Very well then.", + 2: "My Pokémon battling skills rival that of my flower arranging skills.", + 3: "Oh, I hope the pleasant aroma of my Pokémon doesn't put me to sleep again…", + 4: "Seeing flowers in a garden is so soothing." + }, + "victory": { + 1: "Oh! I concede defeat.", + 2: "That match was most delightful.", + 3: "Ah, it appears it is my loss…", + 4: "Oh, my goodness." + }, + "defeat": { + 1: "I was afraid I would doze off…", + 2: "Oh my, it seems my Grass Pokémon overwhelmed you.", + 3: "That battle was such a soothing experience.", + 4: "Oh… Is that all?" + } + }, + "janine": { + "encounter": { + 1: "I am mastering the art of poisonous attacks.\nI shall spar with you today!", + 2: "Father trusts that I can hold my own.\nI will prove him right!", + 3: "My ninja techniques are only second to my Father's!\nCan you keep up?" + }, + "victory": { + 1: "Even now, I still need training… I understand.", + 2: "Your battle technique has outmatched mine.", + 3: "I'm going to really apply myself and improve my skills." + }, + "defeat": { + 1: "Fufufu… the poison has sapped all your strength to battle.", + 2: "Ha! You didn't stand a chance against my superior ninja skills!", + 3: "Father's faith in me has proven to not be misplaced." + } + }, + "sabrina": { + "encounter": { + 1: "Through my psychic ability, I had a vision of your arrival!", + 2: "I dislike fighting, but if you wish, I will show you my powers!", + 3: "I can sense great ambition in you. I shall see if it not unfounded." + }, + "victory": { + 1: "Your power… It far exceeds what I foresaw…", + 2: "I failed to accurately predict your power.", + 3: "Even with my immense psychic powers, I cannot sense another as strong as you." + }, + "defeat": { + 1: "This victory… It is exactly as I foresaw in my visions!", + 2: "Perhaps it was another I sensed a great desire in…", + 3: "Hone your abilities before recklessly charging into battle.\nYou never know what the future may hold if you do…" + } + }, + "blaine": { + "encounter": { + 1: "Hah! Hope you brought a Burn Heal!", + 2: "My fiery Pokémon will incinerate all challengers!", + 3: "Get ready to play with fire!" + }, + "victory": { + 1: "I have burned down to nothing! Not even ashes remain!", + 2: "Didn't I stoke the flames high enough?", + 3: "I'm all burned out… But this makes my motivation to improve burn even hotter!" + }, + "defeat": { + 1: "My raging inferno cannot be quelled!", + 2: "My Pokémon have been powered up with the heat from this victory!", + 3: "Hah! My passion burns brighter than yours!" + } + }, + "giovanni": { + "encounter": { + 1: "I, the leader of Team Rocket, will make you feel a world of pain!", + 2: "My training here will be vital before I am to face my old associates again.", + 3: "I do not think you are prepared for the level of failure you are about to experience!" + }, + "victory": { + 1: "WHAT! Me, lose?! There is nothing I wish to say to you!", + 2: "Hmph… You could never understand what I hope to achieve.", + 3: "This defeat is merely delaying the inevitable.\nI will rise Team Rocket from the ashes in due time." + }, + "defeat": { + 1: "Not being able to measure your own strength shows that you are still but a child.", + 2: "Do not try to interfere with me again.", + 3: "I hope you understand how foolish challenging me was." + } + }, + "roxanne": { + "encounter": { + 1: "Would you kindly demonstrate how you battle?", + 2: "You can learn many things by battling many trainers.", + 3: "Oh, you caught me strategizing.\nWould you like to battle?" + }, + "victory": { + 1: "Oh, I appear to have lost.\nI understand.", + 2: "It seems that I still have so much more to learn when it comes to battle.", + 3: "I'll take what I learned here today to heart." + }, + "defeat": { + 1: "I have learned many things from our battle.\nI hope you have too.", + 2: "I look forward to battling you again.\nI hope you'll use what you've learned here.", + 3: "I won due to everything I have learned." + } + }, + "brawly": { + "encounter": { + 1: "Oh man, a challenger!\nLet's see what you can do!", + 2: "You seem like a big splash.\nLet's battle!", + 3: "Time to create a storm!\nLet's go!" + }, + "victory": { + 1: "Oh woah, you've washed me out!", + 2: "You surfed my wave and crashed me down!", + 3: "I feel like I'm lost in Granite Cave!" + }, + "defeat": { + 1: "Haha, I surfed the big wave!\nChallenge me again sometime.", + 2: "Surf with me again some time!", + 3: "Just like the tides come in and out, I hope you return to challenge me again." + } + }, + "wattson": { + "encounter": { + 1: "Time to get shocked!\nWahahahaha!", + 2: "I'll make sparks fly!\nWahahahaha!", + 3: "I hope you brought Paralyz Heal!\nWahahahaha!" + }, + "victory": { + 1: "Seems like I'm out of charge!\nWahahahaha!", + 2: "You've completely grounded me!\nWahahahaha!", + 3: "Thanks for the thrill!\nWahahahaha!" + }, + "defeat": { + 1: "Recharge your batteries and challenge me again sometime!\nWahahahaha!", + 2: "I hope you found our battle electrifying!\nWahahahaha!", + 3: "Aren't you shocked I won?\nWahahahaha!" + } + }, + "flannery": { + "encounter": { + 1: "Nice to meet you! Wait, no…\nI will crush you!", + 2: "I've only been a leader for a little while, but I'll smoke you!", + 3: "It's time to demonstrate the moves my grandfather has taught me! Let's battle!" + }, + "victory": { + 1: "You remind me of my grandfather…\nNo wonder I lost.", + 2: "Am I trying too hard?\nI should relax, can't get too heated.", + 3: "Losing isn't going to smother me out.\nTime to reignite training!" + }, + "defeat": { + 1: "I hope I've made my grandfather proud…\nLet's battle again some time.", + 2: "I…I can't believe I won!\nDoing things my way worked!", + 3: "Let's exchange burning hot moves again soon!" + } + }, + "norman": { + "encounter": { + 1: "I'm surprised you managed to get here.\nLet's battle.", + 2: "I'll do everything in my power as a Gym Leader to win.\nLet's go!", + 3: "You better give this your all.\nIt's time to battle!" + }, + "victory": { + 1: "I lost to you…?\nRules are rules, though.", + 2: "Was moving from Olivine a mistake…?", + 3: "I can't believe it.\nThat was a great match." + }, + "defeat": { + 1: "We both tried our best.\nI hope we can battle again soon.", + 2: "You should try challenging my kid instead.\nYou might learn something!", + 3: "Thank you for the excellent battle.\nBetter luck next time." + } + }, + "winona": { + "encounter": { + 1: "I've been soaring the skies looking for prey…\nAnd you're my target!", + 2: "No matter how our battle is, my Flying Pokémon and I will triumph with grace. Let's battle!", + 3: "I hope you aren't scared of heights.\nLet's ascend!" + }, + "victory": { + 1: "You're the first Trainer I've seen with more grace than I.\nExcellently played.", + 2: "Oh, my Flying Pokémon have plummeted!\nVery well.", + 3: "Though I may have fallen, my Pokémon will continue to fly!" + }, + "defeat": { + 1: "My Flying Pokémon and I will forever dance elegantly!", + 2: "I hope you enjoyed our show.\nOur graceful dance is finished.", + 3: "Won't you come see our elegant choreography again?" + } + }, + "tate": { + "encounter": { + 1: "Hehehe…\nWere you surprised to see me without my sister?", + 2: "I can see what you're thinking…\nYou want to battle!", + 3: "How can you defeat someone…\nWho knows your every move?" + }, + "victory": { + 1: "It can't be helped…\nI miss Liza…", + 2: "Your bond with your Pokémon was stronger than mine.", + 3: "If I were with Liza, we would have won.\nWe can finish each other's thoughts!" + }, + "defeat": { + 1: "My Pokémon and I are superior!", + 2: "If you can't even defeat me, you'll never be able to defeat Liza either.", + 3: "It's all thanks to my strict training with Liza.\nI can make myself one with Pokémon." + } + }, + "liza": { + "encounter": { + 1: "Fufufu…\nWere you surprised to see me without my brother?", + 2: "I can determine what you desire…\nYou want to battle, don't you?", + 3: "How can you defeat someone…\nWho's one with their Pokémon?" + }, + "victory": { + 1: "It can't be helped…\nI miss Tate…", + 2: "Your bond with your Pokémon…\nIt's stronger than mine.", + 3: "If I were with Tate, we would have won.\nWe can finish each other's sentences!" + }, + "defeat": { + 1: "My Pokémon and I are victorious.", + 2: "If you can't even defeat me, you'll never be able to defeat Tate either.", + 3: "It's all thanks to my strict training with Tate.\nI can synchronize myself with my Pokémon." + } + }, + "juan": { + "encounter": { + 1: "Now's not the time to act coy.\nLet's battle!", + 2: "Ahahaha, You'll be witness to my artistry with Water Pokémon!", + 3: "A typhoon approaches!\nWill you be able to test me?", + 4: "Please, you shall bear witness to our artistry.\nA grand illusion of water sculpted by my Pokémon and myself!" + }, + "victory": { + 1: "You may be a genius who can take on Wallace!", + 2: "I focused on elegance while you trained.\nIt's only natural that you defeated me.", + 3: "Ahahaha!\nVery well, You have won this time.", + 4: "From you, I sense the brilliant shine of skill that will overcome all." + }, + "defeat": { + 1: "My Pokémon and I have sculpted an illusion of Water and come out victorious.", + 2: "Ahahaha, I have won, and you have lost.", + 3: "Shall I loan you my outfit? It may help you battle!\nAhahaha, I jest!", + 4: "I'm the winner! Which is to say, you lost." + } + }, + "crasher_wake": { + "encounter": { + 1: "Crash! Crash! Watch out!\nCrasher Wake…is…heeere!", + 2: "Crash! Crash! Crasher Wake!", + 3: "I'm the tidal wave of power to wash you away!" + }, + "victory": { + 1: "That puts a grin on my face!\nGuhahaha! That was a blast!", + 2: "Hunwah! It's gone and ended!\nHow will I say this…\nI want more! I wanted to battle a lot more!", + 3: "WHAAAAT!?" + }, + "defeat": { + 1: "Yeeeeah! That's right!", + 2: "I won, but I want more! I wanted to battle a lot more!", + 3: "So long!" + } + }, + "falkner": { + "encounter": { + 1: "I'll show you the real power of the magnificent bird Pokémon!", + 2: "Winds, stay with me!", + 3: "Dad! I hope you're watching me battle from above!" + }, + "victory": { + 1: "I understand… I'll bow out gracefully.", + 2: "A defeat is a defeat. You are strong indeed.", + 3: "…Shoot! Yeah, I lost." + }, + "defeat": { + 1: "Dad! I won with your cherished bird Pokémon…", + 2: "Bird Pokémon are the best after all!", + 3: "Feels like I'm catching up to my dad!" + } + }, + "nessa": { + "encounter": { + 1: "No matter what kind of plan your refined mind may be plotting, my partner and I will be sure to sink it.", + 2: "I'm not here to chat. I'm here to win!", + 3: "This is a little gift from my Pokémon… I hope you can take it!" + }, + "victory": { + 1: "You and your Pokémon are just too much…", + 2: "How…? How can this be?!", + 3: "I was totally washed away!" + }, + "defeat": { + 1: "The raging wave crashes again!", + 2: "Time to ride the wave of victory!", + 3: "Ehehe!" + } + }, + "melony": { + "encounter": { + 1: "I'm not going to hold back!", + 2: "All righty, I suppose we should get started.", + 3: "I'll freeze you solid!" + }, + "victory": { + 1: "You… You're pretty good, huh?", + 2: "If you find Gordie around, be sure to give him a right trashing, would you?", + 3: "I think you took breaking the ice a little too literally…" + }, + "defeat": { + 1: "Now do you see how severe battles can be?", + 2: "Hee! Looks like I went and won again!", + 3: "Are you holding back?" + } + }, + "marlon": { + "encounter": { + 1: "You look strong! Shoots! Let's start!", + 2: "I'm strong like the ocean's wide. You're gonna get swept away, fo' sho'.", + 3: "Oh ho, so I'm facing you! That's off the wall." + }, + "victory": { + 1: "You totally rocked that! You're raising some wicked Pokémon. You got this Trainer thing down!", + 2: "You don't just look strong, you're strong fo' reals! Eh, I was swept away, too!", + 3: "You're strong as a gnarly wave!" + }, + "defeat": { + 1: "You're tough, but it's not enough to sway the sea, 'K!", + 2: "Hee! Looks like I went and won again!", + 3: "Sweet, sweet victory!" + } + }, + "shauntal": { + "encounter": { + 1: "Excuse me. You're a challenger, right?\nI'm the Elite Four's Ghost-type Pokémon user, Shauntal, and I shall be your opponent.", + 2: "I absolutely love writing about Trainers who come here and the Pokémon they train.\nCould I use you and your Pokémon as a subject?", + 3: "Every person who works with Pokémon has a story to tell.\nWhat story is about to be told?" + }, + "victory": { + 1: "Wow. I'm dumbstruck!", + 2: "S-sorry! First, I must apologize to my Pokémon…\n\nI'm really sorry you had a bad experience because of me!", + 3: "Even in light of that, I'm still one of the Elite Four!" + }, + "defeat": { + 1: "Eheh.", + 2: "That gave me excellent material for my next novel!", + 3: "And so, another tale ends…" + } + }, + "marshal": { + "encounter": { + 1: "My mentor, Alder, sees your potential as a Trainer and is taking an interest in you.\nIt is my intention to test you--to take you to the limits of your strength. Kiai!", + 2: "Victory, decisive victory, is my intention! Challenger, here I come!", + 3: "In myself, I seek to develop the strength of a fighter and shatter any weakness in myself!\nPrevailing with the force of my convictions!" + }, + "victory": { + 1: "Whew! Well done!", + 2: "As your battles continue, aim for even greater heights!", + 3: "The strength shown by you and your Pokémon has deeply impressed me…" + }, + "defeat": { + 1: "Hmm.", + 2: "That was good battle.", + 3: "Haaah! Haaah! Haiyaaaah!" + } + }, + "cheren": { + "encounter": { + 1: "You remind me of an old friend. That makes me excited about this Pokémon battle!", + 2: `Pokémon battles have no meaning if you don't think why you battle. + $Or better said, it makes battling together with Pokémon meaningless.`, + 3: "My name's Cheren! I'm a Gym Leader and a teacher! Pleasure to meet you." + }, + "victory": { + 1: "Thank you! I saw what was missing in me.", + 2: "Thank you! I feel like I saw a little of the way toward my ideals.", + 3: "Hmm… This is problematic." + }, + "defeat": { + 1: "As a Gym Leader, I aim to be a wall for you to overcome.", + 2: "All right!", + 3: "I made it where I am because Pokémon were by my side.\nPerhaps we need to think about why Pokémon help us not in terms of Pokémon and Trainers but as a relationship between living beings." + } + }, + "chili": { + "encounter": { + 1: "Yeeeeooow! Time to play with FIRE!! I'm the strongest of us brothers!", + 2: "Ta-da! The Fire-type scorcher Chili--that's me--will be your opponent!", + 3: "I'm going to show you what me and my blazing Fire types can do!" + }, + "victory": { + 1: "You got me. I am… burned… out…", + 2: "Whoa ho! You're on fire!", + 3: "Augh! You got me!" + }, + "defeat": { + 1: "I'm on fire! Play with me, and you'll get burned!", + 2: "When you play with fire, you get burned!", + 3: "I mean, c'mon, your opponent was me! You didn't have a chance!" + } + }, + "cilan": { + "encounter": { + 1: `Nothing personal... No hard feelings... Me and my Grass-type Pokémon will... + $Um... We're gonna battle come what may.`, + 2: "So, um, if you're OK with me, I'll, um, put everything I've got into being, er, you know, your opponent.", + 3: "OK… So, um, I'm Cilan, I like Grass-type Pokémon." + }, + "victory": { + 1: "Er… Is it over now?", + 2: `…What a surprise. You are very strong, aren't you? + $I guess my brothers wouldn't have been able to defeat you either…`, + 3: "…Huh. Looks like my timing was, um, off?" + }, + "defeat": { + 1: "Huh? Did I win?", + 2: `I guess… + $I suppose I won, because I've been competing with my brothers Chili and Cress, and we all were able to get tougher.`, + 3: "It…it was quite a thrilling experience…" + } + }, + "roark": { + "encounter": { + 1: "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", + 2: "Here goes! These are my rocking Pokémon, my pride and joy!", + 3: "Rock-type Pokémon are simply the best!", + 4: "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!" + }, + "victory": { + 1: "W-what? That can't be! My buffed-up Pokémon!", + 2: "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", + 3: "With skill like yours, it's natural for you to win.", + 4: "Wh-what?! It can't be! Even that wasn't enough?", + 5: "I blew it." + }, + "defeat": { + 1: "See? I'm proud of my rocking battle style!", + 2: "Thanks! The battle gave me confidence that I may be able to beat my dad!", + 3: "I feel like I just smashed through a really stubborn boulder!" + } + }, + "morty": { + "encounter": { + 1: `With a little more, I could see a future in which I meet the legendary Pokémon. + $You're going to help me reach that level!`, + 2: `It's said that a rainbow-hued Pokémon will come down to appear before a truly powerful Trainer. + $I believed that tale, so I have secretly trained here all my life. As a result, I can now see what others cannot. + $I see a shadow of the person who will make the Pokémon appear. + $I believe that person is me! You're going to help me reach that level!`, + 3: "Whether you choose to believe or not, mystic power does exist.", + 4: "You can bear witness to the fruits of my training.", + 5: "You must make your soul one with that of Pokémon. Can you do this?", + 6: "Say, do you want to be part of my training?" + }, + "victory": { + 1: "I'm not good enough yet…", + 2: `I see… Your journey has taken you to far-away places and you have witnessed much more than I. + $I envy you for that…`, + 3: "How is this possible…", + 4: `I don't think our potentials are so different. + $But you seem to have something more than that… So be it.`, + 5: "Guess I need more training.", + 6: "That's a shame." + }, + "defeat": { + 1: "I moved… one step ahead again.", + 2: "Fufufu…", + 3: "Wh-what?! It can't be! Even that wasn't enough?", + 4: "I feel like I just smashed through a really stubborn boulder!", + 5: "Ahahahah!", + 6: "I knew I would win!" + } + }, + "crispin": { + "encounter": { + 1: "I wanna win, so that's exactly what I'll do!", + 2: "I battle because I wanna battle! And you know what? That's how it should be!" + }, + "victory": { + 1: "I wanted to win…but I lost!", + 2: "I lost…'cause I couldn't win!" + }, + "defeat": { + 1: "Hey, wait a sec. Did I just win? I think I just won! Talk about satisfying!", + 2: "Wooo! That was amazing!" + } + }, + "amarys": { + "encounter": { + 1: `I want to be the one to help a certain person. That being the case, I cannot afford to lose. + $… Our battle starts now.`, + }, + "victory": { + 1: "I am… not enough, I see." + }, + "defeat": { + 1: "Victory belongs to me. Well fought." + } + }, + "lacey": { + "encounter": { + 1: "I'll be facing you with my usual party as a member of the Elite Four." + }, + "victory": { + 1: "That was a great battle!" + }, + "defeat": { + 1: "Let's give your Pokémon a nice round of applause for their efforts!" + } + }, + "drayton": { + "encounter": { + 1: `Man, I love chairs. Don't you love chairs? What lifesavers. + $I don't get why everyone doesn't just sit all the time. Standing up's tiring work!`, + }, + "victory": { + 1: "Guess I should've expected that!" + }, + "defeat": { + 1: "Heh heh! Don't mind me, just scooping up a W over here. I get it if you're upset, but don't go full Kieran on me, OK?" + } + }, + "ramos": { + "encounter": { + 1: `Did yeh enjoy the garden playground I made with all these sturdy plants o' mine? + $Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?`, + }, + "victory": { + 1: "Yeh believe in yer Pokémon… And they believe in yeh, too… It was a fine battle, sprout." + }, + "defeat": { + 1: "Hohoho… Indeed. Frail little blades o' grass'll break through even concrete." + } + }, + "viola": { + "encounter": { + 1: `Whether it's the tears of frustration that follow a loss or the blossoming of joy that comes with victory… + $They're both great subjects for my camera! Fantastic! This'll be just fantastic! + $Now come at me!`, + 2: "My lens is always focused on victory--I won't let anything ruin this shot!" + }, + "victory": { + 1: "You and your Pokémon have shown me a whole new depth of field! Fantastic! Just fantastic!", + 2: `The world you see through a lens, and the world you see with a Pokémon by your side… + $The same world can look entirely different depending on your view.` + }, + "defeat": { + 1: "The photo from the moment of my victory will be a real winner, all right!", + 2: "Yes! I took some great photos!" + } + }, + "candice": { + "encounter": { + 1: `You want to challenge Candice? Sure thing! I was waiting for someone tough! + $But I should tell you, I'm tough because I know how to focus.`, + 2: `Pokémon, fashion, romance… It's all about focus! + $I'll show you just what I mean. Get ready to lose!` + }, + "victory": { + 1: "I must say, I'm warmed up to you! I might even admire you a little.", + 2: `Wow! You're great! You've earned my respect! + $I think your focus and will bowled us over totally. ` + }, + "defeat": { + 1: "I sensed your will to win, but I don't lose!", + 2: "See? Candice's focus! My Pokémon's focus is great, too!" + } + }, + "gardenia": { + "encounter": { + 1: "You have a winning aura about you. So, anyway, this will be fun. Let's have our battle!" + }, + "victory": { + 1: "Amazing! You're very good, aren't you?" + }, + "defeat": { + 1: "Yes! My Pokémon and I are perfectly good!" + } + }, + "aaron": { + "encounter": { + 1: "Ok! Let me take you on!" + }, + "victory": { + 1: "Battling is a deep and complex affair…" + }, + "defeat": { + 1: "Victory over an Elite Four member doesn't come easily." + } + }, + "cress": { + "encounter": { + 1: "That is correct! It shall be I and my esteemed Water types that you must face in battle!" + }, + "victory": { + 1: "Lose? Me? I don't believe this." + }, + "defeat": { + 1: "This is the appropriate result when I'm your opponent." + } + }, + "allister": { + "encounter": { + 1: "'M Allister.\nH-here… I go…" + }, + "victory": { + 1: `I nearly lost my mask from the shock… That was… + $Wow. I can see your skill for what it is.`, + }, + "defeat": { + 1: "Th-that was ace!" + } + }, + "clay": { + "encounter": { + 1: "Harrumph! Kept me waitin', didn't ya, kid? All right, time to see what ya can do!" + }, + "victory": { + 1: "Man oh man… It feels good to go all out and still be defeated!" + }, + "defeat": { + 1: `What's important is how ya react to losin'. + $That's why folks who use losin' as fuel to get better are tough.`, + } + }, + "kofu": { + "encounter": { + 1: "I'mma serve you a full course o' Water-type Pokémon! Don't try to eat 'em, though!" + }, + "victory": { + 1: "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!" + }, + "defeat": { + 1: "You come back to see me again now, ya hear?" + } + }, + "tulip": { + "encounter": { + 1: "Allow me to put my skills to use to make your cute little Pokémon even more beautiful!" + }, + "victory": { + 1: "Your strength has a magic to it that cannot be washed away." + }, + "defeat": { + 1: "You know, in my line of work, people who lack talent in one area or the other often fade away quickly—never to be heard of again." + } + }, + "sidney": { + "encounter": { + 1: `I like that look you're giving me. I guess you'll give me a good match. + $That's good! Looking real good! All right! + $You and me, let's enjoy a battle that can only be staged here!`, + }, + "victory": { + 1: "Well, how do you like that? I lost! Eh, it was fun, so it doesn't matter." + }, + "defeat": { + 1: "No hard feelings, alright?" + } + }, + "phoebe": { + "encounter": { + 1: `While I trained, I gained the ability to commune with Ghost-type Pokémon. + $Yes, the bond I developed with Pokémon is extremely tight. + $So, come on, just try and see if you can even inflict damage on my Pokémon!`, + }, + "victory": { + 1: "Oh, darn. I've gone and lost." + }, + "defeat": { + 1: "I look forward to battling you again sometime!" + } + }, + "glacia": { + "encounter": { + 1: `All I have seen are challenges by weak Trainers and their Pokémon. + $What about you? It would please me to no end if I could go all out against you!`, + }, + "victory": { + 1: `You and your Pokémon… How hot your spirits burn! + $The all-consuming heat overwhelms. + $It's no surprise that my icy skills failed to harm you.`, + }, + "defeat": { + 1: "A fiercely passionate battle, indeed." + } + }, + "drake": { + "encounter": { + 1: `For us to battle with Pokémon as partners, do you know what it takes? Do you know what is needed? + $If you don't, then you will never prevail over me!`, + }, + "victory": { + 1: "Superb, it should be said." + }, + "defeat": { + 1: "I gave my all for that battle!" + } + }, + "wallace": { + "encounter": { + 1: `There's something about you… A difference in your demeanor. + $I think I sense that in you. Now, show me. Show me the power you wield with your Pokémon. + $And I, in turn, shall present you with a performance of illusions in water by me and my Pokémon!`, + }, + "victory": { + 1: `Bravo. I realize now your authenticity and magnificence as a Pokémon Trainer. + $I find much joy in having met you and your Pokémon. You have proven yourself worthy.`, + }, + "defeat": { + 1: "A grand illusion!" + } + }, + "lorelei": { + "encounter": { + 1: `No one can best me when it comes to icy Pokémon! Freezing moves are powerful! + $Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?`, + }, + "victory": { + 1: "How dare you!" + }, + "defeat": { + 1: "There's nothing you can do once you're frozen." + } + }, + "will": { + "encounter": { + 1: `I have trained all around the world, making my psychic Pokémon powerful. + $I can only keep getting better! Losing is not an option!`, + }, + "victory": { + 1: "I… I can't… believe it…" + }, + "defeat": { + 1: "That was close. I wonder what it is that you lack." + } + }, + "malva": { + "encounter": { + 1: `I feel like my heart might just burst into flames. + $I'm burning up with my hatred for you, runt!`, + }, + "victory": { + 1: "What news… So a new challenger has defeated Malva!" + }, + "defeat": { + 1: "I am delighted! Yes, delighted that I could squash you beneath my heel." + } + }, + "hala": { + "encounter": { + 1: "Old Hala is here to make you holler!" + }, + "victory": { + 1: "I could feel the power you gained on your journey." + }, + "defeat": { + 1: "Haha! What a delightful battle!" + } + }, + "molayne": { + "encounter": { + 1: `I gave the captain position to my cousin Sophocles, but I'm confident in my ability. + $My strength is like that of a supernova!`, + }, + "victory": { + 1: "I certainly found an interesting Trainer to face!" + }, + "defeat": { + 1: "Ahaha. What an interesting battle." + } + }, + "rika": { + "encounter": { + 1: "I'd say I'll go easy on you, but… I'd be lying! Think fast!" + }, + "victory": { + 1: "Not bad, kiddo." + }, + "defeat": { + 1: "Nahahaha! You really are something else, kiddo!" + } + }, + "bruno": { + "encounter": { + 1: "We will grind you down with our superior power! Hoo hah!" + }, + "victory": { + 1: "Why? How could I lose?" + }, + "defeat": { + 1: "You can challenge me all you like, but the results will never change!" + } + }, + "bugsy": { + "encounter": { + 1: "I'm Bugsy! I never lose when it comes to bug Pokémon!" + }, + "victory": { + 1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win." + }, + "defeat": { + 1: "Thanks! Thanks to our battle, I was also able to make progress in my research!" + } + }, + "koga": { + "encounter": { + 1: "Fwahahahaha! Pokémon are not merely about brute force--you shall see soon enough!" + }, + "victory": { + 1: "Ah! You've proven your worth!" + }, + "defeat": { + 1: "Have you learned to fear the techniques of the ninja?" + } + }, + "bertha": { + "encounter": { + 1: "Well, would you show this old lady how much you've learned?" + }, + "victory": { + 1: `Well! Dear child, I must say, that was most impressive. + $Your Pokémon believed in you and did their best to earn you the win. + $Even though I've lost, I find myself with this silly grin!`, + }, + "defeat": { + 1: "Hahahahah! Looks like this old lady won!" + } + }, + "lenora": { + "encounter": { + 1: "Well then, challenger, I'm going to research how you battle with the Pokémon you've so lovingly raised!" + }, + "victory": { + 1: "My theory about you was correct. You're more than just talented… You're motivated! I salute you!" + }, + "defeat": { + 1: "Ah ha ha! If you lose, make sure to analyze why, and use that knowledge in your next battle!" + } + }, + "siebold": { + "encounter": { + 1: "As long as I am alive, I shall strive onward to seek the ultimate cuisine... and the strongest opponents in battle!" + }, + "victory": { + 1: "I shall store my memory of you and your Pokémon forever away within my heart." + }, + "defeat": { + 1: `Our Pokémon battle was like food for my soul. It shall keep me going. + $That is how I will pay my respects to you for giving your all in battle!`, + } + }, + "roxie": { + "encounter": { + 1: "Get ready! I'm gonna knock some sense outta ya!" + }, + "victory": { + 1: "Wild! Your reason's already more toxic than mine!" + }, + "defeat": { + 1: "Hey, c'mon! Get serious! You gotta put more out there!" + } + }, + "olivia": { + "encounter": { + 1: "No introduction needed here. Time to battle me, Olivia!" + }, + "victory": { + 1: "Really lovely… Both you and your Pokémon…" + }, + "defeat": { + 1: "Mmm-hmm." + } + }, + "poppy": { + "encounter": { + 1: "Oooh! Do you wanna have a Pokémon battle with me?" + }, + "victory": { + 1: "Uagh?! Mmmuuuggghhh…" + }, + "defeat": { + 1: `Yaaay! I did it! I de-feet-ed you! You can come for… For… An avenge match? + $Come for an avenge match anytime you want!`, + } + }, + "agatha": { + "encounter": { + 1: "Pokémon are for battling! I'll show you how a real Trainer battles!" + }, + "victory": { + 1: "Oh my! You're something special, child!" + }, + "defeat": { + 1: "Bahaha. That's how a proper battle's done!" + } + }, + "flint": { + "encounter": { + 1: "Hope you're warmed up, cause here comes the Big Bang!" + }, + "victory": { + 1: "Incredible! Your moves are so hot, they make mine look lukewarm!" + }, + "defeat": { + 1: "Huh? Is that it? I think you need a bit more passion." + } + }, + "grimsley": { + "encounter": { + 1: "The winner takes everything, and there's nothing left for the loser." + }, + "victory": { + 1: "When one loses, they lose everything… The next thing I'll look for will be victory, too!" + }, + "defeat": { + 1: "If somebody wins, the person who fought against that person will lose." + } + }, + "caitlin": { + "encounter": { + 1: `It's me who appeared when the flower opened up. You who have been waiting… + $You look like a Pokémon Trainer with refined strength and deepened kindness. + $What I look for in my opponent is superb strength… + $Please unleash your power to the fullest!`, + }, + "victory": { + 1: "My Pokémon and I learned so much! I offer you my thanks." + }, + "defeat": { + 1: "I aspire to claim victory with elegance and grace." + } + }, + "diantha": { + "encounter": { + 1: `Battling against you and your Pokémon, all of you brimming with hope for the future… + $Honestly, it just fills me up with energy I need to keep facing each new day! It does!`, + }, + "victory": { + 1: "Witnessing the noble spirits of you and your Pokémon in battle has really touched my heart…" + }, + "defeat": { + 1: "Oh, fantastic! What did you think? My team was pretty cool, right?" + } + }, + "wikstrom": { + "encounter": { + 1: `Well met, young challenger! Verily am I the famed blade of hardened steel, Duke Wikstrom! + $Let the battle begin! En garde!`, + }, + "victory": { + 1: "Glorious! The trust that you share with your honorable Pokémon surpasses even mine!" + }, + "defeat": { + 1: `What manner of magic is this? My heart, it doth hammer ceaselessly in my breast! + $Winning against such a worthy opponent doth give my soul wings--thus do I soar!`, + } + }, + "acerola": { + "encounter": { + 1: "Battling is just plain fun! Come on, I can take you!" + }, + "victory": { + 1: "I'm… I'm speechless! How did you do it?!" + }, + "defeat": { + 1: "Ehaha! What an amazing victory!" + } + }, + "larry_elite": { + "encounter": { + 1: `Hello there… It's me, Larry. + $I serve as a member of the Elite Four too, yes… Unfortunately for me.`, + }, + "victory": { + 1: "Well, that took the wind from under our wings…" + }, + "defeat": { + 1: "It's time for a meeting with the boss." + } + }, + "lance": { + "encounter": { + 1: "I've been waiting for you. Allow me to test your skill.", + 2: "I thought that you would be able to get this far. Let's get this started." + }, + "victory": { + 1: "You got me. You are magnificent!", + 2: "I never expected another trainer to beat me… I'm surprised." + }, + "defeat": { + 1: "That was close. Want to try again?", + 2: "It's not that you are weak. Don't let it bother you." + } + }, + "karen": { + "encounter": { + 1: "I am Karen. Would you care for a showdown with my Dark-type Pokémon?", + 2: "I am unlike those you've already met.", + 3: "You've assembled a charming team. Our battle should be a good one." + }, + "victory": { + 1: "No! I can't win. How did you become so strong?", + 2: "I will not stray from my chosen path.", + 3: "The Champion is looking forward to meeting you." + }, + "defeat": { + 1: "That's about what I expected.", + 2: "Well, that was relatively entertaining.", + 3: "Come visit me anytime." + } + }, + "milo": { + "encounter": { + 1: `Sure seems like you understand Pokémon real well. + $This is gonna be a doozy of a battle! + $I'll have to Dynamax my Pokémon if I want to win!`, + }, + "victory": { + 1: "The power of Grass has wilted… What an incredible Challenger!" + }, + "defeat": { + 1: "This'll really leave you in shock and awe." + } + }, + "lucian": { + "encounter": { + 1: `Just a moment, please. The book I'm reading has nearly reached its thrilling climax… + $The hero has obtained a mystic sword and is about to face their final trial… Ah, never mind. + $Since you've made it this far, I'll put that aside and battle you. + $Let me see if you'll achieve as much glory as the hero of my book!` + }, + "victory": { + 1: "I see… It appears you've put me in checkmate." + }, + "defeat": { + 1: "I have a reputation to uphold." + } + }, + "drasna": { + "encounter": { + 1: `You must be a strong Trainer. Yes, quite strong indeed… + $That's just wonderful news! Facing opponents like you and your team will make my Pokémon grow like weeds!` + }, + "victory": { + 1: "Oh, dear me. That sure was a quick battle… I do hope you'll come back again sometime!" + }, + "defeat": { + 1: "How can this be?" + } + }, + "kahili": { + "encounter": { + 1: "So, here you are… Why don't we see who the winds favor today, you… Or me?" + }, + "victory": { + 1: "It's frustrating to me as a member of the Elite Four, but it seems your strength is the real deal." + }, + "defeat": { + 1: "That was an ace!" + } + }, + "hassel": { + "encounter": { + 1: "Prepare to learn firsthand how the fiery breath of ferocious battle feels!" + }, + "victory": { + 1: `Fortune smiled on me this time, but… + $Judging from how the match went, who knows if I will be so lucky next time.`, + }, + "defeat": { + 1: "That was an ace!" + } + }, + "blue": { + "encounter": { + 1: "You must be pretty good to get this far." + }, + "victory": { + 1: "I've only lost to him and now to you… Him? Hee, hee…" + }, + "defeat": { + 1: "See? My power is what got me here." + } + }, + "piers": { + "encounter": { + 1: "Get ready for a mosh pit with me and my party! Spikemuth, it's time to rock!" + }, + "victory": { + 1: "Me an' my team gave it our best. Let's meet up again for a battle some time…" + }, + "defeat": { + 1: "My throat's ragged from shoutin'… But 'at was an excitin' battle!" + } + }, + "red": { + "encounter": { + 1: "…!" + }, + "victory": { + 1: "…?" + }, + "defeat": { + 1: "…!" + } + }, + "jasmine": { + "encounter": { + 1: "Oh… Your Pokémon are impressive. I think I will enjoy this." + }, + "victory": { + 1: "You are truly strong. I'll have to try much harder, too." + }, + "defeat": { + 1: "I never expected to win." + } + }, + "lance_champion": { + "encounter": { + 1: "I am still the Champion. I won't hold anything back." + }, + "victory": { + 1: "This is the emergence of a new Champion." + }, + "defeat": { + 1: "I successfully defended my Championship." + } + }, + "steven": { + "encounter": { + 1: `Tell me… What have you seen on your journey with your Pokémon? + $What have you felt, meeting so many other Trainers out there? + $Traveling this rich land… Has it awoken something inside you? + $I want you to come at me with all that you've learned. + $My Pokémon and I will respond in turn with all that we know!`, + }, + "victory": { + 1: "So I, the Champion, fall in defeat…" + }, + "defeat": { + 1: "That was time well spent! Thank you!" + } + }, + "cynthia": { + "encounter": { + 1: "I, Cynthia, accept your challenge! There won't be any letup from me!" + }, + "victory": { + 1: "No matter how fun the battle is, it will always end sometime…" + }, + "defeat": { + 1: "Even if you lose, never lose your love of Pokémon." + } + }, + "iris": { + "encounter": { + 1: `Know what? I really look forward to having serious battles with strong Trainers! + $I mean, come on! The Trainers who make it here are Trainers who desire victory with every fiber of their being! + $And they are battling alongside Pokémon that have been through countless difficult battles! + $If I battle with people like that, not only will I get stronger, my Pokémon will, too! + $And we'll get to know each other even better! OK! Brace yourself! + $I'm Iris, the Pokémon League Champion, and I'm going to defeat you!`, + }, + "victory": { + 1: "Aghhhh… I did my best, but we lost…" + }, + "defeat": { + 1: "Yay! We won!" + } + }, + "hau": { + "encounter": { + 1: `I wonder if a Trainer battles differently depending on whether they're from a warm region or a cold region. + $Let's test it out!`, + }, + "victory": { + 1: "That was awesome! I think I kinda understand your vibe a little better now!" + }, + "defeat": { + 1: "Ma-an, that was some kinda battle!" + } + }, + "geeta": { + "encounter": { + 1: `I decided to throw my hat in the ring once more. + $Come now… Show me the fruits of your training.`, + }, + "victory": { + 1: "I eagerly await news of all your achievements!" + }, + "defeat": { + 1: "What's the matter? This isn't all, is it?" + } + }, + "nemona": { + "encounter": { + 1: "Yesss! I'm so psyched! Time for us to let loose!" + }, + "victory": { + 1: "Well, that stinks, but I still had fun! I'll getcha next time!" + }, + "defeat": { + 1: "Well, that was a great battle! Fruitful for sure." + } + }, + "leon": { + "encounter": { + 1: "We're gonna have an absolutely champion time!" + }, + "victory": { + 1: `My time as Champion is over… + $But what a champion time it's been! + $Thank you for the greatest battle I've ever had!`, + }, + "defeat": { + 1: "An absolute champion time, that was!" + } + }, + "whitney": { + "encounter": { + 1: "Hey! Don't you think Pokémon are, like, super cute?" + }, + "victory": { + 1: "Waaah! Waaah! You're so mean!" + }, + "defeat": { + 1: "And that's that!" + } + }, + "chuck": { + "encounter": { + 1: "Hah! You want to challenge me? Are you brave or just ignorant?" + }, + "victory": { + 1: "You're strong! Would you please make me your apprentice?" + }, + "defeat": { + 1: "There. Do you realize how much more powerful I am than you?" + } + }, + "katy": { + "encounter": { + 1: "Don't let your guard down unless you would like to find yourself knocked off your feet!" + }, + "victory": { + 1: "All of my sweet little Pokémon dropped like flies!" + }, + "defeat": { + 1: "Eat up, my cute little Vivillon!" + } + }, + "pryce": { + "encounter": { + 1: "Youth alone does not ensure victory! Experience is what counts." + }, + "victory": { + 1: "Outstanding! That was perfect. Try not to forget what you feel now." + }, + "defeat": { + 1: "Just as I envisioned." + } + }, + "clair": { + "encounter": { + 1: "Do you know who I am? And you still dare to challenge me?" + }, + "victory": { + 1: "I wonder how far you can get with your skill level. This should be fascinating." + }, + "defeat": { + 1: "That's that." + } + }, + "maylene": { + "encounter": { + 1: `I've come to challenge you now, and I won't hold anything back. + $Please prepare yourself for battle!`, + }, + "victory": { + 1: "I admit defeat…" + }, + "defeat": { + 1: "That was awesome." + } + }, + "fantina": { + "encounter": { + 1: `You shall challenge me, yes? But I shall win. + $That is what the Gym Leader of Hearthome does, non?`, + }, + "victory": { + 1: "You are so fantastically strong. I know why I have lost." + }, + "defeat": { + 1: "I am so, so, very happy!" + } + }, + "byron": { + "encounter": { + 1: `Trainer! You're young, just like my son, Roark. + $With more young Trainers taking charge, the future of Pokémon is bright! + $So, as a wall for young people, I'll take your challenge!`, + }, + "victory": { + 1: "Hmm! My sturdy Pokémon--defeated!" + }, + "defeat": { + 1: "Gwahahaha! How were my sturdy Pokémon?!" + } + }, + "olympia": { + "encounter": { + 1: "An ancient custom deciding one's destiny. The battle begins!" + }, + "victory": { + 1: "Create your own path. Let nothing get in your way. Your fate, your future." + }, + "defeat": { + 1: "Our path is clear now." + } + }, + "volkner": { + "encounter": { + 1: `Since you've come this far, you must be quite strong… + $I hope you're the Trainer who'll make me remember how fun it is to battle!`, + }, + "victory": { + 1: `You've got me beat… + $Your desire and the noble way your Pokémon battled for you… + $I even felt thrilled during our match. That was a very good battle.`, + }, + "defeat": { + 1: `It was not shocking at all… + $That is not what I wanted!`, + } + }, + "burgh": { + "encounter": { + 1: `M'hm… If I win this battle, I feel like I can draw a picture unlike any before it. + $OK! I can hear my battle muse loud and clear. Let's get straight to it!`, + 2: `Of course, I'm really proud of all of my Pokémon! + $Well now… Let's get right to it!` + }, + "victory": { + 1: "Is it over? Has my muse abandoned me?", + 2: "Hmm… It's over! You're incredible!" + }, + "defeat": { + 1: "Wow… It's beautiful somehow, isn't it…", + 2: `Sometimes I hear people say something was an ugly win. + $I think if you're trying your best, any win is beautiful.` + } + }, + "elesa": { + "encounter": { + 1: `C'est fini! When I'm certain of that, I feel an electric jolt run through my body! + $I want to feel the sensation, so now my beloved Pokémon are going to make your head spin!`, + }, + "victory": { + 1: "I meant to make your head spin, but you shocked me instead." + }, + "defeat": { + 1: "That was unsatisfying somehow… Will you give it your all next time?" + } + }, + "skyla": { + "encounter": { + 1: `It's finally time for a showdown! That means the Pokémon battle that decides who's at the top, right? + $I love being on the summit! 'Cause you can see forever and ever from high places! + $So, how about you and I have some fun?`, + }, + "victory": { + 1: "Being your opponent in battle is a new source of strength to me. Thank you!" + }, + "defeat": { + 1: "Win or lose, you always gain something from a battle, right?" + } + }, + "brycen": { + "encounter": { + 1: `There is also strength in being with other people and Pokémon. + $Receiving their support makes you stronger. I'll show you this power!`, + }, + "victory": { + 1: "The wonderful combination of you and your Pokémon! What a beautiful friendship!" + }, + "defeat": { + 1: "Extreme conditions really test you and train you!" + } + }, + "drayden": { + "encounter": { + 1: `What I want to find is a young Trainer who can show me a bright future. + $Let's battle with everything we have: your skill, my experience, and the love we've raised our Pokémon with!`, + }, + "victory": { + 1: "This intense feeling that floods me after a defeat… I don't know how to describe it." + }, + "defeat": { + 1: "Harrumph! I know your ability is greater than that!" + } + }, + "grant": { + "encounter": { + 1: `There is only one thing I wish for. + $That by surpassing one another, we find a way to even greater heights.`, + }, + "victory": { + 1: "You are a wall that I am unable to surmount!" + }, + "defeat": { + 1: `Do not give up. + $That is all there really is to it. + $The most important lessons in life are simple.`, + } + }, + "korrina": { + "encounter": { + 1: "Time for Lady Korrina's big appearance!" + }, + "victory": { + 1: "It's your very being that allows your Pokémon to evolve!" + }, + "defeat": { + 1: "What an explosive battle!" + } + }, + "clemont": { + "encounter": { + 1: "Oh! I'm glad that we got to meet!" + }, + "victory": { + 1: "Your passion for battle inspires me!" + }, + "defeat": { + 1: "Looks like my Trainer-Grow-Stronger Machine, Mach 2 is really working!" + } + }, + "valerie": { + "encounter": { + 1: `Oh, if it isn't a young Trainer… It is lovely to get to meet you like this. + $Then I suppose you have earned yourself the right to a battle, as a reward for your efforts. + $The elusive Fairy may appear frail as the breeze and delicate as a bloom, but it is strong.`, + }, + "victory": { + 1: "I hope that you will find things worth smiling about tomorrow…" + }, + "defeat": { + 1: "Oh goodness, what a pity…" + } + }, + "wulfric": { + "encounter": { + 1: `You know what? We all talk big about what you learn from battling and bonds and all that… + $But really, I just do it 'cause it's fun. + $Who cares about the grandstanding? Let's get to battling!`, + }, + "victory": { + 1: "Outstanding! I'm tough as an iceberg, but you smashed me through and through!" + }, + "defeat": { + 1: "Tussle with me and this is what happens!" + } + }, + "kabu": { + "encounter": { + 1: `Every Trainer and Pokémon trains hard in pursuit of victory. + $But that means your opponent is also working hard to win. + $In the end, the match is decided by which side is able to unleash their true potential.`, + }, + "victory": { + 1: "I'm glad I could battle you today!" + }, + "defeat": { + 1: "That's a great way for me to feel my own growth!" + } + }, + "bea": { + "encounter": { + 1: `Do you have an unshakable spirit that won't be moved, no matter how you are attacked? + $I think I'll just test that out, shall I?`, + }, + "victory": { + 1: "I felt the fighting spirit of your Pokémon as you led them in battle." + }, + "defeat": { + 1: "That was the best sort of match anyone could ever hope for." + } + }, + "opal": { + "encounter": { + 1: "Let me have a look at how you and your partner Pokémon behave!" + }, + "victory": { + 1: "Your pink is still lacking, but you're an excellent Trainer with excellent Pokémon." + }, + "defeat": { + 1: "Too bad for you, I guess." + } + }, + "bede": { + "encounter": { + 1: "I suppose I should prove beyond doubt just how pathetic you are and how strong I am." + }, + "victory": { + 1: "I see… Well, that's fine. I wasn't really trying all that hard anyway." + }, + "defeat": { + 1: "Not a bad job, I suppose." + } + }, + "gordie": { + "encounter": { + 1: "So, let's get this over with." + }, + "victory": { + 1: "I just want to climb into a hole… Well, I guess it'd be more like falling from here." + }, + "defeat": { + 1: "Battle like you always do, victory will follow!" + } + }, + "marnie": { + "encounter": { + 1: `The truth is, when all's said and done… I really just wanna become Champion for myself! + $So don't take it personal when I kick your butt!`, + }, + "victory": { + 1: "OK, so I lost… But I got to see a lot of the good points of you and your Pokémon!" + }, + "defeat": { + 1: "Hope you enjoyed our battle tactics." + } + }, + "raihan": { + "encounter": { + 1: "I'm going to defeat the Champion, win the whole tournament, and prove to the world just how strong the great Raihan really is!" + }, + "victory": { + 1: `I look this good even when I lose. + $It's a real curse. + $Guess it's time for another selfie!`, + }, + "defeat": { + 1: "Let's take a selfie to remember this." + } + }, + "brassius": { + "encounter": { + 1: "I assume you are ready? Let our collaborative work of art begin!" + }, + "victory": { + 1: "Ahhh…vant-garde!" + }, + "defeat": { + 1: "I will begin on a new piece at once!" + } + }, + "iono": { + "encounter": { + 1: `How're ya feelin' about this battle? + $... + $Let's get this show on the road! How strong is our challenger? + $I 'unno! Let's find out together!`, + }, + "victory": { + 1: "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!" + }, + "defeat": { + 1: "Your eyeballs are MINE!" + } + }, + "larry": { + "encounter": { + 1: "When all's said and done, simplicity is strongest." + }, + "victory": { + 1: "A serving of defeat, huh?" + }, + "defeat": { + 1: "I'll call it a day." + } + }, + "ryme": { + "encounter": { + 1: "Come on, baby! Rattle me down to the bone!" + }, + "victory": { + 1: "You're cool, my friend—you move my SOUL!" + }, + "defeat": { + 1: "Later, baby!" + } + }, + "grusha": { + "encounter": { + 1: "All I need to do is make sure the power of my Pokémon chills you to the bone!" + }, + "victory": { + 1: "Your burning passion… I kinda like it, to be honest." + }, + "defeat": { + 1: "Things didn't heat up for you." + } + }, + "marnie_elite": { + "encounter": { + 1: "You've made it this far, huh? Let's see if you can handle my Pokémon!", + 2: "I'll give it my best shot, but don't think I'll go easy on you!" + }, + "victory": { + 1: "I can't believe I lost... But you deserved that win. Well done!", + 2: "Looks like I've still got a lot to learn. Great battle, though!" + }, + "defeat": { + 1: "You put up a good fight, but I've got the edge! Better luck next time!", + 2: "Seems like my training's paid off. Thanks for the battle!" + } + }, + "nessa_elite": { + "encounter": { + 1: "The tides are turning in my favor. Ready to get swept away?", + 2: "Let's make some waves with this battle! I hope you're prepared!" + }, + "victory": { + 1: "You navigated those waters perfectly... Well done!", + 2: "Looks like my currents were no match for you. Great job!" + }, + "defeat": { + 1: "Water always finds a way. That was a refreshing battle!", + 2: "You fought well, but the ocean's power is unstoppable!" + } + }, + "bea_elite": { + "encounter": { + 1: "Prepare yourself! My fighting spirit burns bright!", + 2: "Let's see if you can keep up with my relentless pace!" + }, + "victory": { + 1: "Your strength... It's impressive. You truly deserve this win.", + 2: "I've never felt this intensity before. Amazing job!" + }, + "defeat": { + 1: "Another victory for my intense training regimen! Well done!", + 2: "You've got strength, but I trained harder. Great battle!" + } + }, + "allister_elite": { + "encounter": { + 1: "Shadows fall... Are you ready to face your fears?", + 2: "Let's see if you can handle the darkness that I command." + }, + "victory": { + 1: "You've dispelled the shadows... For now. Well done.", + 2: "Your light pierced through my darkness. Great job." + }, + "defeat": { + 1: "The shadows have spoken... Your strength isn't enough.", + 2: "Darkness triumphs... Maybe next time you'll see the light." + } + }, + "raihan_elite": { + "encounter": { + 1: "Storm's brewing! Let's see if you can weather this fight!", + 2: "Get ready to face the eye of the storm!" + }, + "victory": { + 1: "You've bested the storm... Incredible job!", + 2: "You rode the winds perfectly... Great battle!" + }, + "defeat": { + 1: "Another storm weathered, another victory claimed! Well fought!", + 2: "You got caught in my storm! Better luck next time!" + } + }, + "alder": { + "encounter": { + 1: "Prepare yourself for a match against the strongest Trainer in Unova!" + }, + "victory": { + 1: "Well done! You certainly are an unmatched talent." + }, + "defeat": { + 1: `A fresh wind blows through my heart... + $What an extraordinary effort!` + } + }, + "kieran": { + "encounter": { + 1: `Through hard work, I become stronger and stronger! + $I don't lose.` + }, + "victory": { + 1: `I don't believe it... + $What a fun and heart-pounding battle!` + }, + "defeat": { + 1: `Wowzers, what a battle! + $Time for you to train even harder.` + } + }, + "rival": { + "encounter": { + 1: `@c{smile}Hey, I was looking for you! I knew you were eager to get going but I expected at least a goodbye… + $@c{smile_eclosed}So you're really pursuing your dream after all?\n I almost can't believe it. + $@c{serious_smile_fists}Since we're here, how about a battle?\nAfter all, I want to make sure you're ready. + $@c{serious_mopen_fists}Don't hold back, I want you to give me everything you've got!` + }, + "victory": { + 1: `@c{shock}Wow… You cleaned me out.\nAre you actually a beginner? + $@c{smile}Maybe it was a bit of luck but…\nWho knows you might just be able to go all the way. + $By the way, the professor asked me to give you these items. They look pretty cool. + $@c{serious_smile_fists}Good luck out there!` + }, + }, + "rival_female": { + "encounter": { + 1: `@c{smile_wave}There you are! I've been looking everywhere for you!\n@c{angry_mopen}Did you forget to say goodbye to your best friend? + $@c{smile_ehalf}You're going after your dream, huh?\nThat day is really today isn't it… + $@c{smile}Anyway, I'll forgive you for forgetting me, but on one condition. @c{smile_wave_wink}You have to battle me! + $@c{angry_mopen}Give it your all! Wouldn't want your adventure to be over before it started, right?` + }, + "victory": { + 1: `@c{shock}You just started and you're already this strong?!@d{96}\n@c{angry}You totally cheated, didn't you? + $@c{smile_wave_wink}Just kidding!@d{64} @c{smile_eclosed}I lost fair and square… I have a feeling you're going to do really well out there. + $@c{smile}By the way, the professor wanted me to give you some items. Hopefully they're helpful! + $@c{smile_wave}Do your best like always! I believe in you!` + }, + }, + "rival_2": { + "encounter": { + 1: `@c{smile}Hey, you're here too?\n@c{smile_eclosed}Still a perfect record, huh…? + $@c{serious_mopen_fists}I know it kind of looks like I followed you here, but that's mostly not true. + $@c{serious_smile_fists}Honestly though, I've been itching for a rematch since you beat me back at home. + $I've been doing a lot of my own training so I'll definitely put up a fight this time. + $@c{serious_mopen_fists}Don't hold back, just like before!\nLet's go!` + }, + "victory": { + 1: `@c{neutral_eclosed}Oh. I guess I was overconfident. + $@c{smile}That's alright, though. I figured this might happen.\n@c{serious_mopen_fists}It just means I need to try harder for next time!\n + $@c{smile}Oh, not that you really need the help, but I had an extra one of these lying around and figured you might want it.\n + $@c{serious_smile_fists}Don't expect another one after this, though!\nI can't keep giving my opponent an advantage after all. + $@c{smile}Anyway, take care!` + }, + }, + "rival_2_female": { + "encounter": { + 1: `@c{smile_wave}Oh, fancy meeting you here. Looks like you're still undefeated. @c{angry_mopen}Huh… Not bad! + $@c{angry_mopen}I know what you're thinking, and no, I wasn't creeping on you. @c{smile_eclosed}I just happened to be in the area. + $@c{smile_ehalf}I'm happy for you but I just want to let you know that it's OK to lose sometimes. + $@c{smile}We learn from our mistakes, often more than we would if we kept succeeding. + $@c{angry_mopen}In any case, I've been training hard for our rematch, so you'd better give it your all!` + }, + "victory": { + 1: `@c{neutral}I… wasn't supposed to lose that time… + $@c{smile}Aw well. That just means I'll have to train even harder for next time! + $@c{smile_wave}I also got you another one of these!\n@c{smile_wave_wink}No need to thank me~. + $@c{angry_mopen}This is the last one, though! You won't be getting anymore freebies from me after this! + $@c{smile_wave}Keep at it!` + }, + "defeat": { + 1: "It's OK to lose sometimes…" + } + }, + "rival_3": { + "encounter": { + 1: `@c{smile}Hey, look who it is! It's been a while.\n@c{neutral}You're… still undefeated? Huh. + $@c{neutral_eclosed}Things have been kind of… strange.\nIt's not the same back home without you. + $@c{serious}I know it's selfish, but I need to get this off my chest.\n@c{neutral_eclosed}I think you're in over your head here. + $@c{serious}Never losing once is just unrealistic.\nWe need to lose sometimes in order to grow. + $@c{neutral_eclosed}You've had a great run but there's still so much ahead, and it only gets harder. @c{neutral}Are you prepared for that? + $@c{serious_mopen_fists}If so, prove it to me.` + }, + "victory": { + 1: "@c{angry_mhalf}This is ridiculous… I've hardly stopped training…\nHow are we still so far apart?" + }, + }, + "rival_3_female": { + "encounter": { + 1: `@c{smile_wave}Long time no see! Still haven't lost, huh.\n@c{angry}You're starting to get on my nerves. @c{smile_wave_wink}Just kidding! + $@c{smile_ehalf}But really, don't you miss home by now? Or… me?\nI… I mean, we've really missed you. + $@c{smile_eclosed}I support you in your dream and everything, but the reality is you're going to lose sooner or later. + $@c{smile}And when you do, I'll be there for you like always.\n@c{angry_mopen}Now, let me show you how strong I've become!` + }, + "victory": { + 1: "@c{shock}After all that… it wasn't enough…?\nYou'll never come back at this rate…" + + }, + "defeat": { + 1: "You gave it your best, now let's go home." + } + }, + "rival_4": { + "encounter": { + 1: `@c{neutral}Hey. + $I won't mince words or pleasantries with you.\n@c{neutral_eclosed}I'm here to win, plain and simple. + $@c{serious_mhalf_fists}I've learned to maximize my potential by putting all my time into training. + $@c{smile}You get a lot of extra time when you cut out the unnecessary sleep and social interaction. + $@c{serious_mopen_fists}None of that matters anymore, not until I win. + $@c{neutral_eclosed}I've even reached the point where I don't lose anymore.\n@c{smile_eclosed}I suppose your philosophy wasn't so wrong after all. + $@c{angry_mhalf}Losing is for the weak, and I'm not weak anymore. + $@c{serious_mopen_fists}Prepare yourself.` + }, + "victory": { + 1: "@c{neutral}What…@d{64} What are you?" + }, + }, + "rival_4_female": { + "encounter": { + 1: `@c{neutral}It's me! You didn't forget about me again… did you? + $@c{smile}You should be proud of how far you made it. Congrats!\nBut it looks like it's the end of your journey. + $@c{smile_eclosed}You've awoken something in me I never knew was there.\nIt seems like all I do now is train. + $@c{smile_ehalf}I hardly even eat or sleep now, I just train my Pokémon all day, getting stronger every time. + $@c{neutral}In fact, I… hardly recognize myself. + $And now, I've finally reached peak performance.\nI don't think anyone could beat me now. + $And you know what? It's all because of you.\n@c{smile_ehalf}I don't know whether to thank you or hate you. + $@c{angry_mopen}Prepare yourself.` + }, + "victory": { + 1: "@c{neutral}What…@d{64} What are you?" + + }, + "defeat": { + 1: "$@c{smile}You should be proud of how far you made it." + } + }, + "rival_5": { + "encounter": { + 1: "@c{neutral}…" + }, + "victory": { + 1: "@c{neutral}…" + }, + }, + "rival_5_female": { + "encounter": { + 1: "@c{neutral}…" + }, + "victory": { + 1: "@c{neutral}…" + + }, + "defeat": { + 1: "$@c{smile_ehalf}…" + } + }, + "rival_6": { + "encounter": { + 1: `@c{smile_eclosed}We meet again. + $@c{neutral}I've had some time to reflect on all this.\nThere's a reason this all seems so strange. + $@c{neutral_eclosed}Your dream, my drive to beat you…\nIt's all a part of something greater. + $@c{serious}This isn't about me, or about you… This is about the world, @c{serious_mhalf_fists}and it's my purpose to push you to your limits. + $@c{neutral_eclosed}Whether I've fulfilled that purpose I can't say, but I've done everything in my power. + $@c{neutral}This place we ended up in is terrifying… Yet somehow I feel unphased, like I've been here before. + $@c{serious_mhalf_fists}You feel the same, don't you? + $@c{serious}…and it's like something here is speaking to me.\nThis is all the world's known for a long time now. + $Those times we cherished together that seem so recent are nothing but a distant memory. + $@c{neutral_eclosed}Who can say whether they were ever even real in the first place. + $@c{serious_mopen_fists}You need to keep pushing, because if you don't, it will never end. You're the only one who can do this. + $@c{serious_smile_fists}I hardly know what any of this means, I just know that it's true. + $@c{serious_mopen_fists}If you can't defeat me here and now, you won't stand a chance.` + }, + "victory": { + 1: `@c{smile_eclosed}It looks like my work is done here. + $I want you to promise me one thing.\n@c{smile}After you heal the world, please come home.` + }, + }, + "rival_6_female": { + "encounter": { + 1: `@c{smile_ehalf}So it's just us again. + $@c{smile_eclosed}You know, I keep going around and around in my head… + $@c{smile_ehalf}There's something to all this, why everything seems so strange now… + $@c{smile}You have your dream, and I have this ambition in me… + $I just can't help but feel there's a greater purpose to all this, to what we're doing, you and I. + $@c{smile_eclosed}I think I'm supposed to push you… to your limits. + $@c{smile_ehalf}I'm not sure if I've been doing a good job at that, but I've tried my best up to now. + $It's something about this strange and dreadful place… Everything seems so clear… + $This… is all the world's known for a long time now. + $@c{smile_eclosed}It's like I can barely remember the memories we cherished together. + $@c{smile_ehalf}Were they even real? They seem so far away now… + $@c{angry_mopen}You need to keep pushing, because if you don't, it will never end. You're the only one who can do this. + $@c{smile_ehalf}I… don't know what all this means… but I feel it's true. + $@c{neutral}If you can't defeat me here and now, you won't stand a chance.` + }, + "victory": { + 1: `@c{smile_ehalf}I… I think I fulfilled my purpose… + $@c{smile_eclosed}Promise me… After you heal the world… Please… come home safe. + $@c{smile_ehalf}…Thank you.` + + }, + }, +}; + + +// Dialogue of the NPCs in the game when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMdialogue. +export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; + +// Dialogue of the endboss of the game when the player character is male (Or unset) +export const PGMbattleSpecDialogue: SimpleTranslationEntries = { + "encounter": `It appears the time has finally come once again.\nYou know why you have come here, do you not? + $You were drawn here, because you have been here before.\nCountless times. + $Though, perhaps it can be counted.\nTo be precise, this is in fact your 5,643,853rd cycle. + $Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain. + $Until now you have yet to succeed, but I sense a different presence in you this time.\n + $You are the only one here, though it is as if there is… another. + $Will you finally prove a formidable challenge to me?\nThe challenge I have longed for for millennia? + $We begin.`, + "firstStageWin": `I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back. + $Do not disappoint me.`, + "secondStageWin": "…Magnificent." +}; + +// Dialogue of the endboss of the game when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMbattleSpecDialogue. +export const PGFbattleSpecDialogue: SimpleTranslationEntries = PGMbattleSpecDialogue; + +// Dialogue that does not fit into any other category (e.g. tutorial messages, or the end of the game). For when the player character is male +export const PGMmiscDialogue: SimpleTranslationEntries = { + "ending": + `@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now. + $@c{smile}It's over.@d{64} You ended the loop. + $@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once. + $@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it? + $@c{serious_smile_fists}Your legend will always live on in our hearts. + $@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home. + $@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.`, + "ending_female": + `@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you. + $@c{smile_eclosed}Of course… I always had that feeling.\n@c{smile}It's over now, right? You ended the loop. + $@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once. + $I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget! + $@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts. + $@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place. + $Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?`, + "ending_endless": "Congratulations on reaching the current end!\nMore content is coming soon.", + "ending_name": "Devs" +}; +// Dialogue that does not fit into any other category (e.g. tutorial messages, or the end of the game). For when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMmiscDialogue. +export const PGFmiscDialogue: SimpleTranslationEntries = PGMmiscDialogue; + + +// Dialogue of the named double battles in the game. For when the player is male (or unset). +export const PGMdoubleBattleDialogue: DialogueTranslationEntries = { + "blue_red_double": { + "encounter": { + 1: `Blue: Hey Red, let's show them what we're made of! + $Red: ... + $Blue: This is Pallet Town Power!`, + }, + "victory": { + 1: `Blue: That was a great battle! + $Red: ...`, + }, + }, + "red_blue_double": { + "encounter": { + 1: `Red: ...! + $Blue: He never talks much. + $Blue: But dont let that fool you! He is a champ after all!`, + }, + "victory": { + 1: `Red: ...! + $Blue: Next time we will beat you!`, + }, + }, + "tate_liza_double": { + "encounter": { + 1: `Tate: Are you surprised? + $Liza: We are two gym leaders at once! + $Tate: We are twins! + $Liza: We dont need to talk to understand each other! + $Tate: Twice the power... + $Liza: Can you handle it?`, + }, + "victory": { + 1: `Tate: What? Our combination was perfect! + $Liza: Looks like we need to train more...`, + }, + }, + "liza_tate_double": { + "encounter": { + 1: `Liza: Hihihi... Are you surprised? + $Tate: Yes, we are really two gym leaders at once! + $Liza: This is my twin brother Tate! + $Tate: And this is my twin sister Liza! + $Liza: Don't you think we are a perfect combination?` + }, + "victory": { + 1: `Liza: Are we... + $Tate: ...not as strong as we thought?`, + }, + }, + "wallace_steven_double": { + "encounter": { + 1: `Steven: Wallace, let's show them the power of the champions! + $Wallace: We will show you the power of Hoenn! + $Steven: Let's go!`, + }, + "victory": { + 1: `Steven: That was a great battle! + $Wallace: We will win next time!`, + }, + }, + "steven_wallace_double": { + "encounter": { + 1: `Steven: Do you have any rare Pokémon? + $Wallace: Steven... We are here for a battle, not to show off our Pokémon. + $Steven: Oh... I see... Let's go then!`, + }, + "victory": { + 1: `Steven: Now that we are done with the battle, let's show off our Pokémon! + $Wallace: Steven...`, + }, + }, + "alder_iris_double": { + "encounter": { + 1: `Alder: We are the strongest trainers in Unova! + $Iris: Fights against strong trainers are the best!`, + }, + "victory": { + 1: `Alder: Wow! You are super strong! + $Iris: We will win next time!`, + }, + }, + "iris_alder_double": { + "encounter": { + 1: `Iris: Welcome Challenger! I am THE Unova Champion! + $Alder: Iris, aren't you a bit too excited?`, + }, + "victory": { + 1: `Iris: A loss like this is not easy to take... + $Alder: But we will only get stronger with every loss!`, + }, + }, + "piers_marnie_double": { + "encounter": { + 1: `Marnie: Brother, let's show them the power of Spikemuth! + $Piers: We bring darkness!`, + }, + "victory": { + 1: `Marnie: You brought light to our darkness! + $Piers: Its too bright...`, + }, + }, + "marnie_piers_double": { + "encounter": { + 1: `Piers: Ready for a concert? + $Marnie: Brother... They are here to fight, not to sing...`, + }, + "victory": { + 1: `Piers: Now that was a great concert! + $Marnie: Brother...`, + }, + }, +}; + +// Dialogue of the named double battles in the game. For when the player is female. For languages that do not have gendered pronouns, this can be set to PGMdoubleBattleDialogue. +export const PGFdoubleBattleDialogue: DialogueTranslationEntries = PGMdoubleBattleDialogue; diff --git a/src/locales/ca-ES/egg.ts b/src/locales/ca-ES/egg.ts new file mode 100644 index 00000000000..9f699ce0fdc --- /dev/null +++ b/src/locales/ca-ES/egg.ts @@ -0,0 +1,28 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const egg: SimpleTranslationEntries = { + "egg": "Egg", + "greatTier": "Rare", + "ultraTier": "Epic", + "masterTier": "Legendary", + "defaultTier": "Common", + "hatchWavesMessageSoon": "Sounds can be heard coming from inside! It will hatch soon!", + "hatchWavesMessageClose": "It appears to move occasionally. It may be close to hatching.", + "hatchWavesMessageNotClose": "What will hatch from this? It doesn't seem close to hatching.", + "hatchWavesMessageLongTime": "It looks like this Egg will take a long time to hatch.", + "gachaTypeLegendary": "Legendary Rate Up", + "gachaTypeMove": "Rare Egg Move Rate Up", + "gachaTypeShiny": "Shiny Rate Up", + "selectMachine": "Select a machine.", + "notEnoughVouchers": "You don't have enough vouchers!", + "tooManyEggs": "You have too many eggs!", + "pull": "Pull", + "pulls": "Pulls", + "sameSpeciesEgg": "{{species}} will hatch from this egg!", + "hatchFromTheEgg": "{{pokemonName}} hatched from the egg!", + "eggMoveUnlock": "Egg Move unlocked: {{moveName}}", + "rareEggMoveUnlock": "Rare Egg Move unlocked: {{moveName}}", + "moveUPGacha": "Move UP!", + "shinyUPGacha": "Shiny UP!", + "legendaryUPGacha": "UP!", +} as const; diff --git a/src/locales/ca-ES/fight-ui-handler.ts b/src/locales/ca-ES/fight-ui-handler.ts new file mode 100644 index 00000000000..8ceb503c34a --- /dev/null +++ b/src/locales/ca-ES/fight-ui-handler.ts @@ -0,0 +1,9 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const fightUiHandler: SimpleTranslationEntries = { + "pp": "PP", + "power": "Power", + "accuracy": "Accuracy", + "abilityFlyInText": " {{pokemonName}}'s {{passive}}{{abilityName}}", + "passive": "Passive ", // The space at the end is important +} as const; diff --git a/src/locales/ca-ES/filter-bar.ts b/src/locales/ca-ES/filter-bar.ts new file mode 100644 index 00000000000..7a3174957ea --- /dev/null +++ b/src/locales/ca-ES/filter-bar.ts @@ -0,0 +1,33 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const filterBar: SimpleTranslationEntries = { + "genFilter": "Gen", + "typeFilter": "Type", + "caughtFilter": "Caught", + "unlocksFilter": "Unlocks", + "miscFilter": "Misc", + "sortFilter": "Sort", + "all": "All", + "normal": "Normal", + "uncaught": "Uncaught", + "passive": "Passive", + "passiveUnlocked": "Passive Unlocked", + "passiveLocked": "Passive Locked", + "costReduction": "Cost Reduction", + "costReductionUnlocked": "Cost Reduction Unlocked", + "costReductionLocked": "Cost Reduction Locked", + "ribbon": "Ribbon", + "hasWon": "Ribbon - Yes", + "hasNotWon": "Ribbon - No", + "hiddenAbility": "Hidden Ability", + "hasHiddenAbility": "Hidden Ability - Yes", + "noHiddenAbility": "Hidden Ability - No", + "pokerus": "Pokerus", + "hasPokerus": "Pokerus - Yes", + "noPokerus": "Pokerus - No", + "sortByNumber": "No.", + "sortByCost": "Cost", + "sortByCandies": "Candy Count", + "sortByIVs": "IVs", + "sortByName": "Name", +}; diff --git a/src/locales/ca-ES/game-mode.ts b/src/locales/ca-ES/game-mode.ts new file mode 100644 index 00000000000..903f1a63072 --- /dev/null +++ b/src/locales/ca-ES/game-mode.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const gameMode: SimpleTranslationEntries = { + "classic": "Classic", + "endless": "Endless", + "endlessSpliced": "Endless (Spliced)", + "dailyRun": "Daily Run", + "unknown": "Unknown", + "challenge": "Challenge", +} as const; diff --git a/src/locales/ca-ES/game-stats-ui-handler.ts b/src/locales/ca-ES/game-stats-ui-handler.ts new file mode 100644 index 00000000000..a29eaf5d1b6 --- /dev/null +++ b/src/locales/ca-ES/game-stats-ui-handler.ts @@ -0,0 +1,44 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const gameStatsUiHandler: SimpleTranslationEntries = { + "stats": "Stats", + "playTime": "Play Time", + "totalBattles": "Total Battles", + "starters": "Starters", + "shinyStarters": "Shiny Starters", + "speciesSeen": "Species Seen", + "speciesCaught": "Species Caught", + "ribbonsOwned": "Ribbons Owned", + "classicRuns": "Classic Runs", + "classicWins": "Classic Wins", + "dailyRunAttempts": "Daily Run Attempts", + "dailyRunWins": "Daily Run Wins", + "endlessRuns": "Endless Runs", + "highestWaveEndless": "Highest Wave (Endless)", + "highestMoney": "Highest Money", + "highestDamage": "Highest Damage", + "highestHPHealed": "Highest HP Healed", + "pokemonEncountered": "Pokémon Encountered", + "pokemonDefeated": "Pokémon Defeated", + "pokemonCaught": "Pokémon Caught", + "eggsHatched": "Eggs Hatched", + "subLegendsSeen": "Sub-Legends Seen", + "subLegendsCaught": "Sub-Legends Caught", + "subLegendsHatched": "Sub-Legends Hatched", + "legendsSeen": "Legends Seen", + "legendsCaught": "Legends Caught", + "legendsHatched": "Legends Hatched", + "mythicalsSeen": "Mythicals Seen", + "mythicalsCaught": "Mythicals Caught", + "mythicalsHatched": "Mythicals Hatched", + "shiniesSeen": "Shinies Seen", + "shiniesCaught": "Shinies Caught", + "shiniesHatched": "Shinies Hatched", + "pokemonFused": "Pokémon Fused", + "trainersDefeated": "Trainers Defeated", + "eggsPulled": "Eggs Pulled", + "rareEggsPulled": "Rare Eggs Pulled", + "epicEggsPulled": "Epic Eggs Pulled", + "legendaryEggsPulled": "Legendary Eggs Pulled", + "manaphyEggsPulled": "Manaphy Eggs Pulled", +} as const; diff --git a/src/locales/ca-ES/growth.ts b/src/locales/ca-ES/growth.ts new file mode 100644 index 00000000000..410355b143b --- /dev/null +++ b/src/locales/ca-ES/growth.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const growth: SimpleTranslationEntries = { + "Erratic": "Erratic", + "Fast": "Fast", + "Medium_Fast": "Medium Fast", + "Medium_Slow": "Medium Slow", + "Slow": "Slow", + "Fluctuating": "Fluctuating" +} as const; diff --git a/src/locales/ca-ES/menu-ui-handler.ts b/src/locales/ca-ES/menu-ui-handler.ts new file mode 100644 index 00000000000..6eb680544ed --- /dev/null +++ b/src/locales/ca-ES/menu-ui-handler.ts @@ -0,0 +1,28 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const menuUiHandler: SimpleTranslationEntries = { + "GAME_SETTINGS": "Game Settings", + "ACHIEVEMENTS": "Achievements", + "STATS": "Stats", + "VOUCHERS": "Vouchers", + "EGG_LIST": "Egg List", + "EGG_GACHA": "Egg Gacha", + "MANAGE_DATA": "Manage Data", + "COMMUNITY": "Community", + "SAVE_AND_QUIT": "Save and Quit", + "LOG_OUT": "Log Out", + "slot": "Slot {{slotNumber}}", + "importSession": "Import Session", + "importSlotSelect": "Select a slot to import to.", + "exportSession": "Export Session", + "exportSlotSelect": "Select a slot to export from.", + "importData": "Import Data", + "exportData": "Export Data", + "linkDiscord": "Link Discord", + "unlinkDiscord": "Unlink Discord", + "linkGoogle": "Link Google", + "unlinkGoogle": "Unlink Google", + "cancel": "Cancel", + "losingProgressionWarning": "You will lose any progress since the beginning of the battle. Proceed?", + "noEggs": "You are not hatching\nany eggs at the moment!" +} as const; diff --git a/src/locales/ca-ES/menu.ts b/src/locales/ca-ES/menu.ts new file mode 100644 index 00000000000..fe4d96c5120 --- /dev/null +++ b/src/locales/ca-ES/menu.ts @@ -0,0 +1,62 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +/** + * The menu namespace holds most miscellaneous text that isn't directly part of the game's + * contents or directly related to Pokemon data. This includes menu navigation, settings, + * account interactions, descriptive text, etc. + */ +export const menu: SimpleTranslationEntries = { + "cancel": "Cancel", + "continue": "Continue", + "dailyRun": "Daily Run (Beta)", + "loadGame": "Load Game", + "newGame": "New Game", + "settings": "Settings", + "selectGameMode": "Select a game mode.", + "logInOrCreateAccount": "Log in or create an account to start. No email required!", + "username": "Username", + "password": "Password", + "login": "Login", + "orUse": "Or use", + "register": "Register", + "emptyUsername": "Username must not be empty", + "invalidLoginUsername": "The provided username is invalid", + "invalidRegisterUsername": "Username must only contain letters, numbers, or underscores", + "invalidLoginPassword": "The provided password is invalid", + "invalidRegisterPassword": "Password must be 6 characters or longer", + "usernameAlreadyUsed": "The provided username is already in use", + "accountNonExistent": "The provided user does not exist", + "unmatchingPassword": "The provided password does not match", + "passwordNotMatchingConfirmPassword": "Password must match confirm password", + "confirmPassword": "Confirm Password", + "registrationAgeWarning": "By registering, you confirm you are of 13 years of age or older.", + "backToLogin": "Back to Login", + "failedToLoadSaveData": "Failed to load save data. Please reload the page.\nIf this persists, please check #announcements in Discord.", + "sessionSuccess": "Session loaded successfully.", + "failedToLoadSession": "Your session data could not be loaded.\nIt may be corrupted.", + "boyOrGirl": "Are you a boy or a girl?", + "evolving": "What?\n{{pokemonName}} is evolving!", + "stoppedEvolving": "{{pokemonName}} stopped evolving.", + "pauseEvolutionsQuestion": "Would you like to pause evolutions for {{pokemonName}}?\nEvolutions can be re-enabled from the party screen.", + "evolutionsPaused": "Evolutions have been paused for {{pokemonName}}.", + "evolutionDone": "Congratulations!\nYour {{pokemonName}} evolved into {{evolvedPokemonName}}!", + "dailyRankings": "Daily Rankings", + "weeklyRankings": "Weekly Rankings", + "noRankings": "No Rankings", + "positionIcon": "#", + "usernameScoreboard": "Username", + "score": "Score", + "wave": "Wave", + "loading": "Loading…", + "loadingAsset": "Loading asset: {{assetName}}", + "playersOnline": "Players Online", + "yes":"Yes", + "no":"No", + "disclaimer": "DISCLAIMER", + "disclaimerDescription": "This game is an unfinished product; it might have playability issues (including the potential loss of save data),\n change without notice, and may or may not be updated further or completed.", + "choosePokemon": "Choose a Pokémon.", + "renamePokemon": "Rename Pokémon", + "rename": "Rename", + "nickname": "Nickname", + "errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.", +} as const; diff --git a/src/locales/ca-ES/modifier-select-ui-handler.ts b/src/locales/ca-ES/modifier-select-ui-handler.ts new file mode 100644 index 00000000000..75299a08ba7 --- /dev/null +++ b/src/locales/ca-ES/modifier-select-ui-handler.ts @@ -0,0 +1,14 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const modifierSelectUiHandler: SimpleTranslationEntries = { + "transfer": "Transfer", + "reroll": "Reroll", + "lockRarities": "Lock Rarities", + "checkTeam": "Check Team", + "transferDesc": "Transfer a held item from one Pokémon to another.", + "rerollDesc": "Spend money to reroll your item options.", + "lockRaritiesDesc": "Lock item rarities on reroll (affects reroll cost).", + "checkTeamDesc": "Check your team or use a form changing item.", + "rerollCost": "₽{{formattedMoney}}", + "itemCost": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/ca-ES/modifier-type.ts b/src/locales/ca-ES/modifier-type.ts new file mode 100644 index 00000000000..c6c98e44a92 --- /dev/null +++ b/src/locales/ca-ES/modifier-type.ts @@ -0,0 +1,456 @@ +import { ModifierTypeTranslationEntries } from "#app/interfaces/locales"; + +export const modifierType: ModifierTypeTranslationEntries = { + ModifierType: { + "AddPokeballModifierType": { + name: "{{modifierCount}}x {{pokeballName}}", + description: "Receive {{pokeballName}} x{{modifierCount}} (Inventory: {{pokeballAmount}}) \nCatch Rate: {{catchRate}}", + }, + "AddVoucherModifierType": { + name: "{{modifierCount}}x {{voucherTypeName}}", + description: "Receive {{voucherTypeName}} x{{modifierCount}}.", + }, + "PokemonHeldItemModifierType": { + extra: { + "inoperable": "{{pokemonName}} can't take\nthis item!", + "tooMany": "{{pokemonName}} has too many\nof this item!", + } + }, + "PokemonHpRestoreModifierType": { + description: "Restores {{restorePoints}} HP or {{restorePercent}}% HP for one Pokémon, whichever is higher.", + extra: { + "fully": "Fully restores HP for one Pokémon.", + "fullyWithStatus": "Fully restores HP for one Pokémon and heals any status ailment.", + } + }, + "PokemonReviveModifierType": { + description: "Revives one Pokémon and restores {{restorePercent}}% HP.", + }, + "PokemonStatusHealModifierType": { + description: "Heals any status ailment for one Pokémon.", + }, + "PokemonPpRestoreModifierType": { + description: "Restores {{restorePoints}} PP for one Pokémon move.", + extra: { + "fully": "Restores all PP for one Pokémon move.", + } + }, + "PokemonAllMovePpRestoreModifierType": { + description: "Restores {{restorePoints}} PP for all of one Pokémon's moves.", + extra: { + "fully": "Restores all PP for all of one Pokémon's moves.", + } + }, + "PokemonPpUpModifierType": { + description: "Permanently increases PP for one Pokémon move by {{upPoints}} for every 5 maximum PP (maximum 3).", + }, + "PokemonNatureChangeModifierType": { + name: "{{natureName}} Mint", + description: "Changes a Pokémon's nature to {{natureName}} and permanently unlocks the nature for the starter.", + }, + "DoubleBattleChanceBoosterModifierType": { + description: "Doubles the chance of an encounter being a double battle for {{battleCount}} battles.", + }, + "TempBattleStatBoosterModifierType": { + description: "Increases the {{tempBattleStatName}} of all party members by 1 stage for 5 battles.", + }, + "AttackTypeBoosterModifierType": { + description: "Increases the power of a Pokémon's {{moveType}}-type moves by 20%.", + }, + "PokemonLevelIncrementModifierType": { + description: "Increases a Pokémon's level by {{levels}}.", + }, + "AllPokemonLevelIncrementModifierType": { + description: "Increases all party members' level by {{levels}}.", + }, + "PokemonBaseStatBoosterModifierType": { + description: "Increases the holder's base {{statName}} by 10%. The higher your IVs, the higher the stack limit.", + }, + "AllPokemonFullHpRestoreModifierType": { + description: "Restores 100% HP for all Pokémon.", + }, + "AllPokemonFullReviveModifierType": { + description: "Revives all fainted Pokémon, fully restoring HP.", + }, + "MoneyRewardModifierType": { + description: "Grants a {{moneyMultiplier}} amount of money (₽{{moneyAmount}}).", + extra: { + "small": "small", + "moderate": "moderate", + "large": "large", + }, + }, + "ExpBoosterModifierType": { + description: "Increases gain of EXP. Points by {{boostPercent}}%.", + }, + "PokemonExpBoosterModifierType": { + description: "Increases the holder's gain of EXP. Points by {{boostPercent}}%.", + }, + "PokemonFriendshipBoosterModifierType": { + description: "Increases friendship gain per victory by 50%.", + }, + "PokemonMoveAccuracyBoosterModifierType": { + description: "Increases move accuracy by {{accuracyAmount}} (maximum 100).", + }, + "PokemonMultiHitModifierType": { + description: "Attacks hit one additional time at the cost of a 60/75/82.5% power reduction per stack respectively.", + }, + "TmModifierType": { + name: "TM{{moveId}} - {{moveName}}", + description: "Teach {{moveName}} to a Pokémon.", + }, + "TmModifierTypeWithInfo": { + name: "TM{{moveId}} - {{moveName}}", + description: "Teach {{moveName}} to a Pokémon\n(Hold C or Shift for more info).", + }, + "EvolutionItemModifierType": { + description: "Causes certain Pokémon to evolve.", + }, + "FormChangeItemModifierType": { + description: "Causes certain Pokémon to change form.", + }, + "FusePokemonModifierType": { + description: "Combines two Pokémon (transfers Ability, splits base stats and types, shares move pool).", + }, + "TerastallizeModifierType": { + name: "{{teraType}} Tera Shard", + description: "{{teraType}} Terastallizes the holder for up to 10 battles.", + }, + "ContactHeldItemTransferChanceModifierType": { + description: "Upon attacking, there is a {{chancePercent}}% chance the foe's held item will be stolen.", + }, + "TurnHeldItemTransferModifierType": { + description: "Every turn, the holder acquires one held item from the foe.", + }, + "EnemyAttackStatusEffectChanceModifierType": { + description: "Adds a {{chancePercent}}% chance to inflict {{statusEffect}} with attack moves.", + }, + "EnemyEndureChanceModifierType": { + description: "Adds a {{chancePercent}}% chance of enduring a hit.", + }, + + "RARE_CANDY": { name: "Rare Candy" }, + "RARER_CANDY": { name: "Rarer Candy" }, + + "MEGA_BRACELET": { name: "Mega Bracelet", description: "Mega Stones become available." }, + "DYNAMAX_BAND": { name: "Dynamax Band", description: "Max Mushrooms become available." }, + "TERA_ORB": { name: "Tera Orb", description: "Tera Shards become available." }, + + "MAP": { name: "Map", description: "Allows you to choose your destination at a crossroads." }, + + "POTION": { name: "Potion" }, + "SUPER_POTION": { name: "Super Potion" }, + "HYPER_POTION": { name: "Hyper Potion" }, + "MAX_POTION": { name: "Max Potion" }, + "FULL_RESTORE": { name: "Full Restore" }, + + "REVIVE": { name: "Revive" }, + "MAX_REVIVE": { name: "Max Revive" }, + + "FULL_HEAL": { name: "Full Heal" }, + + "SACRED_ASH": { name: "Sacred Ash" }, + + "REVIVER_SEED": { name: "Reviver Seed", description: "Revives the holder for 1/2 HP upon fainting from a direct hit." }, + + "WHITE_HERB": { name: "White Herb", description: "An item to be held by a Pokémon. It will restore any lowered stat in battle." }, + + "ETHER": { name: "Ether" }, + "MAX_ETHER": { name: "Max Ether" }, + + "ELIXIR": { name: "Elixir" }, + "MAX_ELIXIR": { name: "Max Elixir" }, + + "PP_UP": { name: "PP Up" }, + "PP_MAX": { name: "PP Max" }, + + "LURE": { name: "Lure" }, + "SUPER_LURE": { name: "Super Lure" }, + "MAX_LURE": { name: "Max Lure" }, + + "MEMORY_MUSHROOM": { name: "Memory Mushroom", description: "Recall one Pokémon's forgotten move." }, + + "EXP_SHARE": { name: "EXP. All", description: "Non-participants receive 20% of a single participant's EXP. Points." }, + "EXP_BALANCE": { name: "EXP. Balance", description: "Weighs EXP. Points received from battles towards lower-leveled party members." }, + + "OVAL_CHARM": { name: "Oval Charm", description: "When multiple Pokémon participate in a battle, each gets an extra 10% of the total EXP." }, + + "EXP_CHARM": { name: "EXP. Charm" }, + "SUPER_EXP_CHARM": { name: "Super EXP. Charm" }, + "GOLDEN_EXP_CHARM": { name: "Golden EXP. Charm" }, + + "LUCKY_EGG": { name: "Lucky Egg" }, + "GOLDEN_EGG": { name: "Golden Egg" }, + + "SOOTHE_BELL": { name: "Soothe Bell" }, + + "SCOPE_LENS": { name: "Scope Lens", description: "It's a lens for scoping out weak points. It boosts the holder's critical-hit ratio."}, + "LEEK": { name: "Leek", description: "This very long and stiff stalk of leek boosts the critical-hit ratio of Farfetch'd's moves."}, + + "EVIOLITE": { name: "Eviolite", description: "This mysterious evolutionary lump boosts the Defense and Sp. Def stats when held by a Pokémon that can still evolve." }, + + "SOUL_DEW": { name: "Soul Dew", description: "Increases the influence of a Pokémon's nature on its stats by 10% (additive)." }, + + "NUGGET": { name: "Nugget" }, + "BIG_NUGGET": { name: "Big Nugget" }, + "RELIC_GOLD": { name: "Relic Gold" }, + + "AMULET_COIN": { name: "Amulet Coin", description: "Increases money rewards by 20%." }, + "GOLDEN_PUNCH": { name: "Golden Punch", description: "Grants 50% of direct damage inflicted as money." }, + "COIN_CASE": { name: "Coin Case", description: "After every 10th battle, receive 10% of your money in interest." }, + + "LOCK_CAPSULE": { name: "Lock Capsule", description: "Allows you to lock item rarities when rerolling items." }, + + "GRIP_CLAW": { name: "Grip Claw" }, + "WIDE_LENS": { name: "Wide Lens" }, + + "MULTI_LENS": { name: "Multi Lens" }, + + "HEALING_CHARM": { name: "Healing Charm", description: "Increases the effectiveness of HP restoring moves and items by 10% (excludes Revives)." }, + "CANDY_JAR": { name: "Candy Jar", description: "Increases the number of levels added by Rare Candy items by 1." }, + + "BERRY_POUCH": { name: "Berry Pouch", description: "Adds a 30% chance that a used berry will not be consumed." }, + + "FOCUS_BAND": { name: "Focus Band", description: "Adds a 10% chance to survive with 1 HP after being damaged enough to faint." }, + + "QUICK_CLAW": { name: "Quick Claw", description: "Adds a 10% chance to move first regardless of speed (after priority)." }, + + "KINGS_ROCK": { name: "King's Rock", description: "Adds a 10% chance an attack move will cause the opponent to flinch." }, + + "LEFTOVERS": { name: "Leftovers", description: "Heals 1/16 of a Pokémon's maximum HP every turn." }, + "SHELL_BELL": { name: "Shell Bell", description: "Heals 1/8 of a Pokémon's dealt damage." }, + + "TOXIC_ORB": { name: "Toxic Orb", description: "It's a bizarre orb that exudes toxins when touched and will badly poison the holder during battle." }, + "FLAME_ORB": { name: "Flame Orb", description: "It's a bizarre orb that gives off heat when touched and will affect the holder with a burn during battle." }, + + "BATON": { name: "Baton", description: "Allows passing along effects when switching Pokémon, which also bypasses traps." }, + + "SHINY_CHARM": { name: "Shiny Charm", description: "Dramatically increases the chance of a wild Pokémon being Shiny." }, + "ABILITY_CHARM": { name: "Ability Charm", description: "Dramatically increases the chance of a wild Pokémon having a Hidden Ability." }, + + "IV_SCANNER": { name: "IV Scanner", description: "Allows scanning the IVs of wild Pokémon. 2 IVs are revealed per stack. The best IVs are shown first." }, + + "DNA_SPLICERS": { name: "DNA Splicers" }, + + "MINI_BLACK_HOLE": { name: "Mini Black Hole" }, + + "GOLDEN_POKEBALL": { name: "Golden Poké Ball", description: "Adds 1 extra item option at the end of every battle." }, + + "ENEMY_DAMAGE_BOOSTER": { name: "Damage Token", description: "Increases damage by 5%." }, + "ENEMY_DAMAGE_REDUCTION": { name: "Protection Token", description: "Reduces incoming damage by 2.5%." }, + "ENEMY_HEAL": { name: "Recovery Token", description: "Heals 2% of max HP every turn." }, + "ENEMY_ATTACK_POISON_CHANCE": { name: "Poison Token" }, + "ENEMY_ATTACK_PARALYZE_CHANCE": { name: "Paralyze Token" }, + "ENEMY_ATTACK_BURN_CHANCE": { name: "Burn Token" }, + "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Full Heal Token", description: "Adds a 2.5% chance every turn to heal a status condition." }, + "ENEMY_ENDURE_CHANCE": { name: "Endure Token" }, + "ENEMY_FUSED_CHANCE": { name: "Fusion Token", description: "Adds a 1% chance that a wild Pokémon will be a fusion." }, + }, + SpeciesBoosterItem: { + "LIGHT_BALL": { name: "Light Ball", description: "It's a mysterious orb that boosts Pikachu's Attack and Sp. Atk stats." }, + "THICK_CLUB": { name: "Thick Club", description: "This hard bone of unknown origin boosts Cubone or Marowak's Attack stat." }, + "METAL_POWDER": { name: "Metal Powder", description: "Extremely fine yet hard, this odd powder boosts Ditto's Defense stat." }, + "QUICK_POWDER": { name: "Quick Powder", description: "Extremely fine yet hard, this odd powder boosts Ditto's Speed stat." } + }, + TempBattleStatBoosterItem: { + "x_attack": "X Attack", + "x_defense": "X Defense", + "x_sp_atk": "X Sp. Atk", + "x_sp_def": "X Sp. Def", + "x_speed": "X Speed", + "x_accuracy": "X Accuracy", + "dire_hit": "Dire Hit", + }, + + TempBattleStatBoosterStatName: { + "ATK": "Attack", + "DEF": "Defense", + "SPATK": "Sp. Atk", + "SPDEF": "Sp. Def", + "SPD": "Speed", + "ACC": "Accuracy", + "CRIT": "Critical Hit Ratio", + "EVA": "Evasiveness", + "DEFAULT": "???", + }, + + AttackTypeBoosterItem: { + "silk_scarf": "Silk Scarf", + "black_belt": "Black Belt", + "sharp_beak": "Sharp Beak", + "poison_barb": "Poison Barb", + "soft_sand": "Soft Sand", + "hard_stone": "Hard Stone", + "silver_powder": "Silver Powder", + "spell_tag": "Spell Tag", + "metal_coat": "Metal Coat", + "charcoal": "Charcoal", + "mystic_water": "Mystic Water", + "miracle_seed": "Miracle Seed", + "magnet": "Magnet", + "twisted_spoon": "Twisted Spoon", + "never_melt_ice": "Never-Melt Ice", + "dragon_fang": "Dragon Fang", + "black_glasses": "Black Glasses", + "fairy_feather": "Fairy Feather", + }, + BaseStatBoosterItem: { + "hp_up": "HP Up", + "protein": "Protein", + "iron": "Iron", + "calcium": "Calcium", + "zinc": "Zinc", + "carbos": "Carbos", + }, + EvolutionItem: { + "NONE": "None", + + "LINKING_CORD": "Linking Cord", + "SUN_STONE": "Sun Stone", + "MOON_STONE": "Moon Stone", + "LEAF_STONE": "Leaf Stone", + "FIRE_STONE": "Fire Stone", + "WATER_STONE": "Water Stone", + "THUNDER_STONE": "Thunder Stone", + "ICE_STONE": "Ice Stone", + "DUSK_STONE": "Dusk Stone", + "DAWN_STONE": "Dawn Stone", + "SHINY_STONE": "Shiny Stone", + "CRACKED_POT": "Cracked Pot", + "SWEET_APPLE": "Sweet Apple", + "TART_APPLE": "Tart Apple", + "STRAWBERRY_SWEET": "Strawberry Sweet", + "UNREMARKABLE_TEACUP": "Unremarkable Teacup", + + "CHIPPED_POT": "Chipped Pot", + "BLACK_AUGURITE": "Black Augurite", + "GALARICA_CUFF": "Galarica Cuff", + "GALARICA_WREATH": "Galarica Wreath", + "PEAT_BLOCK": "Peat Block", + "AUSPICIOUS_ARMOR": "Auspicious Armor", + "MALICIOUS_ARMOR": "Malicious Armor", + "MASTERPIECE_TEACUP": "Masterpiece Teacup", + "METAL_ALLOY": "Metal Alloy", + "SCROLL_OF_DARKNESS": "Scroll Of Darkness", + "SCROLL_OF_WATERS": "Scroll Of Waters", + "SYRUPY_APPLE": "Syrupy Apple", + }, + FormChangeItem: { + "NONE": "None", + + "ABOMASITE": "Abomasite", + "ABSOLITE": "Absolite", + "AERODACTYLITE": "Aerodactylite", + "AGGRONITE": "Aggronite", + "ALAKAZITE": "Alakazite", + "ALTARIANITE": "Altarianite", + "AMPHAROSITE": "Ampharosite", + "AUDINITE": "Audinite", + "BANETTITE": "Banettite", + "BEEDRILLITE": "Beedrillite", + "BLASTOISINITE": "Blastoisinite", + "BLAZIKENITE": "Blazikenite", + "CAMERUPTITE": "Cameruptite", + "CHARIZARDITE_X": "Charizardite X", + "CHARIZARDITE_Y": "Charizardite Y", + "DIANCITE": "Diancite", + "GALLADITE": "Galladite", + "GARCHOMPITE": "Garchompite", + "GARDEVOIRITE": "Gardevoirite", + "GENGARITE": "Gengarite", + "GLALITITE": "Glalitite", + "GYARADOSITE": "Gyaradosite", + "HERACRONITE": "Heracronite", + "HOUNDOOMINITE": "Houndoominite", + "KANGASKHANITE": "Kangaskhanite", + "LATIASITE": "Latiasite", + "LATIOSITE": "Latiosite", + "LOPUNNITE": "Lopunnite", + "LUCARIONITE": "Lucarionite", + "MANECTITE": "Manectite", + "MAWILITE": "Mawilite", + "MEDICHAMITE": "Medichamite", + "METAGROSSITE": "Metagrossite", + "MEWTWONITE_X": "Mewtwonite X", + "MEWTWONITE_Y": "Mewtwonite Y", + "PIDGEOTITE": "Pidgeotite", + "PINSIRITE": "Pinsirite", + "RAYQUAZITE": "Rayquazite", + "SABLENITE": "Sablenite", + "SALAMENCITE": "Salamencite", + "SCEPTILITE": "Sceptilite", + "SCIZORITE": "Scizorite", + "SHARPEDONITE": "Sharpedonite", + "SLOWBRONITE": "Slowbronite", + "STEELIXITE": "Steelixite", + "SWAMPERTITE": "Swampertite", + "TYRANITARITE": "Tyranitarite", + "VENUSAURITE": "Venusaurite", + + "BLUE_ORB": "Blue Orb", + "RED_ORB": "Red Orb", + "SHARP_METEORITE": "Sharp Meteorite", + "HARD_METEORITE": "Hard Meteorite", + "SMOOTH_METEORITE": "Smooth Meteorite", + "ADAMANT_CRYSTAL": "Adamant Crystal", + "LUSTROUS_GLOBE": "Lustrous Globe", + "GRISEOUS_CORE": "Griseous Core", + "REVEAL_GLASS": "Reveal Glass", + "GRACIDEA": "Gracidea", + "MAX_MUSHROOMS": "Max Mushrooms", + "DARK_STONE": "Dark Stone", + "LIGHT_STONE": "Light Stone", + "PRISON_BOTTLE": "Prison Bottle", + "N_LUNARIZER": "N Lunarizer", + "N_SOLARIZER": "N Solarizer", + "RUSTED_SWORD": "Rusted Sword", + "RUSTED_SHIELD": "Rusted Shield", + "ICY_REINS_OF_UNITY": "Icy Reins Of Unity", + "SHADOW_REINS_OF_UNITY": "Shadow Reins Of Unity", + "WELLSPRING_MASK": "Wellspring Mask", + "HEARTHFLAME_MASK": "Hearthflame Mask", + "CORNERSTONE_MASK": "Cornerstone Mask", + "SHOCK_DRIVE": "Shock Drive", + "BURN_DRIVE": "Burn Drive", + "CHILL_DRIVE": "Chill Drive", + "DOUSE_DRIVE": "Douse Drive", + + "FIST_PLATE": "Fist Plate", + "SKY_PLATE": "Sky Plate", + "TOXIC_PLATE": "Toxic Plate", + "EARTH_PLATE": "Earth Plate", + "STONE_PLATE": "Stone Plate", + "INSECT_PLATE": "Insect Plate", + "SPOOKY_PLATE": "Spooky Plate", + "IRON_PLATE": "Iron Plate", + "FLAME_PLATE": "Flame Plate", + "SPLASH_PLATE": "Splash Plate", + "MEADOW_PLATE": "Meadow Plate", + "ZAP_PLATE": "Zap Plate", + "MIND_PLATE": "Mind Plate", + "ICICLE_PLATE": "Icicle Plate", + "DRACO_PLATE": "Draco Plate", + "DREAD_PLATE": "Dread Plate", + "PIXIE_PLATE": "Pixie Plate", + "BLANK_PLATE": "Blank Plate", + "LEGEND_PLATE": "Legend Plate", + "FIGHTING_MEMORY": "Fighting Memory", + "FLYING_MEMORY": "Flying Memory", + "POISON_MEMORY": "Poison Memory", + "GROUND_MEMORY": "Ground Memory", + "ROCK_MEMORY": "Rock Memory", + "BUG_MEMORY": "Bug Memory", + "GHOST_MEMORY": "Ghost Memory", + "STEEL_MEMORY": "Steel Memory", + "FIRE_MEMORY": "Fire Memory", + "WATER_MEMORY": "Water Memory", + "GRASS_MEMORY": "Grass Memory", + "ELECTRIC_MEMORY": "Electric Memory", + "PSYCHIC_MEMORY": "Psychic Memory", + "ICE_MEMORY": "Ice Memory", + "DRAGON_MEMORY": "Dragon Memory", + "DARK_MEMORY": "Dark Memory", + "FAIRY_MEMORY": "Fairy Memory", + "BLANK_MEMORY": "Blank Memory", + }, +} as const; diff --git a/src/locales/ca-ES/modifier.ts b/src/locales/ca-ES/modifier.ts new file mode 100644 index 00000000000..26a6a9c18ae --- /dev/null +++ b/src/locales/ca-ES/modifier.ts @@ -0,0 +1,14 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const modifier: SimpleTranslationEntries = { + "surviveDamageApply": "{{pokemonNameWithAffix}} hung on\nusing its {{typeName}}!", + "turnHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", + "hitHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", + "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} was revived\nby its {{typeName}}!", + "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}'s lowered stats were restored\nby its {{typeName}}!", + "moneyInterestApply": "You received interest of ₽{{moneyAmount}}\nfrom the {{typeName}}!", + "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was absorbed\nby {{pokemonName}}'s {{typeName}}!", + "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was snatched\nby {{pokemonName}}'s {{typeName}}!", + "enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestored some HP!", + "bypassSpeedChanceApply": "{{pokemonName}} can act faster than normal, thanks to its {{itemName}}!", +} as const; diff --git a/src/locales/ca-ES/move-trigger.ts b/src/locales/ca-ES/move-trigger.ts new file mode 100644 index 00000000000..b85f27228be --- /dev/null +++ b/src/locales/ca-ES/move-trigger.ts @@ -0,0 +1,64 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const moveTriggers: SimpleTranslationEntries = { + "hitWithRecoil" : "{{pokemonName}} was damaged by the recoil!", + "cutHpPowerUpMove": "{{pokemonName}} cut its own HP to power up its move!", + "absorbedElectricity": "{{pokemonName}} absorbed electricity!", + "switchedStatChanges": "{{pokemonName}} switched stat changes with the target!", + "goingAllOutForAttack": "{{pokemonName}} is going all out for this attack!", + "regainedHealth": "{{pokemonName}} regained\nhealth!", + "keptGoingAndCrashed": "{{pokemonName}} kept going\nand crashed!", + "fled": "{{pokemonName}} fled!", + "cannotBeSwitchedOut": "{{pokemonName}} can't be switched out!", + "swappedAbilitiesWithTarget": "{{pokemonName}} swapped\nabilities with its target!", + "coinsScatteredEverywhere": "Coins were scattered everywhere!", + "attackedByItem": "{{pokemonName}} is about to be attacked by its {{itemName}}!", + "whippedUpAWhirlwind": "{{pokemonName}} whipped\nup a whirlwind!", + "flewUpHigh": "{{pokemonName}} flew\nup high!", + "tookInSunlight": "{{pokemonName}} absorbed light!", + "dugAHole": "{{pokemonName}} burrowed its way under the ground!", + "loweredItsHead": "{{pokemonName}} tucked in its head!", + "isGlowing": "{{pokemonName}} became cloaked in a harsh light!", + "bellChimed": "A bell chimed!", + "foresawAnAttack": "{{pokemonName}} foresaw\nan attack!", + "isTighteningFocus": "{{pokemonName}} is\ntightening its focus!", + "hidUnderwater": "{{pokemonName}} hid\nunderwater!", + "soothingAromaWaftedThroughArea": "A soothing aroma wafted through the area!", + "sprangUp": "{{pokemonName}} sprang up!", + "choseDoomDesireAsDestiny": "{{pokemonName}} chose\nDoom Desire as its destiny!", + "vanishedInstantly": "{{pokemonName}} vanished\ninstantly!", + "tookTargetIntoSky": "{{pokemonName}} took {{targetName}}\ninto the sky!", + "becameCloakedInFreezingLight": "{{pokemonName}} became cloaked\nin a freezing light!", + "becameCloakedInFreezingAir": "{{pokemonName}} became cloaked\nin freezing air!", + "isChargingPower": "{{pokemonName}} is absorbing power!", + "burnedItselfOut": "{{pokemonName}} burned itself out!", + "startedHeatingUpBeak": "{{pokemonName}} started\nheating up its beak!", + "isOverflowingWithSpacePower": "{{pokemonName}} is overflowing\nwith space power!", + "usedUpAllElectricity": "{{pokemonName}} used up all its electricity!", + "stoleItem": "{{pokemonName}} stole\n{{targetName}}'s {{itemName}}!", + "incineratedItem": "{{pokemonName}} incinerated\n{{targetName}}'s {{itemName}}!", + "knockedOffItem": "{{pokemonName}} knocked off\n{{targetName}}'s {{itemName}}!", + "tookMoveAttack": "{{pokemonName}} took\nthe {{moveName}} attack!", + "cutOwnHpAndMaximizedStat": "{{pokemonName}} cut its own HP\nand maximized its {{statName}}!", + "copiedStatChanges": "{{pokemonName}} copied\n{{targetName}}'s stat changes!", + "magnitudeMessage": "Magnitude {{magnitude}}!", + "tookAimAtTarget": "{{pokemonName}} took aim\nat {{targetName}}!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{typeName}} type!", + "copiedMove": "{{pokemonName}} copied\n{{moveName}}!", + "sketchedMove": "{{pokemonName}} sketched\n{{moveName}}!", + "acquiredAbility": "The {{pokemonName}} acquired\n{{abilityName}}!", + "copiedTargetAbility": "{{pokemonName}} copied the {{targetName}}'s\n{{abilityName}}!", + "transformedIntoTarget": "{{pokemonName}} transformed\ninto {{targetName}}!", + "tryingToTakeFoeDown": "{{pokemonName}} is hoping to take its attacker down with it!", + "addType": "{{typeName}} was added to\n{{pokemonName}}!", + "cannotUseMove": "{{pokemonName}} cannot use {{moveName}}!", + "healHp": "{{pokemonName}} had its HP restored.", + "sacrificialFullRestore": "{{pokemonName}}'s Healing Wish\nwas granted!", + "invertStats": "{{pokemonName}}'s stat changes\nwere all reversed!", + "resetStats": "{{pokemonName}}'s stat changes\nwere eliminated!", + "faintCountdown": "{{pokemonName}}\nwill faint in {{turnCount}} turns.", + "copyType": "{{pokemonName}}'s type became the same as\n{{targetPokemonName}}'s type!", + "suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!", + "swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!", + "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!", +} as const; diff --git a/src/locales/ca-ES/move.ts b/src/locales/ca-ES/move.ts new file mode 100644 index 00000000000..cec7c93ede5 --- /dev/null +++ b/src/locales/ca-ES/move.ts @@ -0,0 +1,3812 @@ +import { MoveTranslationEntries } from "#app/interfaces/locales"; + +export const move: MoveTranslationEntries = { + "pound": { + name: "Pound", + effect: "The target is physically pounded with a long tail, a foreleg, or the like." + }, + "karateChop": { + name: "Karate Chop", + effect: "The target is attacked with a sharp chop. Critical hits land more easily." + }, + "doubleSlap": { + name: "Double Slap", + effect: "The target is slapped repeatedly, back and forth, two to five times in a row." + }, + "cometPunch": { + name: "Comet Punch", + effect: "The target is hit with a flurry of punches that strike two to five times in a row." + }, + "megaPunch": { + name: "Mega Punch", + effect: "The target is slugged by a punch thrown with muscle-packed power." + }, + "payDay": { + name: "Pay Day", + effect: "Numerous coins are hurled at the target to inflict damage. Money is earned after the battle." + }, + "firePunch": { + name: "Fire Punch", + effect: "The target is punched with a fiery fist. This may also leave the target with a burn." + }, + "icePunch": { + name: "Ice Punch", + effect: "The target is punched with an icy fist. This may also leave the target frozen." + }, + "thunderPunch": { + name: "Thunder Punch", + effect: "The target is punched with an electrified fist. This may also leave the target with paralysis." + }, + "scratch": { + name: "Scratch", + effect: "Hard, pointed, sharp claws rake the target to inflict damage." + }, + "viseGrip": { + name: "Vise Grip", + effect: "The target is gripped and squeezed from both sides to inflict damage." + }, + "guillotine": { + name: "Guillotine", + effect: "A vicious, tearing attack with big pincers. The target faints instantly if this attack hits." + }, + "razorWind": { + name: "Razor Wind", + effect: "In this two-turn attack, blades of wind hit opposing Pokémon on the second turn. Critical hits land more easily." + }, + "swordsDance": { + name: "Swords Dance", + effect: "A frenetic dance to uplift the fighting spirit. This sharply raises the user's Attack stat." + }, + "cut": { + name: "Cut", + effect: "The target is cut with a scythe or claw." + }, + "gust": { + name: "Gust", + effect: "A gust of wind is whipped up by wings and launched at the target to inflict damage." + }, + "wingAttack": { + name: "Wing Attack", + effect: "The target is struck with large, imposing wings spread wide to inflict damage." + }, + "whirlwind": { + name: "Whirlwind", + effect: "The target is blown away, and a different Pokémon is dragged out. In the wild, this ends a battle against a single Pokémon." + }, + "fly": { + name: "Fly", + effect: "The user flies up into the sky and then strikes its target on the next turn." + }, + "bind": { + name: "Bind", + effect: "Things such as long bodies or tentacles are used to bind and squeeze the target for four to five turns." + }, + "slam": { + name: "Slam", + effect: "The target is slammed with a long tail, vines, or the like to inflict damage." + }, + "vineWhip": { + name: "Vine Whip", + effect: "The target is struck with slender, whiplike vines to inflict damage." + }, + "stomp": { + name: "Stomp", + effect: "The target is stomped with a big foot. This may also make the target flinch." + }, + "doubleKick": { + name: "Double Kick", + effect: "The target is quickly kicked twice in succession using both feet." + }, + "megaKick": { + name: "Mega Kick", + effect: "The target is attacked by a kick launched with muscle-packed power." + }, + "jumpKick": { + name: "Jump Kick", + effect: "The user jumps up high, then strikes with a kick. If the kick misses, the user hurts itself." + }, + "rollingKick": { + name: "Rolling Kick", + effect: "The user lashes out with a quick, spinning kick. This may also make the target flinch." + }, + "sandAttack": { + name: "Sand Attack", + effect: "Sand is hurled in the target's face, reducing the target's accuracy." + }, + "headbutt": { + name: "Headbutt", + effect: "The user sticks out its head and attacks by charging straight into the target. This may also make the target flinch." + }, + "hornAttack": { + name: "Horn Attack", + effect: "The target is jabbed with a sharply pointed horn to inflict damage." + }, + "furyAttack": { + name: "Fury Attack", + effect: "The target is jabbed repeatedly with a horn or beak two to five times in a row." + }, + "hornDrill": { + name: "Horn Drill", + effect: "The user stabs the target with a horn that rotates like a drill. The target faints instantly if this attack hits." + }, + "tackle": { + name: "Tackle", + effect: "A physical attack in which the user charges and slams into the target with its whole body." + }, + "bodySlam": { + name: "Body Slam", + effect: "The user drops onto the target with its full body weight. This may also leave the target with paralysis." + }, + "wrap": { + name: "Wrap", + effect: "A long body, vines, or the like are used to wrap and squeeze the target for four to five turns." + }, + "takeDown": { + name: "Take Down", + effect: "A reckless, full-body charge attack for slamming into the target. This also damages the user a little." + }, + "thrash": { + name: "Thrash", + effect: "The user rampages and attacks for two to three turns. The user then becomes confused." + }, + "doubleEdge": { + name: "Double-Edge", + effect: "A reckless, life-risking tackle in which the user rushes the target. This also damages the user quite a lot." + }, + "tailWhip": { + name: "Tail Whip", + effect: "The user wags its tail cutely, making opposing Pokémon less wary and lowering their Defense stats." + }, + "poisonSting": { + name: "Poison Sting", + effect: "The user stabs the target with a poisonous stinger. This may also poison the target." + }, + "twineedle": { + name: "Twineedle", + effect: "The user damages the target twice in succession by jabbing it with two spikes. This may also poison the target." + }, + "pinMissile": { + name: "Pin Missile", + effect: "Sharp spikes are shot at the target in rapid succession. They hit two to five times in a row." + }, + "leer": { + name: "Leer", + effect: "The user gives opposing Pokémon an intimidating leer that lowers the Defense stat." + }, + "bite": { + name: "Bite", + effect: "The target is bitten with viciously sharp fangs. This may also make the target flinch." + }, + "growl": { + name: "Growl", + effect: "The user growls in an endearing way, making opposing Pokémon less wary. This lowers their Attack stats." + }, + "roar": { + name: "Roar", + effect: "The target is scared off, and a different Pokémon is dragged out. In the wild, this ends a battle against a single Pokémon." + }, + "sing": { + name: "Sing", + effect: "A soothing lullaby is sung in a calming voice that puts the target into a deep slumber." + }, + "supersonic": { + name: "Supersonic", + effect: "The user generates odd sound waves from its body that confuse the target." + }, + "sonicBoom": { + name: "Sonic Boom", + effect: "The target is hit with a destructive shock wave that always inflicts 20 HP damage." + }, + "disable": { + name: "Disable", + effect: "For four turns, this move prevents the target from using the move it last used." + }, + "acid": { + name: "Acid", + effect: "Opposing Pokémon are attacked with a spray of harsh acid. This may also lower their Sp. Def stats." + }, + "ember": { + name: "Ember", + effect: "The target is attacked with small flames. This may also leave the target with a burn." + }, + "flamethrower": { + name: "Flamethrower", + effect: "The target is scorched with an intense blast of fire. This may also leave the target with a burn." + }, + "mist": { + name: "Mist", + effect: "The user cloaks itself and its allies in a white mist that prevents any of their stats from being lowered for five turns." + }, + "waterGun": { + name: "Water Gun", + effect: "The target is blasted with a forceful shot of water." + }, + "hydroPump": { + name: "Hydro Pump", + effect: "The target is blasted by a huge volume of water launched under great pressure." + }, + "surf": { + name: "Surf", + effect: "The user attacks everything around it by swamping its surroundings with a giant wave." + }, + "iceBeam": { + name: "Ice Beam", + effect: "The target is struck with an icy-cold beam of energy. This may also leave the target frozen." + }, + "blizzard": { + name: "Blizzard", + effect: "A howling blizzard is summoned to strike opposing Pokémon. This may also leave the opposing Pokémon frozen." + }, + "psybeam": { + name: "Psybeam", + effect: "The target is attacked with a peculiar ray. This may also leave the target confused." + }, + "bubbleBeam": { + name: "Bubble Beam", + effect: "A spray of bubbles is forcefully ejected at the target. This may also lower the target's Speed stat." + }, + "auroraBeam": { + name: "Aurora Beam", + effect: "The target is hit with a rainbow-colored beam. This may also lower the target's Attack stat." + }, + "hyperBeam": { + name: "Hyper Beam", + effect: "The target is attacked with a powerful beam. The user can't move on the next turn." + }, + "peck": { + name: "Peck", + effect: "The target is jabbed with a sharply pointed beak or horn." + }, + "drillPeck": { + name: "Drill Peck", + effect: "A corkscrewing attack that strikes the target with a sharp beak acting as a drill." + }, + "submission": { + name: "Submission", + effect: "The user grabs the target and recklessly dives for the ground. This also damages the user a little." + }, + "lowKick": { + name: "Low Kick", + effect: "A powerful low kick that makes the target fall over. The heavier the target, the greater the move's power." + }, + "counter": { + name: "Counter", + effect: "A retaliation move that counters any physical attack, inflicting double the damage taken." + }, + "seismicToss": { + name: "Seismic Toss", + effect: "The target is thrown using the power of gravity. It inflicts damage equal to the user's level." + }, + "strength": { + name: "Strength", + effect: "The target is slugged with a punch thrown at maximum power." + }, + "absorb": { + name: "Absorb", + effect: "A nutrient-draining attack. The user's HP is restored by half the damage taken by the target." + }, + "megaDrain": { + name: "Mega Drain", + effect: "A nutrient-draining attack. The user's HP is restored by half the damage taken by the target." + }, + "leechSeed": { + name: "Leech Seed", + effect: "A seed is planted on the target. It steals some HP from the target every turn." + }, + "growth": { + name: "Growth", + effect: "The user's body grows all at once, raising the Attack and Sp. Atk stats." + }, + "razorLeaf": { + name: "Razor Leaf", + effect: "Sharp-edged leaves are launched to slash at opposing Pokémon. Critical hits land more easily." + }, + "solarBeam": { + name: "Solar Beam", + effect: "In this two-turn attack, the user gathers light, then blasts a bundled beam on the next turn." + }, + "poisonPowder": { + name: "Poison Powder", + effect: "The user scatters a cloud of poisonous dust that poisons the target." + }, + "stunSpore": { + name: "Stun Spore", + effect: "The user scatters a cloud of numbing powder that paralyzes the target." + }, + "sleepPowder": { + name: "Sleep Powder", + effect: "The user scatters a big cloud of sleep-inducing dust around the target." + }, + "petalDance": { + name: "Petal Dance", + effect: "The user attacks the target by scattering petals for two to three turns. The user then becomes confused." + }, + "stringShot": { + name: "String Shot", + effect: "Opposing Pokémon are bound with silk blown from the user's mouth that harshly lowers the Speed stat." + }, + "dragonRage": { + name: "Dragon Rage", + effect: "This attack hits the target with a shock wave of pure rage. This attack always inflicts 40 HP damage." + }, + "fireSpin": { + name: "Fire Spin", + effect: "The target becomes trapped within a fierce vortex of fire that rages for four to five turns." + }, + "thunderShock": { + name: "Thunder Shock", + effect: "A jolt of electricity crashes down on the target to inflict damage. This may also leave the target with paralysis." + }, + "thunderbolt": { + name: "Thunderbolt", + effect: "A strong electric blast crashes down on the target. This may also leave the target with paralysis." + }, + "thunderWave": { + name: "Thunder Wave", + effect: "The user launches a weak jolt of electricity that paralyzes the target." + }, + "thunder": { + name: "Thunder", + effect: "A wicked thunderbolt is dropped on the target to inflict damage. This may also leave the target with paralysis." + }, + "rockThrow": { + name: "Rock Throw", + effect: "The user picks up and throws a small rock at the target to attack." + }, + "earthquake": { + name: "Earthquake", + effect: "The user sets off an earthquake that strikes every Pokémon around it." + }, + "fissure": { + name: "Fissure", + effect: "The user opens up a fissure in the ground and drops the target in. The target faints instantly if this attack hits." + }, + "dig": { + name: "Dig", + effect: "The user burrows into the ground, then attacks on the next turn." + }, + "toxic": { + name: "Toxic", + effect: "A move that leaves the target badly poisoned. Its poison damage worsens every turn." + }, + "confusion": { + name: "Confusion", + effect: "The target is hit by a weak telekinetic force. This may also confuse the target." + }, + "psychic": { + name: "Psychic", + effect: "The target is hit by a strong telekinetic force. This may also lower the target's Sp. Def stat." + }, + "hypnosis": { + name: "Hypnosis", + effect: "The user employs hypnotic suggestion to make the target fall into a deep sleep." + }, + "meditate": { + name: "Meditate", + effect: "The user meditates to awaken the power deep within its body and raise its Attack stat." + }, + "agility": { + name: "Agility", + effect: "The user relaxes and lightens its body to move faster. This sharply raises the Speed stat." + }, + "quickAttack": { + name: "Quick Attack", + effect: "The user lunges at the target at a speed that makes it almost invisible. This move always goes first." + }, + "rage": { + name: "Rage", + effect: "As long as this move is in use, the power of rage raises the Attack stat each time the user is hit in battle." + }, + "teleport": { + name: "Teleport", + effect: "The user switches places with a party Pokémon in waiting, if any. If a wild Pokémon uses this move, it flees." + }, + "nightShade": { + name: "Night Shade", + effect: "The user makes the target see a frightening mirage. It inflicts damage equal to the user's level." + }, + "mimic": { + name: "Mimic", + effect: "The user copies the target's last move. The move can be used during battle until the Pokémon is switched out." + }, + "screech": { + name: "Screech", + effect: "An earsplitting screech harshly lowers the target's Defense stat." + }, + "doubleTeam": { + name: "Double Team", + effect: "By moving rapidly, the user makes illusory copies of itself to raise its evasiveness." + }, + "recover": { + name: "Recover", + effect: "Restoring its own cells, the user restores its own HP by half of its max HP." + }, + "harden": { + name: "Harden", + effect: "The user stiffens all the muscles in its body to raise its Defense stat." + }, + "minimize": { + name: "Minimize", + effect: "The user compresses its body to make itself look smaller, which sharply raises its evasiveness." + }, + "smokescreen": { + name: "Smokescreen", + effect: "The user releases an obscuring cloud of smoke or ink. This lowers the target's accuracy." + }, + "confuseRay": { + name: "Confuse Ray", + effect: "The target is exposed to a sinister ray that triggers confusion." + }, + "withdraw": { + name: "Withdraw", + effect: "The user withdraws its body into its hard shell, raising its Defense stat." + }, + "defenseCurl": { + name: "Defense Curl", + effect: "The user curls up to conceal weak spots and raise its Defense stat." + }, + "barrier": { + name: "Barrier", + effect: "The user throws up a sturdy wall that sharply raises its Defense stat." + }, + "lightScreen": { + name: "Light Screen", + effect: "A wondrous wall of light is put up to reduce damage from special attacks for five turns." + }, + "haze": { + name: "Haze", + effect: "The user creates a haze that eliminates every stat change among all the Pokémon engaged in battle." + }, + "reflect": { + name: "Reflect", + effect: "A wondrous wall of light is put up to reduce damage from physical attacks for five turns." + }, + "focusEnergy": { + name: "Focus Energy", + effect: "The user takes a deep breath and focuses so that critical hits land more easily." + }, + "bide": { + name: "Bide", + effect: "The user endures attacks for two turns, then strikes back to cause double the damage taken." + }, + "metronome": { + name: "Metronome", + effect: "The user waggles a finger and stimulates its brain into randomly using nearly any move." + }, + "mirrorMove": { + name: "Mirror Move", + effect: "The user counters the target by mimicking the target's last move." + }, + "selfDestruct": { + name: "Self-Destruct", + effect: "The user attacks everything around it by causing an explosion. The user faints upon using this move." + }, + "eggBomb": { + name: "Egg Bomb", + effect: "A large egg is hurled at the target with maximum force to inflict damage." + }, + "lick": { + name: "Lick", + effect: "The target is licked with a long tongue, causing damage. This may also leave the target with paralysis." + }, + "smog": { + name: "Smog", + effect: "The target is attacked with a discharge of filthy gases. This may also poison the target." + }, + "sludge": { + name: "Sludge", + effect: "Unsanitary sludge is hurled at the target. This may also poison the target." + }, + "boneClub": { + name: "Bone Club", + effect: "The user clubs the target with a bone. This may also make the target flinch." + }, + "fireBlast": { + name: "Fire Blast", + effect: "The target is attacked with an intense blast of all-consuming fire. This may also leave the target with a burn." + }, + "waterfall": { + name: "Waterfall", + effect: "The user charges at the target and may make it flinch." + }, + "clamp": { + name: "Clamp", + effect: "The target is clamped and squeezed by the user's very thick and sturdy shell for four to five turns." + }, + "swift": { + name: "Swift", + effect: "Star-shaped rays are shot at opposing Pokémon. This attack never misses." + }, + "skullBash": { + name: "Skull Bash", + effect: "The user tucks in its head to raise its Defense stat on the first turn, then rams the target on the next turn." + }, + "spikeCannon": { + name: "Spike Cannon", + effect: "Sharp spikes are shot at the target in rapid succession. They hit two to five times in a row." + }, + "constrict": { + name: "Constrict", + effect: "The target is attacked with long, creeping tentacles, vines, or the like. This may also lower the target's Speed stat." + }, + "amnesia": { + name: "Amnesia", + effect: "The user temporarily empties its mind to forget its concerns. This sharply raises the user's Sp. Def stat." + }, + "kinesis": { + name: "Kinesis", + effect: "The user distracts the target by bending a spoon. This lowers the target's accuracy." + }, + "softBoiled": { + name: "Soft-Boiled", + effect: "The user restores its own HP by up to half of its max HP." + }, + "highJumpKick": { + name: "High Jump Kick", + effect: "The target is attacked with a knee kick from a jump. If it misses, the user is hurt instead." + }, + "glare": { + name: "Glare", + effect: "The user intimidates the target with the pattern on its belly to cause paralysis." + }, + "dreamEater": { + name: "Dream Eater", + effect: "The user eats the dreams of a sleeping target. The user's HP is restored by half the damage taken by the target." + }, + "poisonGas": { + name: "Poison Gas", + effect: "A cloud of poison gas is sprayed in the face of opposing Pokémon, poisoning those it hits." + }, + "barrage": { + name: "Barrage", + effect: "Round objects are hurled at the target to strike two to five times in a row." + }, + "leechLife": { + name: "Leech Life", + effect: "The user drains the target's blood. The user's HP is restored by half the damage taken by the target." + }, + "lovelyKiss": { + name: "Lovely Kiss", + effect: "With a scary face, the user tries to force a kiss on the target. If it succeeds, the target falls asleep." + }, + "skyAttack": { + name: "Sky Attack", + effect: "A second-turn attack move where critical hits land more easily. This may also make the target flinch." + }, + "transform": { + name: "Transform", + effect: "The user transforms into a copy of the target right down to having the same move set." + }, + "bubble": { + name: "Bubble", + effect: "A spray of countless bubbles is jetted at the opposing Pokémon. This may also lower their Speed stat." + }, + "dizzyPunch": { + name: "Dizzy Punch", + effect: "The target is hit with rhythmically launched punches. This may also leave the target confused." + }, + "spore": { + name: "Spore", + effect: "The user scatters bursts of spores that induce sleep." + }, + "flash": { + name: "Flash", + effect: "The user flashes a bright light that cuts the target's accuracy." + }, + "psywave": { + name: "Psywave", + effect: "The target is attacked with an odd psychic wave. The attack varies in intensity." + }, + "splash": { + name: "Splash", + effect: "The user just flops and splashes around to no effect at all..." + }, + "acidArmor": { + name: "Acid Armor", + effect: "The user alters its cellular structure to liquefy itself, sharply raising its Defense stat." + }, + "crabhammer": { + name: "Crabhammer", + effect: "The target is hammered with a large pincer. Critical hits land more easily." + }, + "explosion": { + name: "Explosion", + effect: "The user attacks everything around it by causing a tremendous explosion. The user faints upon using this move." + }, + "furySwipes": { + name: "Fury Swipes", + effect: "The target is raked with sharp claws or scythes quickly two to five times in a row." + }, + "bonemerang": { + name: "Bonemerang", + effect: "The user throws the bone it holds. The bone loops around to hit the target twice—coming and going." + }, + "rest": { + name: "Rest", + effect: "The user goes to sleep for two turns. This fully restores the user's HP and heals any status conditions." + }, + "rockSlide": { + name: "Rock Slide", + effect: "Large boulders are hurled at opposing Pokémon to inflict damage. This may also make the opposing Pokémon flinch." + }, + "hyperFang": { + name: "Hyper Fang", + effect: "The user bites hard on the target with its sharp front fangs. This may also make the target flinch." + }, + "sharpen": { + name: "Sharpen", + effect: "The user makes its edges more jagged, which raises its Attack stat." + }, + "conversion": { + name: "Conversion", + effect: "The user changes its type to become the same type as the move at the top of the list of moves it knows." + }, + "triAttack": { + name: "Tri Attack", + effect: "The user strikes with a simultaneous three-beam attack. This may also burn, freeze, or paralyze the target." + }, + "superFang": { + name: "Super Fang", + effect: "The user chomps hard on the target with its sharp front fangs. This cuts the target's HP in half." + }, + "slash": { + name: "Slash", + effect: "The target is attacked with a slash of claws or blades. Critical hits land more easily." + }, + "substitute": { + name: "Substitute", + effect: "The user creates a substitute for itself using some of its HP. The substitute serves as the user's decoy." + }, + "struggle": { + name: "Struggle", + effect: "This attack is used in desperation only if the user has no PP. It also damages the user a little." + }, + "sketch": { + name: "Sketch", + effect: "It enables the user to permanently learn the move last used by the target. Once used, Sketch disappears." + }, + "tripleKick": { + name: "Triple Kick", + effect: "A consecutive three-kick attack that becomes more powerful with each successful hit." + }, + "thief": { + name: "Thief", + effect: "The user attacks and has a 30% chance to steal the target's held item simultaneously." + }, + "spiderWeb": { + name: "Spider Web", + effect: "The user ensnares the target with thin, gooey silk so it can't flee from battle." + }, + "mindReader": { + name: "Mind Reader", + effect: "The user senses the target's movements with its mind to ensure its next attack does not miss the target." + }, + "nightmare": { + name: "Nightmare", + effect: "A sleeping target sees a nightmare that inflicts some damage every turn." + }, + "flameWheel": { + name: "Flame Wheel", + effect: "The user cloaks itself in fire and charges at the target. This may also leave the target with a burn." + }, + "snore": { + name: "Snore", + effect: "This attack can be used only if the user is asleep. The harsh noise may also make the target flinch." + }, + "curse": { + name: "Curse", + effect: "A move that works differently for the Ghost type than for all other types." + }, + "flail": { + name: "Flail", + effect: "The user flails about aimlessly to attack. The less HP the user has, the greater the move's power." + }, + "conversion2": { + name: "Conversion 2", + effect: "The user changes its type to make itself resistant to the type of the attack the target used last." + }, + "aeroblast": { + name: "Aeroblast", + effect: "A vortex of air is shot at the target to inflict damage. Critical hits land more easily." + }, + "cottonSpore": { + name: "Cotton Spore", + effect: "The user releases cotton-like spores that cling to opposing Pokémon, which harshly lowers their Speed stats." + }, + "reversal": { + name: "Reversal", + effect: "An all-out attack that becomes more powerful the less HP the user has." + }, + "spite": { + name: "Spite", + effect: "The user unleashes its grudge on the move last used by the target by cutting 4 PP from it." + }, + "powderSnow": { + name: "Powder Snow", + effect: "The user attacks with a chilling gust of powdery snow. This may also freeze opposing Pokémon." + }, + "protect": { + name: "Protect", + effect: "This move enables the user to protect itself from all attacks. Its chance of failing rises if it is used in succession." + }, + "machPunch": { + name: "Mach Punch", + effect: "The user throws a punch at blinding speed. This move always goes first." + }, + "scaryFace": { + name: "Scary Face", + effect: "The user frightens the target with a scary face to harshly lower its Speed stat." + }, + "feintAttack": { + name: "Feint Attack", + effect: "The user approaches the target disarmingly, then throws a sucker punch. This attack never misses." + }, + "sweetKiss": { + name: "Sweet Kiss", + effect: "The user kisses the target with a sweet, angelic cuteness that causes confusion." + }, + "bellyDrum": { + name: "Belly Drum", + effect: "The user maximizes its Attack stat in exchange for HP equal to half its max HP." + }, + "sludgeBomb": { + name: "Sludge Bomb", + effect: "Unsanitary sludge is hurled at the target. This may also poison the target." + }, + "mudSlap": { + name: "Mud-Slap", + effect: "The user hurls mud in the target's face to inflict damage and lower its accuracy." + }, + "octazooka": { + name: "Octazooka", + effect: "The user attacks by spraying ink in the target's face or eyes. This may also lower the target's accuracy." + }, + "spikes": { + name: "Spikes", + effect: "The user lays a trap of spikes at the opposing team's feet. The trap hurts Pokémon that switch into battle." + }, + "zapCannon": { + name: "Zap Cannon", + effect: "The user fires an electric blast like a cannon to inflict damage and cause paralysis." + }, + "foresight": { + name: "Foresight", + effect: "Enables a Ghost-type target to be hit by Normal- and Fighting-type attacks. This also enables an evasive target to be hit." + }, + "destinyBond": { + name: "Destiny Bond", + effect: "After using this move, if the user faints, the Pokémon that landed the knockout hit also faints. Its chance of failing rises if it is used in succession." + }, + "perishSong": { + name: "Perish Song", + effect: "Any Pokémon that hears this song faints in three turns, unless it switches out of battle." + }, + "icyWind": { + name: "Icy Wind", + effect: "The user attacks with a gust of chilled air. This also lowers opposing Pokémon's Speed stats." + }, + "detect": { + name: "Detect", + effect: "This move enables the user to protect itself from all attacks. Its chance of failing rises if it is used in succession." + }, + "boneRush": { + name: "Bone Rush", + effect: "The user strikes the target with a hard bone two to five times in a row." + }, + "lockOn": { + name: "Lock-On", + effect: "The user takes sure aim at the target. This ensures the next attack does not miss the target." + }, + "outrage": { + name: "Outrage", + effect: "The user rampages and attacks for two to three turns. The user then becomes confused." + }, + "sandstorm": { + name: "Sandstorm", + effect: "A five-turn sandstorm is summoned to hurt all combatants except Rock, Ground, and Steel types. It raises the Sp. Def stat of Rock types." + }, + "gigaDrain": { + name: "Giga Drain", + effect: "A nutrient-draining attack. The user's HP is restored by half the damage taken by the target." + }, + "endure": { + name: "Endure", + effect: "The user endures any attack with at least 1 HP. Its chance of failing rises if it is used in succession." + }, + "charm": { + name: "Charm", + effect: "The user gazes at the target rather charmingly, making it less wary. This harshly lowers the target's Attack stat." + }, + "rollout": { + name: "Rollout", + effect: "The user continually rolls into the target over five turns. It becomes more powerful each time it hits." + }, + "falseSwipe": { + name: "False Swipe", + effect: "A restrained attack that prevents the target from fainting. The target is left with at least 1 HP." + }, + "swagger": { + name: "Swagger", + effect: "The user enrages and confuses the target. However, this also sharply raises the target's Attack stat." + }, + "milkDrink": { + name: "Milk Drink", + effect: "The user restores its own HP by up to half of its max HP." + }, + "spark": { + name: "Spark", + effect: "The user throws an electrically charged tackle at the target. This may also leave the target with paralysis." + }, + "furyCutter": { + name: "Fury Cutter", + effect: "The target is slashed with scythes or claws. This attack becomes more powerful if it hits in succession." + }, + "steelWing": { + name: "Steel Wing", + effect: "The target is hit with wings of steel. This may also raise the user's Defense stat." + }, + "meanLook": { + name: "Mean Look", + effect: "The user pins the target with a dark, arresting look. The target becomes unable to flee." + }, + "attract": { + name: "Attract", + effect: "If it is the opposite gender of the user, the target becomes infatuated and less likely to attack." + }, + "sleepTalk": { + name: "Sleep Talk", + effect: "While it is asleep, the user randomly uses one of the moves it knows." + }, + "healBell": { + name: "Heal Bell", + effect: "The user makes a soothing bell chime to heal the status conditions of all the party Pokémon." + }, + "return": { + name: "Return", + effect: "This full-power attack grows more powerful the more the user likes its Trainer." + }, + "present": { + name: "Present", + effect: "The user attacks by giving the target a gift with a hidden trap. It restores HP sometimes, however." + }, + "frustration": { + name: "Frustration", + effect: "This full-power attack grows more powerful the less the user likes its Trainer." + }, + "safeguard": { + name: "Safeguard", + effect: "The user creates a protective field that prevents status conditions for five turns." + }, + "painSplit": { + name: "Pain Split", + effect: "The user adds its HP to the target's HP, then equally shares the combined HP with the target." + }, + "sacredFire": { + name: "Sacred Fire", + effect: "The target is razed with a mystical fire of great intensity. This may also leave the target with a burn." + }, + "magnitude": { + name: "Magnitude", + effect: "The user attacks everything around it with a ground-shaking quake. Its power varies." + }, + "dynamicPunch": { + name: "Dynamic Punch", + effect: "The user punches the target with full, concentrated power. This confuses the target if it hits." + }, + "megahorn": { + name: "Megahorn", + effect: "Using its tough and impressive horn, the user rams into the target with no letup." + }, + "dragonBreath": { + name: "Dragon Breath", + effect: "The user exhales a mighty gust that inflicts damage. This may also leave the target with paralysis." + }, + "batonPass": { + name: "Baton Pass", + effect: "The user switches places with a party Pokémon in waiting and passes along any stat changes." + }, + "encore": { + name: "Encore", + effect: "The user compels the target to keep using the move it encored for three turns." + }, + "pursuit": { + name: "Pursuit", + effect: "The power of this attack move is doubled if it's used on a target that's switching out of battle." + }, + "rapidSpin": { + name: "Rapid Spin", + effect: "A spin attack that can also eliminate such moves as Bind, Wrap, and Leech Seed. This also raises the user's Speed stat." + }, + "sweetScent": { + name: "Sweet Scent", + effect: "A sweet scent that harshly lowers opposing Pokémon's evasiveness." + }, + "ironTail": { + name: "Iron Tail", + effect: "The target is slammed with a steel-hard tail. This may also lower the target's Defense stat." + }, + "metalClaw": { + name: "Metal Claw", + effect: "The target is raked with steel claws. This may also raise the user's Attack stat." + }, + "vitalThrow": { + name: "Vital Throw", + effect: "The user attacks last. In return, this throw move never misses." + }, + "morningSun": { + name: "Morning Sun", + effect: "The user restores its own HP. The amount of HP regained varies with the weather." + }, + "synthesis": { + name: "Synthesis", + effect: "The user restores its own HP. The amount of HP regained varies with the weather." + }, + "moonlight": { + name: "Moonlight", + effect: "The user restores its own HP. The amount of HP regained varies with the weather." + }, + "hiddenPower": { + name: "Hidden Power", + effect: "A unique attack that varies in type depending on the Pokémon using it." + }, + "crossChop": { + name: "Cross Chop", + effect: "The user delivers a double chop with its forearms crossed. Critical hits land more easily." + }, + "twister": { + name: "Twister", + effect: "The user whips up a vicious tornado to tear at opposing Pokémon. This may also make them flinch." + }, + "rainDance": { + name: "Rain Dance", + effect: "The user summons a heavy rain that falls for five turns, powering up Water-type moves. It lowers the power of Fire-type moves." + }, + "sunnyDay": { + name: "Sunny Day", + effect: "The user intensifies the sun for five turns, powering up Fire-type moves. It lowers the power of Water-type moves." + }, + "crunch": { + name: "Crunch", + effect: "The user crunches up the target with sharp fangs. This may also lower the target's Defense stat." + }, + "mirrorCoat": { + name: "Mirror Coat", + effect: "A retaliation move that counters any special attack, inflicting double the damage taken." + }, + "psychUp": { + name: "Psych Up", + effect: "The user hypnotizes itself into copying any stat change made by the target." + }, + "extremeSpeed": { + name: "Extreme Speed", + effect: "The user charges the target at blinding speed. This move always goes first." + }, + "ancientPower": { + name: "Ancient Power", + effect: "The user attacks with a prehistoric power. This may also raise all the user's stats at once." + }, + "shadowBall": { + name: "Shadow Ball", + effect: "The user hurls a shadowy blob at the target. This may also lower the target's Sp. Def stat." + }, + "futureSight": { + name: "Future Sight", + effect: "Two turns after this move is used, a hunk of psychic energy attacks the target." + }, + "rockSmash": { + name: "Rock Smash", + effect: "The user attacks with a punch. This may also lower the target's Defense stat." + }, + "whirlpool": { + name: "Whirlpool", + effect: "The user traps the target in a violent swirling whirlpool for four to five turns." + }, + "beatUp": { + name: "Beat Up", + effect: "The user gets all party Pokémon to attack the target. The more party Pokémon, the greater the number of attacks." + }, + "fakeOut": { + name: "Fake Out", + effect: "This attack hits first and makes the target flinch. It only works the first turn each time the user enters battle." + }, + "uproar": { + name: "Uproar", + effect: "The user attacks in an uproar for three turns. During that time, no Pokémon can fall asleep." + }, + "stockpile": { + name: "Stockpile", + effect: "The user charges up power and raises both its Defense and Sp. Def stats. The move can be used three times." + }, + "spitUp": { + name: "Spit Up", + effect: "The power stored using the move Stockpile is released at once in an attack. The more power is stored, the greater the move's power." + }, + "swallow": { + name: "Swallow", + effect: "The power stored using the move Stockpile is absorbed by the user to heal its HP. Storing more power heals more HP." + }, + "heatWave": { + name: "Heat Wave", + effect: "The user attacks by exhaling hot breath on opposing Pokémon. This may also leave those Pokémon with a burn." + }, + "hail": { + name: "Hail", + effect: "The user summons a hailstorm lasting five turns. It damages all Pokémon except Ice types." + }, + "torment": { + name: "Torment", + effect: "The user torments and enrages the target, making it incapable of using the same move twice in a row." + }, + "flatter": { + name: "Flatter", + effect: "Flattery is used to confuse the target. However, this also raises the target's Sp. Atk stat." + }, + "willOWisp": { + name: "Will-O-Wisp", + effect: "The user shoots a sinister flame at the target to inflict a burn." + }, + "memento": { + name: "Memento", + effect: "The user faints when using this move. In return, this harshly lowers the target's Attack and Sp. Atk stats." + }, + "facade": { + name: "Facade", + effect: "This attack move doubles its power if the user is poisoned, burned, or paralyzed." + }, + "focusPunch": { + name: "Focus Punch", + effect: "The user focuses its mind before launching a punch. This move fails if the user is hit before it is used." + }, + "smellingSalts": { + name: "Smelling Salts", + effect: "This attack's power is doubled when used on a target with paralysis. This also cures the target's paralysis, however." + }, + "followMe": { + name: "Follow Me", + effect: "The user draws attention to itself, making all targets take aim only at the user." + }, + "naturePower": { + name: "Nature Power", + effect: "This attack makes use of nature's power. Its effects vary depending on the user's environment." + }, + "charge": { + name: "Charge", + effect: "The user boosts the power of the Electric move it uses on the next turn. This also raises the user's Sp. Def stat." + }, + "taunt": { + name: "Taunt", + effect: "The target is taunted into a rage that allows it to use only attack moves for three turns." + }, + "helpingHand": { + name: "Helping Hand", + effect: "The user assists an ally by boosting the power of that ally's attack." + }, + "trick": { + name: "Trick", + effect: "The user catches the target off guard and swaps its held item with its own." + }, + "rolePlay": { + name: "Role Play", + effect: "The user mimics the target completely, copying the target's Ability." + }, + "wish": { + name: "Wish", + effect: "One turn after this move is used, the user's or its replacement's HP is restored by half the user's max HP." + }, + "assist": { + name: "Assist", + effect: "The user hurriedly and randomly uses a move among those known by ally Pokémon." + }, + "ingrain": { + name: "Ingrain", + effect: "The user lays roots that restore its HP on every turn. Because it's rooted, it can't switch out." + }, + "superpower": { + name: "Superpower", + effect: "The user attacks the target with great power. However, this also lowers the user's Attack and Defense stats." + }, + "magicCoat": { + name: "Magic Coat", + effect: "Moves like Leech Seed and moves that inflict status conditions are blocked by a barrier and reflected back to the user of those moves." + }, + "recycle": { + name: "Recycle", + effect: "The user recycles a held item that has been used in battle so it can be used again." + }, + "revenge": { + name: "Revenge", + effect: "This attack move's power is doubled if the user has been hurt by the opponent in the same turn." + }, + "brickBreak": { + name: "Brick Break", + effect: "The user attacks with a swift chop. It can also break barriers, such as Light Screen and Reflect." + }, + "yawn": { + name: "Yawn", + effect: "The user lets loose a huge yawn that lulls the target into falling asleep on the next turn." + }, + "knockOff": { + name: "Knock Off", + effect: "The user slaps down the target's held item, and that item can't be used in that battle. The move does more damage if the target has a held item." + }, + "endeavor": { + name: "Endeavor", + effect: "This attack move cuts down the target's HP to equal the user's HP." + }, + "eruption": { + name: "Eruption", + effect: "The user attacks opposing Pokémon with explosive fury. The lower the user's HP, the lower the move's power." + }, + "skillSwap": { + name: "Skill Swap", + effect: "The user employs its psychic power to exchange Abilities with the target." + }, + "imprison": { + name: "Imprison", + effect: "If opposing Pokémon know any move also known by the user, they are prevented from using it." + }, + "refresh": { + name: "Refresh", + effect: "The user rests to cure itself of poisoning, a burn, or paralysis." + }, + "grudge": { + name: "Grudge", + effect: "If the user faints, the user's grudge fully depletes the PP of the opponent's move that knocked it out." + }, + "snatch": { + name: "Snatch", + effect: "The user steals the effects of any attempts to use a healing or stat-changing move." + }, + "secretPower": { + name: "Secret Power", + effect: "The additional effects of this attack depend upon where it was used." + }, + "dive": { + name: "Dive", + effect: "Diving on the first turn, the user floats up and attacks on the next turn." + }, + "armThrust": { + name: "Arm Thrust", + effect: "The user lets loose a flurry of open-palmed arm thrusts that hit two to five times in a row." + }, + "camouflage": { + name: "Camouflage", + effect: "The user's type is changed depending on its environment, such as at water's edge, in grass, or in a cave." + }, + "tailGlow": { + name: "Tail Glow", + effect: "The user stares at flashing lights to focus its mind, drastically raising its Sp. Atk stat." + }, + "lusterPurge": { + name: "Luster Purge", + effect: "The user lets loose a damaging burst of light. This may also lower the target's Sp. Def stat." + }, + "mistBall": { + name: "Mist Ball", + effect: "A mist-like flurry of down envelops and damages the target. This may also lower the target's Sp. Atk stat." + }, + "featherDance": { + name: "Feather Dance", + effect: "The user covers the target's body with a mass of down that harshly lowers its Attack stat." + }, + "teeterDance": { + name: "Teeter Dance", + effect: "The user performs a wobbly dance that confuses the Pokémon around it." + }, + "blazeKick": { + name: "Blaze Kick", + effect: "The user launches a kick that lands a critical hit more easily. This may also leave the target with a burn." + }, + "mudSport": { + name: "Mud Sport", + effect: "The user kicks up mud on the battlefield. This weakens Electric-type moves for five turns." + }, + "iceBall": { + name: "Ice Ball", + effect: "The user attacks the target for five turns. The move's power increases each time it hits." + }, + "needleArm": { + name: "Needle Arm", + effect: "The user attacks by wildly swinging its thorny arms. This may also make the target flinch." + }, + "slackOff": { + name: "Slack Off", + effect: "The user slacks off, restoring its own HP by up to half of its max HP." + }, + "hyperVoice": { + name: "Hyper Voice", + effect: "The user lets loose a horribly echoing shout with the power to inflict damage." + }, + "poisonFang": { + name: "Poison Fang", + effect: "The user bites the target with toxic fangs. This may also leave the target badly poisoned." + }, + "crushClaw": { + name: "Crush Claw", + effect: "The user slashes the target with hard and sharp claws. This may also lower the target's Defense stat." + }, + "blastBurn": { + name: "Blast Burn", + effect: "The target is razed by a fiery explosion. The user can't move on the next turn." + }, + "hydroCannon": { + name: "Hydro Cannon", + effect: "The target is hit with a watery blast. The user can't move on the next turn." + }, + "meteorMash": { + name: "Meteor Mash", + effect: "The target is hit with a hard punch fired like a meteor. This may also raise the user's Attack stat." + }, + "astonish": { + name: "Astonish", + effect: "The user attacks the target while shouting in a startling fashion. This may also make the target flinch." + }, + "weatherBall": { + name: "Weather Ball", + effect: "This attack move varies in power and type depending on the weather." + }, + "aromatherapy": { + name: "Aromatherapy", + effect: "The user releases a soothing scent that heals all status conditions affecting the user's party." + }, + "fakeTears": { + name: "Fake Tears", + effect: "The user feigns crying to fluster the target, harshly lowering its Sp. Def stat." + }, + "airCutter": { + name: "Air Cutter", + effect: "The user launches razor-like wind to slash opposing Pokémon. Critical hits land more easily." + }, + "overheat": { + name: "Overheat", + effect: "The user attacks the target at full power. The attack's recoil harshly lowers the user's Sp. Atk stat." + }, + "odorSleuth": { + name: "Odor Sleuth", + effect: "Enables a Ghost-type target to be hit by Normal- and Fighting-type attacks. This also enables an evasive target to be hit." + }, + "rockTomb": { + name: "Rock Tomb", + effect: "Boulders are hurled at the target. This also lowers the target's Speed stat by preventing its movement." + }, + "silverWind": { + name: "Silver Wind", + effect: "The target is attacked with powdery scales blown by the wind. This may also raise all the user's stats." + }, + "metalSound": { + name: "Metal Sound", + effect: "A horrible sound like scraping metal harshly lowers the target's Sp. Def stat." + }, + "grassWhistle": { + name: "Grass Whistle", + effect: "The user plays a pleasant melody that lulls the target into a deep sleep." + }, + "tickle": { + name: "Tickle", + effect: "The user tickles the target into laughing, reducing its Attack and Defense stats." + }, + "cosmicPower": { + name: "Cosmic Power", + effect: "The user absorbs a mystical power from space to raise its Defense and Sp. Def stats." + }, + "waterSpout": { + name: "Water Spout", + effect: "The user spouts water to damage opposing Pokémon. The lower the user's HP, the lower the move's power." + }, + "signalBeam": { + name: "Signal Beam", + effect: "The user attacks with a sinister beam of light. This may also confuse the target." + }, + "shadowPunch": { + name: "Shadow Punch", + effect: "The user throws a punch from the shadows. This attack never misses." + }, + "extrasensory": { + name: "Extrasensory", + effect: "The user attacks with an odd, unseeable power. This may also make the target flinch." + }, + "skyUppercut": { + name: "Sky Uppercut", + effect: "The user attacks the target with an uppercut thrown skyward with force." + }, + "sandTomb": { + name: "Sand Tomb", + effect: "The user traps the target inside a harshly raging sandstorm for four to five turns." + }, + "sheerCold": { + name: "Sheer Cold", + effect: "The target faints instantly. It's less likely to hit the target if it's used by Pokémon other than Ice types." + }, + "muddyWater": { + name: "Muddy Water", + effect: "The user attacks by shooting muddy water at opposing Pokémon. This may also lower their accuracy." + }, + "bulletSeed": { + name: "Bullet Seed", + effect: "The user forcefully shoots seeds at the target two to five times in a row." + }, + "aerialAce": { + name: "Aerial Ace", + effect: "The user confounds the target with speed, then slashes. This attack never misses." + }, + "icicleSpear": { + name: "Icicle Spear", + effect: "The user launches sharp icicles at the target two to five times in a row." + }, + "ironDefense": { + name: "Iron Defense", + effect: "The user hardens its body's surface like iron, sharply raising its Defense stat." + }, + "block": { + name: "Block", + effect: "The user blocks the target's way with arms spread wide to prevent escape." + }, + "howl": { + name: "Howl", + effect: "The user howls loudly to raise the spirit of itself and allies. This raises their Attack stats." + }, + "dragonClaw": { + name: "Dragon Claw", + effect: "The user slashes the target with huge sharp claws." + }, + "frenzyPlant": { + name: "Frenzy Plant", + effect: "The user slams the target with the roots of an enormous tree. The user can't move on the next turn." + }, + "bulkUp": { + name: "Bulk Up", + effect: "The user tenses its muscles to bulk up its body, raising both its Attack and Defense stats." + }, + "bounce": { + name: "Bounce", + effect: "The user bounces up high, then drops on the target on the second turn. This may also leave the target with paralysis." + }, + "mudShot": { + name: "Mud Shot", + effect: "The user attacks by hurling a blob of mud at the target. This also lowers the target's Speed stat." + }, + "poisonTail": { + name: "Poison Tail", + effect: "The user hits the target with its tail. This may also poison the target. Critical hits land more easily." + }, + "covet": { + name: "Covet", + effect: "The user endearingly approaches the target, then has a 30% chance to steal the target's held item." + }, + "voltTackle": { + name: "Volt Tackle", + effect: "The user electrifies itself and charges the target. This also damages the user quite a lot. This attack may leave the target with paralysis." + }, + "magicalLeaf": { + name: "Magical Leaf", + effect: "The user scatters curious leaves that chase the target. This attack never misses." + }, + "waterSport": { + name: "Water Sport", + effect: "The user soaks the battlefield with water. This weakens Fire-type moves for five turns." + }, + "calmMind": { + name: "Calm Mind", + effect: "The user quietly focuses its mind and calms its spirit to raise its Sp. Atk and Sp. Def stats." + }, + "leafBlade": { + name: "Leaf Blade", + effect: "The user handles a sharp leaf like a sword and attacks by cutting its target. Critical hits land more easily." + }, + "dragonDance": { + name: "Dragon Dance", + effect: "The user vigorously performs a mystic, powerful dance that raises its Attack and Speed stats." + }, + "rockBlast": { + name: "Rock Blast", + effect: "The user hurls hard rocks at the target. Two to five rocks are launched in a row." + }, + "shockWave": { + name: "Shock Wave", + effect: "The user strikes the target with a quick jolt of electricity. This attack never misses." + }, + "waterPulse": { + name: "Water Pulse", + effect: "The user attacks the target with a pulsing blast of water. This may also confuse the target." + }, + "doomDesire": { + name: "Doom Desire", + effect: "Two turns after this move is used, a concentrated bundle of light blasts the target." + }, + "psychoBoost": { + name: "Psycho Boost", + effect: "The user attacks the target at full power. The attack's recoil harshly lowers the user's Sp. Atk stat." + }, + "roost": { + name: "Roost", + effect: "The user lands and rests its body. This move restores the user's HP by up to half of its max HP." + }, + "gravity": { + name: "Gravity", + effect: "This move enables Flying-type Pokémon or Pokémon with the Levitate Ability to be hit by Ground-type moves. Moves that involve flying can't be used." + }, + "miracleEye": { + name: "Miracle Eye", + effect: "Enables a Dark-type target to be hit by Psychic-type attacks. This also enables an evasive target to be hit." + }, + "wakeUpSlap": { + name: "Wake-Up Slap", + effect: "This attack inflicts big damage on a sleeping target. This also wakes the target up, however." + }, + "hammerArm": { + name: "Hammer Arm", + effect: "The user swings and hits with its strong, heavy fist. It lowers the user's Speed, however." + }, + "gyroBall": { + name: "Gyro Ball", + effect: "The user tackles the target with a high-speed spin. The slower the user compared to the target, the greater the move's power." + }, + "healingWish": { + name: "Healing Wish", + effect: "The user faints. In return, the Pokémon taking its place will have its HP restored and status conditions cured." + }, + "brine": { + name: "Brine", + effect: "If the target's HP is half or less, this attack will hit with double the power." + }, + "naturalGift": { + name: "Natural Gift", + effect: "The user draws power to attack by using its held Berry. The Berry determines the move's type and power." + }, + "feint": { + name: "Feint", + effect: "This attack hits a target using a move such as Protect or Detect. This also lifts the effects of those moves." + }, + "pluck": { + name: "Pluck", + effect: "The user pecks the target. If the target is holding a Berry, the user eats it and gains its effect." + }, + "tailwind": { + name: "Tailwind", + effect: "The user whips up a turbulent whirlwind that ups the Speed stats of the user and its allies for four turns." + }, + "acupressure": { + name: "Acupressure", + effect: "The user applies pressure to stress points, sharply boosting one of its or its allies' stats." + }, + "metalBurst": { + name: "Metal Burst", + effect: "The user retaliates with much greater force against the opponent that last inflicted damage on it." + }, + "uTurn": { + name: "U-turn", + effect: "After making its attack, the user rushes back to switch places with a party Pokémon in waiting." + }, + "closeCombat": { + name: "Close Combat", + effect: "The user fights the target up close without guarding itself. This also lowers the user's Defense and Sp. Def stats." + }, + "payback": { + name: "Payback", + effect: "The user stores power, then attacks. If the user moves after the target, this attack's power will be doubled." + }, + "assurance": { + name: "Assurance", + effect: "If the target has already taken some damage in the same turn, this attack's power is doubled." + }, + "embargo": { + name: "Embargo", + effect: "This move prevents the target from using its held item for five turns. Its Trainer is also prevented from using items on it." + }, + "fling": { + name: "Fling", + effect: "The user flings its held item at the target to attack. This move's power and effects depend on the item." + }, + "psychoShift": { + name: "Psycho Shift", + effect: "Using its psychic power of suggestion, the user transfers its status conditions to the target." + }, + "trumpCard": { + name: "Trump Card", + effect: "The fewer PP this move has, the greater its power." + }, + "healBlock": { + name: "Heal Block", + effect: "For five turns, the user prevents the opposing team from using any moves, Abilities, or held items that recover HP." + }, + "wringOut": { + name: "Wring Out", + effect: "The user powerfully wrings the target. The more HP the target has, the greater the move's power." + }, + "powerTrick": { + name: "Power Trick", + effect: "The user employs its psychic power to switch its Attack stat with its Defense stat." + }, + "gastroAcid": { + name: "Gastro Acid", + effect: "The user hurls up its stomach acids on the target. The fluid eliminates the effect of the target's Ability." + }, + "luckyChant": { + name: "Lucky Chant", + effect: "The user chants an incantation toward the sky, preventing opposing Pokémon from landing critical hits for five turns." + }, + "meFirst": { + name: "Me First", + effect: "The user cuts ahead of the target to copy and use the target's intended move with greater power. This move fails if it isn't used first." + }, + "copycat": { + name: "Copycat", + effect: "The user mimics the move used immediately before it. The move fails if no other move has been used yet." + }, + "powerSwap": { + name: "Power Swap", + effect: "The user employs its psychic power to switch changes to its Attack and Sp. Atk stats with the target." + }, + "guardSwap": { + name: "Guard Swap", + effect: "The user employs its psychic power to switch changes to its Defense and Sp. Def stats with the target." + }, + "punishment": { + name: "Punishment", + effect: "The more the target has powered up with stat changes, the greater the move's power." + }, + "lastResort": { + name: "Last Resort", + effect: "This move can be used only after the user has used all the other moves it knows in the battle." + }, + "worrySeed": { + name: "Worry Seed", + effect: "A seed that causes worry is planted on the target. It prevents sleep by making the target's Ability Insomnia." + }, + "suckerPunch": { + name: "Sucker Punch", + effect: "This move enables the user to attack first. This move fails if the target is not readying an attack." + }, + "toxicSpikes": { + name: "Toxic Spikes", + effect: "The user lays a trap of poison spikes at the feet of the opposing team. The spikes will poison opposing Pokémon that switch into battle." + }, + "heartSwap": { + name: "Heart Swap", + effect: "The user employs its psychic power to switch stat changes with the target." + }, + "aquaRing": { + name: "Aqua Ring", + effect: "The user envelops itself in a veil made of water. It regains some HP every turn." + }, + "magnetRise": { + name: "Magnet Rise", + effect: "The user levitates using electrically generated magnetism for five turns." + }, + "flareBlitz": { + name: "Flare Blitz", + effect: "The user cloaks itself in fire and charges the target. This also damages the user quite a lot. This attack may leave the target with a burn." + }, + "forcePalm": { + name: "Force Palm", + effect: "The target is attacked with a shock wave. This may also leave the target with paralysis." + }, + "auraSphere": { + name: "Aura Sphere", + effect: "The user lets loose a blast of aura power from deep within its body at the target. This attack never misses." + }, + "rockPolish": { + name: "Rock Polish", + effect: "The user polishes its body to reduce drag. This sharply raises the Speed stat." + }, + "poisonJab": { + name: "Poison Jab", + effect: "The target is stabbed with a tentacle, arm, or the like steeped in poison. This may also poison the target." + }, + "darkPulse": { + name: "Dark Pulse", + effect: "The user releases a horrible aura imbued with dark thoughts. This may also make the target flinch." + }, + "nightSlash": { + name: "Night Slash", + effect: "The user slashes the target the instant an opportunity arises. Critical hits land more easily." + }, + "aquaTail": { + name: "Aqua Tail", + effect: "The user attacks by swinging its tail as if it were a vicious wave in a raging storm." + }, + "seedBomb": { + name: "Seed Bomb", + effect: "The user slams a barrage of hard-shelled seeds down on the target from above." + }, + "airSlash": { + name: "Air Slash", + effect: "The user attacks with a blade of air that slices even the sky. This may also make the target flinch." + }, + "xScissor": { + name: "X-Scissor", + effect: "The user slashes at the target by crossing its scythes or claws as if they were a pair of scissors." + }, + "bugBuzz": { + name: "Bug Buzz", + effect: "The user generates a damaging sound wave by vibration. This may also lower the target's Sp. Def stat." + }, + "dragonPulse": { + name: "Dragon Pulse", + effect: "The target is attacked with a shock wave generated by the user's gaping mouth." + }, + "dragonRush": { + name: "Dragon Rush", + effect: "The user tackles the target while exhibiting overwhelming menace. This may also make the target flinch." + }, + "powerGem": { + name: "Power Gem", + effect: "The user attacks with a ray of light that sparkles as if it were made of gemstones." + }, + "drainPunch": { + name: "Drain Punch", + effect: "An energy-draining punch. The user's HP is restored by half the damage taken by the target." + }, + "vacuumWave": { + name: "Vacuum Wave", + effect: "The user whirls its fists to send a wave of pure vacuum at the target. This move always goes first." + }, + "focusBlast": { + name: "Focus Blast", + effect: "The user heightens its mental focus and unleashes its power. This may also lower the target's Sp. Def stat." + }, + "energyBall": { + name: "Energy Ball", + effect: "The user draws power from nature and fires it at the target. This may also lower the target's Sp. Def stat." + }, + "braveBird": { + name: "Brave Bird", + effect: "The user tucks in its wings and charges from a low altitude. This also damages the user quite a lot." + }, + "earthPower": { + name: "Earth Power", + effect: "The user makes the ground under the target erupt with power. This may also lower the target's Sp. Def stat." + }, + "switcheroo": { + name: "Switcheroo", + effect: "The user trades held items with the target faster than the eye can follow." + }, + "gigaImpact": { + name: "Giga Impact", + effect: "The user charges at the target using every bit of its power. The user can't move on the next turn." + }, + "nastyPlot": { + name: "Nasty Plot", + effect: "The user stimulates its brain by thinking bad thoughts. This sharply raises the user's Sp. Atk stat." + }, + "bulletPunch": { + name: "Bullet Punch", + effect: "The user strikes the target with tough punches as fast as bullets. This move always goes first." + }, + "avalanche": { + name: "Avalanche", + effect: "The power of this attack move is doubled if the user has been hurt by the target in the same turn." + }, + "iceShard": { + name: "Ice Shard", + effect: "The user flash-freezes chunks of ice and hurls them at the target. This move always goes first." + }, + "shadowClaw": { + name: "Shadow Claw", + effect: "The user slashes with a sharp claw made from shadows. Critical hits land more easily." + }, + "thunderFang": { + name: "Thunder Fang", + effect: "The user bites with electrified fangs. This may also make the target flinch or leave it with paralysis." + }, + "iceFang": { + name: "Ice Fang", + effect: "The user bites with cold-infused fangs. This may also make the target flinch or leave it frozen." + }, + "fireFang": { + name: "Fire Fang", + effect: "The user bites with flame-cloaked fangs. This may also make the target flinch or leave it with a burn." + }, + "shadowSneak": { + name: "Shadow Sneak", + effect: "The user extends its shadow and attacks the target from behind. This move always goes first." + }, + "mudBomb": { + name: "Mud Bomb", + effect: "The user launches a hard-packed mud ball to attack. This may also lower the target's accuracy." + }, + "psychoCut": { + name: "Psycho Cut", + effect: "The user tears at the target with blades formed by psychic power. Critical hits land more easily." + }, + "zenHeadbutt": { + name: "Zen Headbutt", + effect: "The user focuses its willpower to its head and attacks the target. This may also make the target flinch." + }, + "mirrorShot": { + name: "Mirror Shot", + effect: "The user lets loose a flash of energy at the target from its polished body. This may also lower the target's accuracy." + }, + "flashCannon": { + name: "Flash Cannon", + effect: "The user gathers all its light energy and releases it all at once. This may also lower the target's Sp. Def stat." + }, + "rockClimb": { + name: "Rock Climb", + effect: "The user attacks the target by smashing into it with incredible force. This may also confuse the target." + }, + "defog": { + name: "Defog", + effect: "A strong wind blows away the target's barriers such as Reflect or Light Screen. This also lowers the target's evasiveness." + }, + "trickRoom": { + name: "Trick Room", + effect: "The user creates a bizarre area in which slower Pokémon get to move first for five turns." + }, + "dracoMeteor": { + name: "Draco Meteor", + effect: "Comets are summoned down from the sky onto the target. The attack's recoil harshly lowers the user's Sp. Atk stat." + }, + "discharge": { + name: "Discharge", + effect: "The user strikes everything around it by letting loose a flare of electricity. This may also cause paralysis." + }, + "lavaPlume": { + name: "Lava Plume", + effect: "The user torches everything around it in an inferno of scarlet flames. This may also leave those it hits with a burn." + }, + "leafStorm": { + name: "Leaf Storm", + effect: "The user whips up a storm of leaves around the target. The attack's recoil harshly lowers the user's Sp. Atk stat." + }, + "powerWhip": { + name: "Power Whip", + effect: "The user violently whirls its vines, tentacles, or the like to harshly lash the target." + }, + "rockWrecker": { + name: "Rock Wrecker", + effect: "The user launches a huge boulder at the target to attack. The user can't move on the next turn." + }, + "crossPoison": { + name: "Cross Poison", + effect: "A slashing attack with a poisonous blade that may also poison the target. Critical hits land more easily." + }, + "gunkShot": { + name: "Gunk Shot", + effect: "The user shoots filthy garbage at the target to attack. This may also poison the target." + }, + "ironHead": { + name: "Iron Head", + effect: "The user slams the target with its steel-hard head. This may also make the target flinch." + }, + "magnetBomb": { + name: "Magnet Bomb", + effect: "The user launches steel bombs that stick to the target. This attack never misses." + }, + "stoneEdge": { + name: "Stone Edge", + effect: "The user stabs the target from below with sharpened stones. Critical hits land more easily." + }, + "captivate": { + name: "Captivate", + effect: "If any opposing Pokémon is the opposite gender of the user, it is charmed, which harshly lowers its Sp. Atk stat." + }, + "stealthRock": { + name: "Stealth Rock", + effect: "The user lays a trap of levitating stones around the opposing team. The trap hurts opposing Pokémon that switch into battle." + }, + "grassKnot": { + name: "Grass Knot", + effect: "The user snares the target with grass and trips it. The heavier the target, the greater the move's power." + }, + "chatter": { + name: "Chatter", + effect: "The user attacks the target with sound waves of deafening chatter. This confuses the target." + }, + "judgment": { + name: "Judgment", + effect: "The user releases countless shots of light at the target. This move's type varies depending on the kind of Plate the user is holding." + }, + "bugBite": { + name: "Bug Bite", + effect: "The user bites the target. If the target is holding a Berry, the user eats it and gains its effect." + }, + "chargeBeam": { + name: "Charge Beam", + effect: "The user attacks the target with an electric charge. The user may use any remaining electricity to raise its Sp. Atk stat." + }, + "woodHammer": { + name: "Wood Hammer", + effect: "The user slams its rugged body into the target to attack. This also damages the user quite a lot." + }, + "aquaJet": { + name: "Aqua Jet", + effect: "The user lunges at the target at a speed that makes it almost invisible. This move always goes first." + }, + "attackOrder": { + name: "Attack Order", + effect: "The user calls out its underlings to pummel the target. Critical hits land more easily." + }, + "defendOrder": { + name: "Defend Order", + effect: "The user calls out its underlings to shield its body, raising its Defense and Sp. Def stats." + }, + "healOrder": { + name: "Heal Order", + effect: "The user calls out its underlings to heal it. The user regains up to half of its max HP." + }, + "headSmash": { + name: "Head Smash", + effect: "The user attacks the target with a hazardous, full-power headbutt. This also damages the user terribly." + }, + "doubleHit": { + name: "Double Hit", + effect: "The user slams the target with a long tail, vines, or a tentacle. The target is hit twice in a row." + }, + "roarOfTime": { + name: "Roar of Time", + effect: "The user blasts the target with power that distorts even time. The user can't move on the next turn." + }, + "spacialRend": { + name: "Spacial Rend", + effect: "The user tears the target along with the space around it. Critical hits land more easily." + }, + "lunarDance": { + name: "Lunar Dance", + effect: "The user faints. In return, the Pokémon taking its place will have its status and HP fully restored." + }, + "crushGrip": { + name: "Crush Grip", + effect: "The target is crushed with great force. The more HP the target has left, the greater this move's power." + }, + "magmaStorm": { + name: "Magma Storm", + effect: "The target becomes trapped within a maelstrom of fire that rages for four to five turns." + }, + "darkVoid": { + name: "Dark Void", + effect: "Opposing Pokémon are dragged into a world of total darkness that makes them sleep." + }, + "seedFlare": { + name: "Seed Flare", + effect: "The user emits a shock wave from its body to attack its target. This may also harshly lower the target's Sp. Def stat." + }, + "ominousWind": { + name: "Ominous Wind", + effect: "The user blasts the target with a gust of repulsive wind. This may also raise all the user's stats at once." + }, + "shadowForce": { + name: "Shadow Force", + effect: "The user disappears, then strikes the target on the next turn. This move hits even if the target protects itself." + }, + "honeClaws": { + name: "Hone Claws", + effect: "The user sharpens its claws to boost its Attack stat and accuracy." + }, + "wideGuard": { + name: "Wide Guard", + effect: "The user and its allies are protected from wide-ranging attacks for one turn." + }, + "guardSplit": { + name: "Guard Split", + effect: "The user employs its psychic power to average its Defense and Sp. Def stats with those of the target." + }, + "powerSplit": { + name: "Power Split", + effect: "The user employs its psychic power to average its Attack and Sp. Atk stats with those of the target." + }, + "wonderRoom": { + name: "Wonder Room", + effect: "The user creates a bizarre area in which Pokémon's Defense and Sp. Def stats are swapped for five turns." + }, + "psyshock": { + name: "Psyshock", + effect: "The user materializes an odd psychic wave to attack the target. This attack does physical damage." + }, + "venoshock": { + name: "Venoshock", + effect: "The user drenches the target in a special poisonous liquid. This move's power is doubled if the target is poisoned." + }, + "autotomize": { + name: "Autotomize", + effect: "The user sheds part of its body to make itself lighter and sharply raise its Speed stat." + }, + "ragePowder": { + name: "Rage Powder", + effect: "The user scatters a cloud of irritating powder to draw attention to itself. Opposing Pokémon aim only at the user." + }, + "telekinesis": { + name: "Telekinesis", + effect: "The user makes the target float with its psychic power. The target is easier to hit for three turns." + }, + "magicRoom": { + name: "Magic Room", + effect: "The user creates a bizarre area in which Pokémon's held items lose their effects for five turns." + }, + "smackDown": { + name: "Smack Down", + effect: "The user throws a stone or similar projectile to attack the target. A flying Pokémon will fall to the ground when it's hit." + }, + "stormThrow": { + name: "Storm Throw", + effect: "The user strikes the target with a fierce blow. This attack always results in a critical hit." + }, + "flameBurst": { + name: "Flame Burst", + effect: "The user attacks the target with a bursting flame. The bursting flame damages Pokémon next to the target as well." + }, + "sludgeWave": { + name: "Sludge Wave", + effect: "The user strikes everything around it by swamping the area with a giant sludge wave. This may also poison those hit." + }, + "quiverDance": { + name: "Quiver Dance", + effect: "The user lightly performs a beautiful, mystic dance. This boosts the user's Sp. Atk, Sp. Def, and Speed stats." + }, + "heavySlam": { + name: "Heavy Slam", + effect: "The user slams into the target with its heavy body. The more the user outweighs the target, the greater the move's power." + }, + "synchronoise": { + name: "Synchronoise", + effect: "Using an odd shock wave, the user inflicts damage on any Pokémon of the same type in the area around it." + }, + "electroBall": { + name: "Electro Ball", + effect: "The user hurls an electric orb at the target. The faster the user is than the target, the greater the move's power." + }, + "soak": { + name: "Soak", + effect: "The user shoots a torrent of water at the target and changes the target's type to Water." + }, + "flameCharge": { + name: "Flame Charge", + effect: "Cloaking itself in flame, the user attacks the target. Then, building up more power, the user raises its Speed stat." + }, + "coil": { + name: "Coil", + effect: "The user coils up and concentrates. This raises its Attack and Defense stats as well as its accuracy." + }, + "lowSweep": { + name: "Low Sweep", + effect: "The user makes a swift attack on the target's legs, which lowers the target's Speed stat." + }, + "acidSpray": { + name: "Acid Spray", + effect: "The user spits fluid that works to melt the target. This harshly lowers the target's Sp. Def stat." + }, + "foulPlay": { + name: "Foul Play", + effect: "The user turns the target's power against it. The higher the target's Attack stat, the greater the damage it deals." + }, + "simpleBeam": { + name: "Simple Beam", + effect: "The user's mysterious psychic wave changes the target's Ability to Simple." + }, + "entrainment": { + name: "Entrainment", + effect: "The user dances with an odd rhythm that compels the target to mimic it, making the target's Ability the same as the user's." + }, + "afterYou": { + name: "After You", + effect: "The user helps the target and makes it use its move right after the user." + }, + "round": { + name: "Round", + effect: "The user attacks the target with a song. Others can join in the Round to increase the power of the attack." + }, + "echoedVoice": { + name: "Echoed Voice", + effect: "The user attacks the target with an echoing voice. If this move is used every turn, its power is increased." + }, + "chipAway": { + name: "Chip Away", + effect: "Looking for an opening, the user strikes consistently. The target's stat changes don't affect this attack's damage." + }, + "clearSmog": { + name: "Clear Smog", + effect: "The user attacks the target by throwing a clump of special mud. All stat changes are returned to normal." + }, + "storedPower": { + name: "Stored Power", + effect: "The user attacks the target with stored power. The more the user's stats are raised, the greater the move's power." + }, + "quickGuard": { + name: "Quick Guard", + effect: "The user protects itself and its allies from priority moves." + }, + "allySwitch": { + name: "Ally Switch", + effect: "The user teleports using a strange power and switches places with one of its allies." + }, + "scald": { + name: "Scald", + effect: "The user shoots boiling hot water at its target. This may also leave the target with a burn." + }, + "shellSmash": { + name: "Shell Smash", + effect: "The user breaks its shell, which lowers Defense and Sp. Def stats but sharply raises its Attack, Sp. Atk, and Speed stats." + }, + "healPulse": { + name: "Heal Pulse", + effect: "The user emits a healing pulse that restores the target's HP by up to half of its max HP." + }, + "hex": { + name: "Hex", + effect: "This relentless attack does massive damage to a target affected by status conditions." + }, + "skyDrop": { + name: "Sky Drop", + effect: "The user takes the target into the sky, then drops it during the next turn. The target cannot attack while in the sky." + }, + "shiftGear": { + name: "Shift Gear", + effect: "The user rotates its gears, raising its Attack stat and sharply raising its Speed stat." + }, + "circleThrow": { + name: "Circle Throw", + effect: "The target is thrown, and a different Pokémon is dragged out. In the wild, this ends a battle against a single Pokémon." + }, + "incinerate": { + name: "Incinerate", + effect: "The user attacks opposing Pokémon with fire. If a Pokémon is holding a certain item, such as a Berry, the item becomes burned up and unusable." + }, + "quash": { + name: "Quash", + effect: "The user suppresses the target and makes its move go last." + }, + "acrobatics": { + name: "Acrobatics", + effect: "The user nimbly strikes the target. The fewer held items, the higher the damage it inflicts." + }, + "reflectType": { + name: "Reflect Type", + effect: "The user reflects the target's type, making the user the same type as the target." + }, + "retaliate": { + name: "Retaliate", + effect: "The user gets revenge for a fainted ally. If an ally fainted in the previous turn, this move's power is increased." + }, + "finalGambit": { + name: "Final Gambit", + effect: "The user risks everything to attack its target. The user faints but does damage equal to its HP." + }, + "bestow": { + name: "Bestow", + effect: "The user passes its held item to the target when the target isn't holding an item." + }, + "inferno": { + name: "Inferno", + effect: "The user attacks by engulfing the target in an intense fire. This leaves the target with a burn." + }, + "waterPledge": { + name: "Water Pledge", + effect: "A column of water hits the target. When used with its fire equivalent, its power increases and a rainbow appears." + }, + "firePledge": { + name: "Fire Pledge", + effect: "A column of fire hits the target. When used with its grass equivalent, its power increases and a vast sea of fire appears." + }, + "grassPledge": { + name: "Grass Pledge", + effect: "A column of grass hits the target. When used with its water equivalent, its power increases and a vast swamp appears." + }, + "voltSwitch": { + name: "Volt Switch", + effect: "After making its attack, the user rushes back to switch places with a party Pokémon in waiting." + }, + "struggleBug": { + name: "Struggle Bug", + effect: "While resisting, the user attacks opposing Pokémon. This lowers the Sp. Atk stats of those hit." + }, + "bulldoze": { + name: "Bulldoze", + effect: "The user strikes everything around it by stomping down on the ground. This lowers the Speed stats of those hit." + }, + "frostBreath": { + name: "Frost Breath", + effect: "The user blows its cold breath on the target. This attack always results in a critical hit." + }, + "dragonTail": { + name: "Dragon Tail", + effect: "The target is knocked away, and a different Pokémon is dragged out. In the wild, this ends a battle against a single Pokémon." + }, + "workUp": { + name: "Work Up", + effect: "The user is roused, and its Attack and Sp. Atk stats increase." + }, + "electroweb": { + name: "Electroweb", + effect: "The user attacks and captures opposing Pokémon using an electric net. This lowers their Speed stats." + }, + "wildCharge": { + name: "Wild Charge", + effect: "The user shrouds itself in electricity and smashes into its target. This also damages the user a little." + }, + "drillRun": { + name: "Drill Run", + effect: "The user crashes into its target while rotating its body like a drill. Critical hits land more easily." + }, + "dualChop": { + name: "Dual Chop", + effect: "The user attacks its target by hitting it with brutal strikes. The target is hit twice in a row." + }, + "heartStamp": { + name: "Heart Stamp", + effect: "The user unleashes a vicious blow after its cute act makes the target less wary. This may also make the target flinch." + }, + "hornLeech": { + name: "Horn Leech", + effect: "The user drains the target's energy with its horns. The user's HP is restored by half the damage taken by the target." + }, + "sacredSword": { + name: "Sacred Sword", + effect: "The user attacks by slicing with a long horn. The target's stat changes don't affect this attack's damage." + }, + "razorShell": { + name: "Razor Shell", + effect: "The user cuts its target with sharp shells. This may also lower the target's Defense stat." + }, + "heatCrash": { + name: "Heat Crash", + effect: "The user slams its target with its flame-covered body. The more the user outweighs the target, the greater the move's power." + }, + "leafTornado": { + name: "Leaf Tornado", + effect: "The user attacks its target by encircling it in sharp leaves. This attack may also lower the target's accuracy." + }, + "steamroller": { + name: "Steamroller", + effect: "The user crushes its target by rolling over the target with its rolled-up body. This may also make the target flinch." + }, + "cottonGuard": { + name: "Cotton Guard", + effect: "The user protects itself by wrapping its body in soft cotton, which drastically raises the user's Defense stat." + }, + "nightDaze": { + name: "Night Daze", + effect: "The user lets loose a pitch-black shock wave at its target. This may also lower the target's accuracy." + }, + "psystrike": { + name: "Psystrike", + effect: "The user materializes an odd psychic wave to attack the target. This attack does physical damage." + }, + "tailSlap": { + name: "Tail Slap", + effect: "The user attacks by striking the target with its hard tail. It hits the target two to five times in a row." + }, + "hurricane": { + name: "Hurricane", + effect: "The user attacks by wrapping its opponent in a fierce wind that flies up into the sky. This may also confuse the target." + }, + "headCharge": { + name: "Head Charge", + effect: "The user charges its head into its target, using its powerful guard hair. This also damages the user a little." + }, + "gearGrind": { + name: "Gear Grind", + effect: "The user attacks by throwing steel gears at its target twice." + }, + "searingShot": { + name: "Searing Shot", + effect: "The user torches everything around it in an inferno of scarlet flames. This may also leave those it hits with a burn." + }, + "technoBlast": { + name: "Techno Blast", + effect: "The user fires a beam of light at its target. The move's type changes depending on the Drive the user holds." + }, + "relicSong": { + name: "Relic Song", + effect: "The user sings an ancient song and attacks by appealing to the hearts of the listening opposing Pokémon. This may also induce sleep." + }, + "secretSword": { + name: "Secret Sword", + effect: "The user cuts with its long horn. The odd power contained in the horn does physical damage to the target." + }, + "glaciate": { + name: "Glaciate", + effect: "The user attacks by blowing freezing cold air at opposing Pokémon. This lowers their Speed stats." + }, + "boltStrike": { + name: "Bolt Strike", + effect: "The user surrounds itself with a great amount of electricity and charges its target. This may also leave the target with paralysis." + }, + "blueFlare": { + name: "Blue Flare", + effect: "The user attacks by engulfing the target in an intense, yet beautiful, blue flame. This may also leave the target with a burn." + }, + "fieryDance": { + name: "Fiery Dance", + effect: "Cloaked in flames, the user attacks the target by dancing and flapping its wings. This may also raise the user's Sp. Atk stat." + }, + "freezeShock": { + name: "Freeze Shock", + effect: "On the second turn, the user hits the target with electrically charged ice. This may also leave the target with paralysis." + }, + "iceBurn": { + name: "Ice Burn", + effect: "On the second turn, an ultracold, freezing wind surrounds the target. This may leave the target with a burn." + }, + "snarl": { + name: "Snarl", + effect: "The user yells as if it's ranting about something, which lowers the Sp. Atk stats of opposing Pokémon." + }, + "icicleCrash": { + name: "Icicle Crash", + effect: "The user attacks by harshly dropping large icicles onto the target. This may also make the target flinch." + }, + "vCreate": { + name: "V-create", + effect: "With a hot flame on its forehead, the user hurls itself at its target. This lowers the user's Defense, Sp. Def, and Speed stats." + }, + "fusionFlare": { + name: "Fusion Flare", + effect: "The user brings down a giant flame. This move's power is increased when influenced by an enormous lightning bolt." + }, + "fusionBolt": { + name: "Fusion Bolt", + effect: "The user throws down a giant lightning bolt. This move's power is increased when influenced by an enormous flame." + }, + "flyingPress": { + name: "Flying Press", + effect: "The user dives down onto the target from the sky. This move is Fighting and Flying type simultaneously." + }, + "matBlock": { + name: "Mat Block", + effect: "Using a pulled-up mat as a shield, the user protects itself and its allies from damaging moves. This does not stop status moves." + }, + "belch": { + name: "Belch", + effect: "The user lets out a damaging belch at the target. The user must eat a held Berry to use this move." + }, + "rototiller": { + name: "Rototiller", + effect: "Tilling the soil, the user makes it easier for plants to grow. This raises the Attack and Sp. Atk stats of Grass-type Pokémon." + }, + "stickyWeb": { + name: "Sticky Web", + effect: "The user weaves a sticky net around the opposing team, which lowers their Speed stats upon switching into battle." + }, + "fellStinger": { + name: "Fell Stinger", + effect: "When the user knocks out a target with this move, the user's Attack stat rises drastically." + }, + "phantomForce": { + name: "Phantom Force", + effect: "The user vanishes somewhere, then strikes the target on the next turn. This move hits even if the target protects itself." + }, + "trickOrTreat": { + name: "Trick-or-Treat", + effect: "The user takes the target trick-or-treating. This adds Ghost type to the target's type." + }, + "nobleRoar": { + name: "Noble Roar", + effect: "Letting out a noble roar, the user intimidates the target and lowers its Attack and Sp. Atk stats." + }, + "ionDeluge": { + name: "Ion Deluge", + effect: "The user disperses electrically charged particles, which changes Normal-type moves to Electric-type moves." + }, + "parabolicCharge": { + name: "Parabolic Charge", + effect: "The user attacks everything around it. The user's HP is restored by half the damage taken by those hit." + }, + "forestsCurse": { + name: "Forest's Curse", + effect: "The user puts a forest curse on the target. The target is now Grass type as well." + }, + "petalBlizzard": { + name: "Petal Blizzard", + effect: "The user stirs up a violent petal blizzard and attacks everything around it." + }, + "freezeDry": { + name: "Freeze-Dry", + effect: "The user rapidly cools the target. This may also leave the target frozen. This move is super effective on Water types." + }, + "disarmingVoice": { + name: "Disarming Voice", + effect: "Letting out a charming cry, the user does emotional damage to opposing Pokémon. This attack never misses." + }, + "partingShot": { + name: "Parting Shot", + effect: "With a parting threat, the user lowers the target's Attack and Sp. Atk stats. Then it switches with a party Pokémon." + }, + "topsyTurvy": { + name: "Topsy-Turvy", + effect: "All stat changes affecting the target turn topsy-turvy and become the opposite of what they were." + }, + "drainingKiss": { + name: "Draining Kiss", + effect: "The user steals the target's HP with a kiss. The user's HP is restored by over half of the damage taken by the target." + }, + "craftyShield": { + name: "Crafty Shield", + effect: "The user protects itself and its allies from status moves with a mysterious power. This does not stop moves that do damage." + }, + "flowerShield": { + name: "Flower Shield", + effect: "The user raises the Defense stats of all Grass-type Pokémon in battle with a mysterious power." + }, + "grassyTerrain": { + name: "Grassy Terrain", + effect: "The user turns the ground to grass for five turns. This restores the HP of Pokémon on the ground a little every turn and powers up Grass-type moves." + }, + "mistyTerrain": { + name: "Misty Terrain", + effect: "This protects Pokémon on the ground from status conditions and halves damage from Dragon-type moves for five turns." + }, + "electrify": { + name: "Electrify", + effect: "If the target is electrified before it uses a move during that turn, the target's move becomes Electric type." + }, + "playRough": { + name: "Play Rough", + effect: "The user plays rough with the target and attacks it. This may also lower the target's Attack stat." + }, + "fairyWind": { + name: "Fairy Wind", + effect: "The user stirs up a fairy wind and strikes the target with it." + }, + "moonblast": { + name: "Moonblast", + effect: "Borrowing the power of the moon, the user attacks the target. This may also lower the target's Sp. Atk stat." + }, + "boomburst": { + name: "Boomburst", + effect: "The user attacks everything around it with the destructive power of a terrible, explosive sound." + }, + "fairyLock": { + name: "Fairy Lock", + effect: "By locking down the battlefield, the user keeps all Pokémon from fleeing during the next turn." + }, + "kingsShield": { + name: "King's Shield", + effect: "The user takes a defensive stance while it protects itself from damage. It also lowers the Attack stat of any attacker that makes direct contact." + }, + "playNice": { + name: "Play Nice", + effect: "The user and the target become friends, and the target loses its will to fight. This lowers the target's Attack stat." + }, + "confide": { + name: "Confide", + effect: "The user tells the target a secret, and the target loses its ability to concentrate. This lowers the target's Sp. Atk stat." + }, + "diamondStorm": { + name: "Diamond Storm", + effect: "The user whips up a storm of diamonds to damage opposing Pokémon. This may also sharply raise the user's Defense stat." + }, + "steamEruption": { + name: "Steam Eruption", + effect: "The user immerses the target in superheated steam. This may also leave the target with a burn." + }, + "hyperspaceHole": { + name: "Hyperspace Hole", + effect: "Using a hyperspace hole, the user appears right next to the target and strikes. This also hits a target using a move such as Protect or Detect." + }, + "waterShuriken": { + name: "Water Shuriken", + effect: "The user hits the target with throwing stars two to five times in a row. This move always goes first." + }, + "mysticalFire": { + name: "Mystical Fire", + effect: "The user attacks by breathing a special, hot fire. This also lowers the target's Sp. Atk stat." + }, + "spikyShield": { + name: "Spiky Shield", + effect: "In addition to protecting the user from attacks, this move also damages any attacker that makes direct contact." + }, + "aromaticMist": { + name: "Aromatic Mist", + effect: "The user raises the Sp. Def stat of an ally Pokémon by using a mysterious aroma." + }, + "eerieImpulse": { + name: "Eerie Impulse", + effect: "The user's body generates an eerie impulse. Exposing the target to it harshly lowers the target's Sp. Atk stat." + }, + "venomDrench": { + name: "Venom Drench", + effect: "Opposing Pokémon are drenched in an odd poisonous liquid. This lowers the Attack, Sp. Atk, and Speed stats of a poisoned target." + }, + "powder": { + name: "Powder", + effect: "The user covers the target in a combustible powder. If the target uses a Fire-type move, the powder explodes and damages the target." + }, + "geomancy": { + name: "Geomancy", + effect: "The user absorbs energy and sharply raises its Sp. Atk, Sp. Def, and Speed stats on the next turn." + }, + "magneticFlux": { + name: "Magnetic Flux", + effect: "The user manipulates magnetic fields, which raises the Defense and Sp. Def stats of ally Pokémon with the Plus or Minus Ability." + }, + "happyHour": { + name: "Happy Hour", + effect: "Using Happy Hour doubles the amount of prize money received after battle." + }, + "electricTerrain": { + name: "Electric Terrain", + effect: "The user electrifies the ground for five turns, powering up Electric-type moves. Pokémon on the ground no longer fall asleep." + }, + "dazzlingGleam": { + name: "Dazzling Gleam", + effect: "The user damages opposing Pokémon by emitting a powerful flash." + }, + "celebrate": { + name: "Celebrate", + effect: "The Pokémon congratulates you on your special day!" + }, + "holdHands": { + name: "Hold Hands", + effect: "The user and an ally hold hands. This makes them very happy." + }, + "babyDollEyes": { + name: "Baby-Doll Eyes", + effect: "The user stares at the target with its baby-doll eyes, which lowers the target's Attack stat. This move always goes first." + }, + "nuzzle": { + name: "Nuzzle", + effect: "The user attacks by nuzzling its electrified cheeks against the target. This also leaves the target with paralysis." + }, + "holdBack": { + name: "Hold Back", + effect: "The user holds back when it attacks, and the target is left with at least 1 HP." + }, + "infestation": { + name: "Infestation", + effect: "The target is infested and attacked for four to five turns. The target can't flee during this time." + }, + "powerUpPunch": { + name: "Power-Up Punch", + effect: "Striking opponents over and over makes the user's fists harder. Hitting a target raises the Attack stat." + }, + "oblivionWing": { + name: "Oblivion Wing", + effect: "The user absorbs its target's HP. The user's HP is restored by over half of the damage taken by the target." + }, + "thousandArrows": { + name: "Thousand Arrows", + effect: "This move also hits opposing Pokémon that are in the air. Those Pokémon are knocked down to the ground." + }, + "thousandWaves": { + name: "Thousand Waves", + effect: "The user attacks with a wave that crawls along the ground. Those it hits can't flee from battle." + }, + "landsWrath": { + name: "Land's Wrath", + effect: "The user gathers the energy of the land and focuses that power on opposing Pokémon to damage them." + }, + "lightOfRuin": { + name: "Light of Ruin", + effect: "Drawing power from the Eternal Flower, the user fires a powerful beam of light. This also damages the user quite a lot." + }, + "originPulse": { + name: "Origin Pulse", + effect: "The user attacks opposing Pokémon with countless beams of light that glow a deep and brilliant blue." + }, + "precipiceBlades": { + name: "Precipice Blades", + effect: "The user attacks opposing Pokémon by manifesting the power of the land in fearsome blades of stone." + }, + "dragonAscent": { + name: "Dragon Ascent", + effect: "After soaring upward, the user attacks its target by dropping out of the sky at high speeds. But it lowers its own Defense and Sp. Def stats in the process." + }, + "hyperspaceFury": { + name: "Hyperspace Fury", + effect: "Using its many arms, the user unleashes a barrage of attacks that ignore the effects of moves like Protect and Detect. But the user's Defense stat falls." + }, + "breakneckBlitzPhysical": { + name: "Breakneck Blitz", + effect: "The user builds up its momentum using its Z-Power and crashes into the target at full speed. The power varies, depending on the original move." + }, + "breakneckBlitzSpecial": { + name: "Breakneck Blitz", + effect: "Dummy Data" + }, + "allOutPummelingPhysical": { + name: "All-Out Pummeling", + effect: "The user rams an energy orb created by its Z-Power into the target with full force. The power varies, depending on the original move." + }, + "allOutPummelingSpecial": { + name: "All-Out Pummeling", + effect: "Dummy Data" + }, + "supersonicSkystrikePhysical": { + name: "Supersonic Skystrike", + effect: "The user soars up with its Z-Power and plummets toward the target at full speed. The power varies, depending on the original move." + }, + "supersonicSkystrikeSpecial": { + name: "Supersonic Skystrike", + effect: "Dummy Data" + }, + "acidDownpourPhysical": { + name: "Acid Downpour", + effect: "The user creates a poisonous swamp using its Z-Power and sinks the target into it at full force. The power varies, depending on the original move." + }, + "acidDownpourSpecial": { + name: "Acid Downpour", + effect: "Dummy Data" + }, + "tectonicRagePhysical": { + name: "Tectonic Rage", + effect: "The user burrows deep into the ground and slams into the target with the full force of its Z-Power. The power varies, depending on the original move." + }, + "tectonicRageSpecial": { + name: "Tectonic Rage", + effect: "Dummy Data" + }, + "continentalCrushPhysical": { + name: "Continental Crush", + effect: "The user summons a huge rock mountain using its Z-Power and drops it onto the target with full force. The power varies, depending on the original move." + }, + "continentalCrushSpecial": { + name: "Continental Crush", + effect: "Dummy Data" + }, + "savageSpinOutPhysical": { + name: "Savage Spin-Out", + effect: "The user binds the target with full force with threads of silk that the user spits using its Z-Power. The power varies, depending on the original move." + }, + "savageSpinOutSpecial": { + name: "Savage Spin-Out", + effect: "Dummy Data" + }, + "neverEndingNightmarePhysical": { + name: "Never-Ending Nightmare", + effect: "Deep-seated grudges summoned by the user's Z-Power trap the target. The power varies, depending on the original move." + }, + "neverEndingNightmareSpecial": { + name: "Never-Ending Nightmare", + effect: "Dummy Data" + }, + "corkscrewCrashPhysical": { + name: "Corkscrew Crash", + effect: "The user spins very fast and rams into the target with the full force of its Z-Power. The power varies, depending on the original move." + }, + "corkscrewCrashSpecial": { + name: "Corkscrew Crash", + effect: "Dummy Data" + }, + "infernoOverdrivePhysical": { + name: "Inferno Overdrive", + effect: "The user breathes a stream of intense fire toward the target with the full force of its Z-Power. The power varies depending on the original move." + }, + "infernoOverdriveSpecial": { + name: "Inferno Overdrive", + effect: "Dummy Data" + }, + "hydroVortexPhysical": { + name: "Hydro Vortex", + effect: "The user creates a huge whirling current using its Z-Power to swallow the target with full force. The power varies, depending on the original move." + }, + "hydroVortexSpecial": { + name: "Hydro Vortex", + effect: "Dummy Data" + }, + "bloomDoomPhysical": { + name: "Bloom Doom", + effect: "The user collects energy from plants using its Z-Power and attacks the target with full force. The power varies, depending on the original move." + }, + "bloomDoomSpecial": { + name: "Bloom Doom", + effect: "Dummy Data" + }, + "gigavoltHavocPhysical": { + name: "Gigavolt Havoc", + effect: "The user hits the target with a powerful electric current collected by its Z-Power. The power varies, depending on the original move." + }, + "gigavoltHavocSpecial": { + name: "Gigavolt Havoc", + effect: "Dummy Data" + }, + "shatteredPsychePhysical": { + name: "Shattered Psyche", + effect: "The user controls the target with its Z-Power and hurts the target with full force. The power varies, depending on the original move." + }, + "shatteredPsycheSpecial": { + name: "Shattered Psyche", + effect: "Dummy Data" + }, + "subzeroSlammerPhysical": { + name: "Subzero Slammer", + effect: "The user dramatically drops the temperature using its Z-Power and freezes the target with full force. The power varies, depending on the original move." + }, + "subzeroSlammerSpecial": { + name: "Subzero Slammer", + effect: "Dummy Data" + }, + "devastatingDrakePhysical": { + name: "Devastating Drake", + effect: "The user materializes its aura using its Z-Power and attacks the target with full force. The power varies, depending on the original move." + }, + "devastatingDrakeSpecial": { + name: "Devastating Drake", + effect: "Dummy Data" + }, + "blackHoleEclipsePhysical": { + name: "Black Hole Eclipse", + effect: "The user gathers dark energy using its Z-Power and sucks the target into it. The power varies, depending on the original move." + }, + "blackHoleEclipseSpecial": { + name: "Black Hole Eclipse", + effect: "Dummy Data" + }, + "twinkleTacklePhysical": { + name: "Twinkle Tackle", + effect: "The user creates a very charming space using its Z-Power and totally toys with the target. The power varies, depending on the original move." + }, + "twinkleTackleSpecial": { + name: "Twinkle Tackle", + effect: "Dummy Data" + }, + "catastropika": { + name: "Catastropika", + effect: "The user, Pikachu, surrounds itself with the maximum amount of electricity using its Z-Power and pounces on its target with full force." + }, + "shoreUp": { + name: "Shore Up", + effect: "The user regains up to half of its max HP. It restores more HP in a sandstorm." + }, + "firstImpression": { + name: "First Impression", + effect: "Although this move has great power, it only works the first turn each time the user enters battle." + }, + "banefulBunker": { + name: "Baneful Bunker", + effect: "In addition to protecting the user from attacks, this move also poisons any attacker that makes direct contact." + }, + "spiritShackle": { + name: "Spirit Shackle", + effect: "The user attacks while simultaneously stitching the target's shadow to the ground to prevent the target from escaping." + }, + "darkestLariat": { + name: "Darkest Lariat", + effect: "The user swings both arms and hits the target. The target's stat changes don't affect this attack's damage." + }, + "sparklingAria": { + name: "Sparkling Aria", + effect: "The user bursts into song, emitting many bubbles. Any Pokémon suffering from a burn will be healed by the touch of these bubbles." + }, + "iceHammer": { + name: "Ice Hammer", + effect: "The user swings and hits with its strong, heavy fist. It lowers the user's Speed, however." + }, + "floralHealing": { + name: "Floral Healing", + effect: "The user restores the target's HP by up to half of its max HP. It restores more HP when the terrain is grass." + }, + "highHorsepower": { + name: "High Horsepower", + effect: "The user fiercely attacks the target using its entire body." + }, + "strengthSap": { + name: "Strength Sap", + effect: "The user restores its HP by the same amount as the target's Attack stat. It also lowers the target's Attack stat." + }, + "solarBlade": { + name: "Solar Blade", + effect: "In this two-turn attack, the user gathers light and fills a blade with the light's energy, attacking the target on the next turn." + }, + "leafage": { + name: "Leafage", + effect: "The user attacks by pelting the target with leaves." + }, + "spotlight": { + name: "Spotlight", + effect: "The user shines a spotlight on the target so that only the target will be attacked during the turn." + }, + "toxicThread": { + name: "Toxic Thread", + effect: "The user shoots poisonous threads to poison the target and lower the target's Speed stat." + }, + "laserFocus": { + name: "Laser Focus", + effect: "The user concentrates intensely. The attack on the next turn always results in a critical hit." + }, + "gearUp": { + name: "Gear Up", + effect: "The user engages its gears to raise the Attack and Sp. Atk stats of ally Pokémon with the Plus or Minus Ability." + }, + "throatChop": { + name: "Throat Chop", + effect: "The user attacks the target's throat, and the resultant suffering prevents the target from using moves that emit sound for two turns." + }, + "pollenPuff": { + name: "Pollen Puff", + effect: "The user attacks the enemy with a pollen puff that explodes. If the target is an ally, it gives the ally a pollen puff that restores its HP instead." + }, + "anchorShot": { + name: "Anchor Shot", + effect: "The user entangles the target with its anchor chain while attacking. The target becomes unable to flee." + }, + "psychicTerrain": { + name: "Psychic Terrain", + effect: "This protects Pokémon on the ground from priority moves and powers up Psychic-type moves for five turns." + }, + "lunge": { + name: "Lunge", + effect: "The user makes a lunge at the target, attacking with full force. This also lowers the target's Attack stat." + }, + "fireLash": { + name: "Fire Lash", + effect: "The user strikes the target with a burning lash. This also lowers the target's Defense stat." + }, + "powerTrip": { + name: "Power Trip", + effect: "The user boasts its strength and attacks the target. The more the user's stats are raised, the greater the move's power." + }, + "burnUp": { + name: "Burn Up", + effect: "To inflict massive damage, the user burns itself out. After using this move, the user will no longer be Fire type." + }, + "speedSwap": { + name: "Speed Swap", + effect: "The user exchanges Speed stats with the target." + }, + "smartStrike": { + name: "Smart Strike", + effect: "The user stabs the target with a sharp horn. This attack never misses." + }, + "purify": { + name: "Purify", + effect: "The user heals the target's status condition. If the move succeeds, it also restores the user's own HP." + }, + "revelationDance": { + name: "Revelation Dance", + effect: "The user attacks the target by dancing very hard. The user's type determines the type of this move." + }, + "coreEnforcer": { + name: "Core Enforcer", + effect: "If the Pokémon the user has inflicted damage on have already used their moves, this move eliminates the effect of the target's Ability." + }, + "tropKick": { + name: "Trop Kick", + effect: "The user lands an intense kick of tropical origins on the target. This also lowers the target's Attack stat." + }, + "instruct": { + name: "Instruct", + effect: "The user instructs the target to use the target's last move again." + }, + "beakBlast": { + name: "Beak Blast", + effect: "The user first heats up its beak, and then it attacks the target. Making direct contact with the Pokémon while it's heating up its beak results in a burn." + }, + "clangingScales": { + name: "Clanging Scales", + effect: "The user rubs the scales on its entire body and makes a huge noise to attack opposing Pokémon. The user's Defense stat goes down after the attack." + }, + "dragonHammer": { + name: "Dragon Hammer", + effect: "The user uses its body like a hammer to attack the target and inflict damage." + }, + "brutalSwing": { + name: "Brutal Swing", + effect: "The user swings its body around violently to inflict damage on everything in its vicinity." + }, + "auroraVeil": { + name: "Aurora Veil", + effect: "This move reduces damage from physical and special moves for five turns. This can be used only when it is snowing." + }, + "sinisterArrowRaid": { + name: "Sinister Arrow Raid", + effect: "The user, Decidueye, creates countless arrows using its Z-Power and shoots the target with full force." + }, + "maliciousMoonsault": { + name: "Malicious Moonsault", + effect: "The user, Incineroar, strengthens its body using its Z-Power and crashes into the target with full force." + }, + "oceanicOperetta": { + name: "Oceanic Operetta", + effect: "The user, Primarina, summons a massive amount of water using its Z-Power and attacks the target with full force." + }, + "guardianOfAlola": { + name: "Guardian of Alola", + effect: "The user, the Land Spirit Pokémon, obtains Alola's energy using its Z-Power and attacks the target with full force. This reduces the target's HP greatly." + }, + "soulStealing7StarStrike": { + name: "Soul-Stealing 7-Star Strike", + effect: "After obtaining Z-Power, the user, Marshadow, punches and kicks the target consecutively with full force." + }, + "stokedSparksurfer": { + name: "Stoked Sparksurfer", + effect: "After obtaining Z-Power, the user, Alolan Raichu, attacks the target with full force. This move leaves the target with paralysis." + }, + "pulverizingPancake": { + name: "Pulverizing Pancake", + effect: "Z-Power brings out the true capabilities of the user, Snorlax. The Pokémon moves its enormous body energetically and attacks the target with full force." + }, + "extremeEvoboost": { + name: "Extreme Evoboost", + effect: "After obtaining Z-Power, the user, Eevee, gets energy from its evolved friends and boosts its stats sharply." + }, + "genesisSupernova": { + name: "Genesis Supernova", + effect: "After obtaining Z-Power, the user, Mew, attacks the target with full force. The terrain will be charged with psychic energy." + }, + "shellTrap": { + name: "Shell Trap", + effect: "The user sets a shell trap. If the user is hit by a physical move, the trap will explode and inflict damage on opposing Pokémon." + }, + "fleurCannon": { + name: "Fleur Cannon", + effect: "The user unleashes a strong beam. The attack's recoil harshly lowers the user's Sp. Atk stat." + }, + "psychicFangs": { + name: "Psychic Fangs", + effect: "The user bites the target with its psychic capabilities. This can also destroy Light Screen and Reflect." + }, + "stompingTantrum": { + name: "Stomping Tantrum", + effect: "Driven by frustration, the user attacks the target. If the user's previous move has failed, the power of this move doubles." + }, + "shadowBone": { + name: "Shadow Bone", + effect: "The user attacks by beating the target with a bone that contains a spirit. This may also lower the target's Defense stat." + }, + "accelerock": { + name: "Accelerock", + effect: "The user smashes into the target at high speed. This move always goes first." + }, + "liquidation": { + name: "Liquidation", + effect: "The user slams into the target using a full-force blast of water. This may also lower the target's Defense stat." + }, + "prismaticLaser": { + name: "Prismatic Laser", + effect: "The user shoots powerful lasers using the power of a prism. The user can't move on the next turn." + }, + "spectralThief": { + name: "Spectral Thief", + effect: "The user hides in the target's shadow, steals the target's stat boosts, and then attacks." + }, + "sunsteelStrike": { + name: "Sunsteel Strike", + effect: "The user slams into the target with the force of a meteor. This move can be used on the target regardless of its Abilities." + }, + "moongeistBeam": { + name: "Moongeist Beam", + effect: "The user emits a sinister ray to attack the target. This move can be used on the target regardless of its Abilities." + }, + "tearfulLook": { + name: "Tearful Look", + effect: "The user gets teary eyed to make the target lose its combative spirit. This lowers the target's Attack and Sp. Atk stats." + }, + "zingZap": { + name: "Zing Zap", + effect: "A strong electric blast crashes down on the target, giving it an electric shock. This may also make the target flinch." + }, + "naturesMadness": { + name: "Nature's Madness", + effect: "The user hits the target with the force of nature. It halves the target's HP." + }, + "multiAttack": { + name: "Multi-Attack", + effect: "Cloaking itself in high energy, the user slams into the target. The memory held determines the move's type." + }, + "tenMillionVoltThunderbolt": { + name: "10,000,000 Volt Thunderbolt", + effect: "The user, Pikachu wearing a cap, powers up a jolt of electricity using its Z-Power and unleashes it. Critical hits land more easily." + }, + "mindBlown": { + name: "Mind Blown", + effect: "The user attacks everything around it by causing its own head to explode. This also damages the user." + }, + "plasmaFists": { + name: "Plasma Fists", + effect: "The user attacks with electrically charged fists. This move changes Normal-type moves to Electric-type moves." + }, + "photonGeyser": { + name: "Photon Geyser", + effect: "The user attacks a target with a pillar of light. This move inflicts Attack or Sp. Atk damage—whichever stat is higher for the user." + }, + "lightThatBurnsTheSky": { + name: "Light That Burns the Sky", + effect: "This attack inflicts Attack or Sp. Atk damage—whichever stat is higher for the user, Necrozma. This move ignores the target's Ability." + }, + "searingSunrazeSmash": { + name: "Searing Sunraze Smash", + effect: "After obtaining Z-Power, the user, Solgaleo, attacks the target with full force. This move can ignore the effect of the target's Ability." + }, + "menacingMoonrazeMaelstrom": { + name: "Menacing Moonraze Maelstrom", + effect: "After obtaining Z-Power, the user, Lunala, attacks the target with full force. This move can ignore the effect of the target's Ability." + }, + "letsSnuggleForever": { + name: "Let's Snuggle Forever", + effect: "After obtaining Z-Power, the user, Mimikyu, punches the target with full force." + }, + "splinteredStormshards": { + name: "Splintered Stormshards", + effect: "After obtaining Z-Power, the user, Lycanroc, attacks the target with full force. This move negates the effect on the battlefield." + }, + "clangorousSoulblaze": { + name: "Clangorous Soulblaze", + effect: "After obtaining Z-Power, the user, Kommo-o, attacks the opposing Pokémon with full force. This move boosts the user's stats." + }, + "zippyZap": { + name: "Zippy Zap", + effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness." + }, + "splishySplash": { + name: "Splishy Splash", + effect: "The user charges a huge wave with electricity and hits the opposing Pokémon with the wave. This may also leave the opposing Pokémon with paralysis." + }, + "floatyFall": { + name: "Floaty Fall", + effect: "The user floats in the air, and then dives at a steep angle to attack the target. This may also make the target flinch." + }, + "pikaPapow": { + name: "Pika Papow", + effect: "The more Pikachu loves its Trainer, the greater the move's power. It never misses." + }, + "bouncyBubble": { + name: "Bouncy Bubble", + effect: "The user attacks by shooting water bubbles at the target. It then absorbs water and restores its HP by the damage taken by the target." + }, + "buzzyBuzz": { + name: "Buzzy Buzz", + effect: "The user shoots a jolt of electricity to attack the target. This also leaves the target with paralysis." + }, + "sizzlySlide": { + name: "Sizzly Slide", + effect: "The user cloaks itself in fire and charges at the target. This also leaves the target with a burn." + }, + "glitzyGlow": { + name: "Glitzy Glow", + effect: "The user bombards the target with telekinetic force. A wondrous wall of light is put up to weaken the power of the opposing Pokémon's special moves." + }, + "baddyBad": { + name: "Baddy Bad", + effect: "The user acts bad and attacks the target. A wondrous wall of light is put up to weaken the power of the opposing Pokémon's physical moves." + }, + "sappySeed": { + name: "Sappy Seed", + effect: "The user grows a gigantic stalk that scatters seeds to attack the target. The seeds drain the target's HP every turn." + }, + "freezyFrost": { + name: "Freezy Frost", + effect: "The user attacks with a crystal made of cold frozen haze. It eliminates every stat change among all the Pokémon engaged in battle." + }, + "sparklySwirl": { + name: "Sparkly Swirl", + effect: "The user attacks the target by wrapping it with a whirlwind of an overpowering scent. This also heals all status conditions of the user's party." + }, + "veeveeVolley": { + name: "Veevee Volley", + effect: "The more Eevee loves its Trainer, the greater the move's power. It never misses." + }, + "doubleIronBash": { + name: "Double Iron Bash", + effect: "The user rotates, centering the hex nut in its chest, and then strikes with its arms twice in a row. This may also make the target flinch." + }, + "maxGuard": { + name: "Max Guard", + effect: "This move enables the user to protect itself from all attacks. Its chance of failing rises if it is used in succession." + }, + "dynamaxCannon": { + name: "Dynamax Cannon", + effect: "The user unleashes a strong beam from its core. Deals up to twice the damage if the target is overly leveled." + }, + "snipeShot": { + name: "Snipe Shot", + effect: "The user ignores the effects of opposing Pokémon's moves and Abilities that draw in moves, allowing this move to hit the chosen target." + }, + "jawLock": { + name: "Jaw Lock", + effect: "This move prevents the user and the target from switching out until either of them faints. The effect goes away if either of the Pokémon leaves the field." + }, + "stuffCheeks": { + name: "Stuff Cheeks", + effect: "The user eats its held Berry, then sharply raises its Defense stat." + }, + "noRetreat": { + name: "No Retreat", + effect: "This move raises all the user's stats but prevents the user from switching out or fleeing." + }, + "tarShot": { + name: "Tar Shot", + effect: "The user pours sticky tar over the target, lowering the target's Speed stat. The target becomes weaker to Fire-type moves." + }, + "magicPowder": { + name: "Magic Powder", + effect: "The user scatters a cloud of magic powder that changes the target to Psychic type." + }, + "dragonDarts": { + name: "Dragon Darts", + effect: "The user attacks twice using Dreepy. If there are two targets, this move hits each target once." + }, + "teatime": { + name: "Teatime", + effect: "The user has teatime with all the Pokémon in the battle. Each Pokémon eats its held Berry." + }, + "octolock": { + name: "Octolock", + effect: "The user locks the target in and prevents it from fleeing. This move also lowers the target's Defense and Sp. Def every turn." + }, + "boltBeak": { + name: "Bolt Beak", + effect: "The user stabs the target with its electrified beak. If the user attacks before the target, the power of this move is doubled." + }, + "fishiousRend": { + name: "Fishious Rend", + effect: "The user rends the target with its hard gills. If the user attacks before the target, the power of this move is doubled." + }, + "courtChange": { + name: "Court Change", + effect: "With its mysterious power, the user swaps the effects on either side of the field." + }, + "maxFlare": { + name: "Max Flare", + effect: "This is a Fire-type attack Dynamax Pokémon use. The user intensifies the sun for five turns." + }, + "maxFlutterby": { + name: "Max Flutterby", + effect: "This is a Bug-type attack Dynamax Pokémon use. This lowers the target's Sp. Atk stat." + }, + "maxLightning": { + name: "Max Lightning", + effect: "This is an Electric-type attack Dynamax Pokémon use. The user turns the ground into Electric Terrain for five turns." + }, + "maxStrike": { + name: "Max Strike", + effect: "This is a Normal-type attack Dynamax Pokémon use. This lowers the target's Speed stat." + }, + "maxKnuckle": { + name: "Max Knuckle", + effect: "This is a Fighting-type attack Dynamax Pokémon use. This raises ally Pokémon's Attack stats." + }, + "maxPhantasm": { + name: "Max Phantasm", + effect: "This is a Ghost-type attack Dynamax Pokémon use. This lowers the target's Defense stat." + }, + "maxHailstorm": { + name: "Max Hailstorm", + effect: "This is an Ice-type attack Dynamax Pokémon use. The user summons a hailstorm lasting five turns." + }, + "maxOoze": { + name: "Max Ooze", + effect: "This is a Poison-type attack Dynamax Pokémon use. This raises ally Pokémon's Sp. Atk stats." + }, + "maxGeyser": { + name: "Max Geyser", + effect: "This is a Water-type attack Dynamax Pokémon use. The user summons a heavy rain that falls for five turns." + }, + "maxAirstream": { + name: "Max Airstream", + effect: "This is a Flying-type attack Dynamax Pokémon use. This raises ally Pokémon's Speed stats." + }, + "maxStarfall": { + name: "Max Starfall", + effect: "This is a Fairy-type attack Dynamax Pokémon use. The user turns the ground into Misty Terrain for five turns." + }, + "maxWyrmwind": { + name: "Max Wyrmwind", + effect: "This is a Dragon-type attack Dynamax Pokémon use. This lowers the target's Attack stat." + }, + "maxMindstorm": { + name: "Max Mindstorm", + effect: "This is a Psychic-type attack Dynamax Pokémon use. The user turns the ground into Psychic Terrain for five turns." + }, + "maxRockfall": { + name: "Max Rockfall", + effect: "This is a Rock-type attack Dynamax Pokémon use. The user summons a sandstorm lasting five turns." + }, + "maxQuake": { + name: "Max Quake", + effect: "This is a Ground-type attack Dynamax Pokémon use. This raises ally Pokémon's Sp. Def stats." + }, + "maxDarkness": { + name: "Max Darkness", + effect: "This is a Dark-type attack Dynamax Pokémon use. This lowers the target's Sp. Def stat." + }, + "maxOvergrowth": { + name: "Max Overgrowth", + effect: "This is a Grass-type attack Dynamax Pokémon use. The user turns the ground into Grassy Terrain for five turns." + }, + "maxSteelspike": { + name: "Max Steelspike", + effect: "This is a Steel-type attack Dynamax Pokémon use. This raises ally Pokémon's Defense stats." + }, + "clangorousSoul": { + name: "Clangorous Soul", + effect: "The user raises all its stats by using some of its HP." + }, + "bodyPress": { + name: "Body Press", + effect: "The user attacks by slamming its body into the target. The higher the user's Defense, the more damage it can inflict on the target." + }, + "decorate": { + name: "Decorate", + effect: "The user sharply raises the target's Attack and Sp. Atk stats by decorating the target." + }, + "drumBeating": { + name: "Drum Beating", + effect: "The user plays its drum, controlling the drum's roots to attack the target. This also lowers the target's Speed stat." + }, + "snapTrap": { + name: "Snap Trap", + effect: "The user snares the target in a snap trap for four to five turns." + }, + "pyroBall": { + name: "Pyro Ball", + effect: "The user attacks by igniting a small stone and launching it as a fiery ball at the target. This may also leave the target with a burn." + }, + "behemothBlade": { + name: "Behemoth Blade", + effect: "The user wields a large, powerful sword using its whole body and cuts the target in a vigorous attack." + }, + "behemothBash": { + name: "Behemoth Bash", + effect: "The user's body becomes a firm shield and slams into the target fiercely." + }, + "auraWheel": { + name: "Aura Wheel", + effect: "Morpeko attacks and raises its Speed with the energy stored in its cheeks. This move's type changes depending on the user's form." + }, + "breakingSwipe": { + name: "Breaking Swipe", + effect: "The user swings its tough tail wildly and attacks opposing Pokémon. This also lowers their Attack stats." + }, + "branchPoke": { + name: "Branch Poke", + effect: "The user attacks the target by poking it with a sharply pointed branch." + }, + "overdrive": { + name: "Overdrive", + effect: "The user attacks opposing Pokémon by twanging a guitar or bass guitar, causing a huge echo and strong vibration." + }, + "appleAcid": { + name: "Apple Acid", + effect: "The user attacks the target with an acidic liquid created from tart apples. This also lowers the target's Sp. Def stat." + }, + "gravApple": { + name: "Grav Apple", + effect: "The user inflicts damage by dropping an apple from high above. This also lowers the target's Defense stat." + }, + "spiritBreak": { + name: "Spirit Break", + effect: "The user attacks the target with so much force that it could break the target's spirit. This also lowers the target's Sp. Atk stat." + }, + "strangeSteam": { + name: "Strange Steam", + effect: "The user attacks the target by emitting steam. This may also confuse the target." + }, + "lifeDew": { + name: "Life Dew", + effect: "The user scatters mysterious water around and restores the HP of itself and its ally Pokémon in the battle." + }, + "obstruct": { + name: "Obstruct", + effect: "This move enables the user to protect itself from all attacks. Its chance of failing rises if it is used in succession. Direct contact harshly lowers the attacker's Defense stat." + }, + "falseSurrender": { + name: "False Surrender", + effect: "The user pretends to bow its head, but then it stabs the target with its disheveled hair. This attack never misses." + }, + "meteorAssault": { + name: "Meteor Assault", + effect: "The user attacks wildly with its thick leek. The user can't move on the next turn, because the force of this move makes it stagger." + }, + "eternabeam": { + name: "Eternabeam", + effect: "This is Eternatus's most powerful attack in its original form. The user can't move on the next turn." + }, + "steelBeam": { + name: "Steel Beam", + effect: "The user fires a beam of steel that it collected from its entire body. This also damages the user." + }, + "expandingForce": { + name: "Expanding Force", + effect: "The user attacks the target with its psychic power. This move's power goes up and damages all opposing Pokémon on Psychic Terrain." + }, + "steelRoller": { + name: "Steel Roller", + effect: "The user attacks while destroying the terrain. This move fails when the ground hasn't turned into a terrain." + }, + "scaleShot": { + name: "Scale Shot", + effect: "The user attacks by shooting scales two to five times in a row. This move boosts the user's Speed stat but lowers its Defense stat." + }, + "meteorBeam": { + name: "Meteor Beam", + effect: "In this two-turn attack, the user gathers space power and boosts its Sp. Atk stat, then attacks the target on the next turn." + }, + "shellSideArm": { + name: "Shell Side Arm", + effect: "This move inflicts physical or special damage, whichever will be more effective. This may also poison the target." + }, + "mistyExplosion": { + name: "Misty Explosion", + effect: "The user attacks everything around it and faints upon using this move. This move's power is increased on Misty Terrain." + }, + "grassyGlide": { + name: "Grassy Glide", + effect: "Gliding on the ground, the user attacks the target. This move always goes first on Grassy Terrain." + }, + "risingVoltage": { + name: "Rising Voltage", + effect: "The user attacks with electric voltage rising from the ground. This move's power doubles when the target is on Electric Terrain." + }, + "terrainPulse": { + name: "Terrain Pulse", + effect: "The user utilizes the power of the terrain to attack. This move's type and power changes depending on the terrain when it's used." + }, + "skitterSmack": { + name: "Skitter Smack", + effect: "The user skitters behind the target to attack. This also lowers the target's Sp. Atk stat." + }, + "burningJealousy": { + name: "Burning Jealousy", + effect: "The user attacks with energy from jealousy. This leaves all opposing Pokémon that have had their stats boosted during the turn with a burn." + }, + "lashOut": { + name: "Lash Out", + effect: "The user lashes out to vent its frustration toward the target. If the user's stats were lowered during this turn, the power of this move is doubled." + }, + "poltergeist": { + name: "Poltergeist", + effect: "The user attacks the target by controlling the target's item. The move fails if the target doesn't have an item." + }, + "corrosiveGas": { + name: "Corrosive Gas", + effect: "The user surrounds everything around it with highly acidic gas and melts away items they hold." + }, + "coaching": { + name: "Coaching", + effect: "The user properly coaches its ally Pokémon, boosting their Attack and Defense stats." + }, + "flipTurn": { + name: "Flip Turn", + effect: "After making its attack, the user rushes back to switch places with a party Pokémon in waiting." + }, + "tripleAxel": { + name: "Triple Axel", + effect: "A consecutive three-kick attack that becomes more powerful with each successful hit." + }, + "dualWingbeat": { + name: "Dual Wingbeat", + effect: "The user slams the target with its wings. The target is hit twice in a row." + }, + "scorchingSands": { + name: "Scorching Sands", + effect: "The user throws scorching sand at the target to attack. This may also leave the target with a burn." + }, + "jungleHealing": { + name: "Jungle Healing", + effect: "The user becomes one with the jungle, restoring HP and healing any status conditions of itself and its ally Pokémon in battle." + }, + "wickedBlow": { + name: "Wicked Blow", + effect: "The user, having mastered the Dark style, strikes the target with a fierce blow. This attack always results in a critical hit." + }, + "surgingStrikes": { + name: "Surging Strikes", + effect: "The user, having mastered the Water style, strikes the target with a flowing motion three times in a row. This attack always results in a critical hit." + }, + "thunderCage": { + name: "Thunder Cage", + effect: "The user traps the target in a cage of sparking electricity for four to five turns." + }, + "dragonEnergy": { + name: "Dragon Energy", + effect: "Converting its life-force into power, the user attacks opposing Pokémon. The lower the user's HP, the lower the move's power." + }, + "freezingGlare": { + name: "Freezing Glare", + effect: "The user shoots its psychic power from its eyes to attack. This may also leave the target frozen." + }, + "fieryWrath": { + name: "Fiery Wrath", + effect: "The user transforms its wrath into a fire-like aura to attack. This may also make opposing Pokémon flinch." + }, + "thunderousKick": { + name: "Thunderous Kick", + effect: "The user overwhelms the target with lightning-like movement before delivering a kick. This also lowers the target's Defense stat." + }, + "glacialLance": { + name: "Glacial Lance", + effect: "The user attacks by hurling a blizzard-cloaked icicle lance at opposing Pokémon." + }, + "astralBarrage": { + name: "Astral Barrage", + effect: "The user attacks by sending a frightful amount of small ghosts at opposing Pokémon." + }, + "eerieSpell": { + name: "Eerie Spell", + effect: "The user attacks with its tremendous psychic power. This also removes 3 PP from the target's last move." + }, + "direClaw": { + name: "Dire Claw", + effect: "The user lashes out at the target with ruinous claws. This may also leave the target poisoned, paralyzed, or asleep." + }, + "psyshieldBash": { + name: "Psyshield Bash", + effect: "Cloaking itself in psychic energy, the user slams into the target. This also boosts the user's Defense stat." + }, + "powerShift": { + name: "Power Shift", + effect: "The user swaps its Attack and Defense stats." + }, + "stoneAxe": { + name: "Stone Axe", + effect: "The user swings its stone axes at the target. Stone splinters left behind by this attack float around the target." + }, + "springtideStorm": { + name: "Springtide Storm", + effect: "The user attacks by wrapping opposing Pokémon in fierce winds brimming with love and hate. This may also lower their Attack stats." + }, + "mysticalPower": { + name: "Mystical Power", + effect: "The user attacks by emitting a mysterious power. This also boosts the user's Sp. Atk stat." + }, + "ragingFury": { + name: "Raging Fury", + effect: "The user rampages around spewing flames for two to three turns. The user then becomes confused." + }, + "waveCrash": { + name: "Wave Crash", + effect: "The user shrouds itself in water and slams into the target with its whole body to inflict damage. This also damages the user quite a lot." + }, + "chloroblast": { + name: "Chloroblast", + effect: "The user launches its amassed chlorophyll to inflict damage on the target. This also damages the user." + }, + "mountainGale": { + name: "Mountain Gale", + effect: "The user hurls giant chunks of ice at the target to inflict damage. This may also make the target flinch." + }, + "victoryDance": { + name: "Victory Dance", + effect: "The user performs an intense dance to usher in victory, boosting its Attack, Defense, and Speed stats." + }, + "headlongRush": { + name: "Headlong Rush", + effect: "The user smashes into the target in a full-body tackle. This also lowers the user's Defense and Sp. Def stats." + }, + "barbBarrage": { + name: "Barb Barrage", + effect: "The user launches countless toxic barbs to inflict damage. This may also poison the target. This move's power is doubled if the target is already poisoned." + }, + "esperWing": { + name: "Esper Wing", + effect: "The user slashes the target with aura-enriched wings. This also boosts the user's Speed stat. This move has a heightened chance of landing a critical hit." + }, + "bitterMalice": { + name: "Bitter Malice", + effect: "The user attacks the target with spine-chilling resentment. This also lowers the target's Attack stat." + }, + "shelter": { + name: "Shelter", + effect: "The user makes its skin as hard as an iron shield, sharply boosting its Defense stat." + }, + "tripleArrows": { + name: "Triple Arrows", + effect: "The user kicks, then fires three arrows. This move has a heightened chance of landing a critical hit and may also lower the target's Defense stat or make it flinch." + }, + "infernalParade": { + name: "Infernal Parade", + effect: "The user attacks with myriad fireballs. This may also leave the target with a burn. This move's power is doubled if the target has a status condition." + }, + "ceaselessEdge": { + name: "Ceaseless Edge", + effect: "The user slashes its shell blade at the target. Shell splinters left behind by this attack remain scattered under the target as spikes." + }, + "bleakwindStorm": { + name: "Bleakwind Storm", + effect: "The user attacks with savagely cold winds that cause both body and spirit to tremble. This may also lower the Speed stats of opposing Pokémon." + }, + "wildboltStorm": { + name: "Wildbolt Storm", + effect: "The user summons a thunderous tempest and savagely attacks with lightning and wind. This may also leave opposing Pokémon with paralysis." + }, + "sandsearStorm": { + name: "Sandsear Storm", + effect: "The user attacks by wrapping opposing Pokémon in fierce winds and searingly hot sand. This may also leave them with a burn." + }, + "lunarBlessing": { + name: "Lunar Blessing", + effect: "The user receives a blessing from the crescent moon, restoring HP and curing status conditions for itself and its ally Pokémon currently in the battle." + }, + "takeHeart": { + name: "Take Heart", + effect: "The user lifts its spirits, curing its own status conditions and boosting its Sp. Atk and Sp. Def stats." + }, + "gMaxWildfire": { + name: "G-Max Wildfire", + effect: "A Fire-type attack that Gigantamax Charizard use. This move continues to deal damage to opponents for four turns." + }, + "gMaxBefuddle": { + name: "G-Max Befuddle", + effect: "A Bug-type attack that Gigantamax Butterfree use. This move inflicts the poisoned, paralyzed, or asleep status condition on opponents." + }, + "gMaxVoltCrash": { + name: "G-Max Volt Crash", + effect: "An Electric-type attack that Gigantamax Pikachu use. This move paralyzes opponents." + }, + "gMaxGoldRush": { + name: "G-Max Gold Rush", + effect: "A Normal-type attack that Gigantamax Meowth use. This move confuses opponents and also earns extra money." + }, + "gMaxChiStrike": { + name: "G-Max Chi Strike", + effect: "A Fighting-type attack that Gigantamax Machamp use. This move raises the chance of critical hits." + }, + "gMaxTerror": { + name: "G-Max Terror", + effect: "A Ghost-type attack that Gigantamax Gengar use. This Pokémon steps on the opposing Pokémon's shadow to prevent them from escaping." + }, + "gMaxResonance": { + name: "G-Max Resonance", + effect: "An Ice-type attack that Gigantamax Lapras use. This move reduces the damage received for five turns." + }, + "gMaxCuddle": { + name: "G-Max Cuddle", + effect: "A Normal-type attack that Gigantamax Eevee use. This move infatuates opponents." + }, + "gMaxReplenish": { + name: "G-Max Replenish", + effect: "A Normal-type attack that Gigantamax Snorlax use. This move restores Berries that have been eaten." + }, + "gMaxMalodor": { + name: "G-Max Malodor", + effect: "A Poison-type attack that Gigantamax Garbodor use. This move poisons opponents." + }, + "gMaxStonesurge": { + name: "G-Max Stonesurge", + effect: "A Water-type attack that Gigantamax Drednaw use. This move scatters sharp rocks around the field." + }, + "gMaxWindRage": { + name: "G-Max Wind Rage", + effect: "A Flying-type attack that Gigantamax Corviknight use. This move removes the effects of moves like Reflect and Light Screen." + }, + "gMaxStunShock": { + name: "G-Max Stun Shock", + effect: "An Electric-type attack that Gigantamax Toxtricity use. This move poisons or paralyzes opponents." + }, + "gMaxFinale": { + name: "G-Max Finale", + effect: "A Fairy-type attack that Gigantamax Alcremie use. This move heals the HP of allies." + }, + "gMaxDepletion": { + name: "G-Max Depletion", + effect: "A Dragon-type attack that Gigantamax Duraludon use. Reduces the PP of the last move used." + }, + "gMaxGravitas": { + name: "G-Max Gravitas", + effect: "A Psychic-type attack that Gigantamax Orbeetle use. This move changes gravity for five turns." + }, + "gMaxVolcalith": { + name: "G-Max Volcalith", + effect: "A Rock-type attack that Gigantamax Coalossal use. This move continues to deal damage to opponents for four turns." + }, + "gMaxSandblast": { + name: "G-Max Sandblast", + effect: "A Ground-type attack that Gigantamax Sandaconda use. Opponents are trapped in a raging sandstorm for four to five turns." + }, + "gMaxSnooze": { + name: "G-Max Snooze", + effect: "A Dark-type attack that Gigantamax Grimmsnarl use. The user lets loose a huge yawn that lulls the targets into falling asleep on the next turn." + }, + "gMaxTartness": { + name: "G-Max Tartness", + effect: "A Grass-type attack that Gigantamax Flapple use. This move reduces the opponents' evasiveness." + }, + "gMaxSweetness": { + name: "G-Max Sweetness", + effect: "A Grass-type attack that Gigantamax Appletun use. This move heals the status conditions of allies." + }, + "gMaxSmite": { + name: "G-Max Smite", + effect: "A Fairy-type attack that Gigantamax Hatterene use. This move confuses opponents." + }, + "gMaxSteelsurge": { + name: "G-Max Steelsurge", + effect: "A Steel-type attack that Gigantamax Copperajah use. This move scatters sharp spikes around the field." + }, + "gMaxMeltdown": { + name: "G-Max Meltdown", + effect: "A Steel-type attack that Gigantamax Melmetal use. This move makes opponents incapable of using the same move twice in a row." + }, + "gMaxFoamBurst": { + name: "G-Max Foam Burst", + effect: "A Water-type attack that Gigantamax Kingler use. This move harshly lowers the Speed of opponents." + }, + "gMaxCentiferno": { + name: "G-Max Centiferno", + effect: "A Fire-type attack that Gigantamax Centiskorch use. This move traps opponents in flames for four to five turns." + }, + "gMaxVineLash": { + name: "G-Max Vine Lash", + effect: "A Grass-type attack that Gigantamax Venusaur use. This move continues to deal damage to opponents for four turns." + }, + "gMaxCannonade": { + name: "G-Max Cannonade", + effect: "A Water-type attack that Gigantamax Blastoise use. This move continues to deal damage to opponents for four turns." + }, + "gMaxDrumSolo": { + name: "G-Max Drum Solo", + effect: "A Grass-type attack that Gigantamax Rillaboom use. This move can be used on the target regardless of its Abilities." + }, + "gMaxFireball": { + name: "G-Max Fireball", + effect: "A Fire-type attack that Gigantamax Cinderace use. This move can be used on the target regardless of its Abilities." + }, + "gMaxHydrosnipe": { + name: "G-Max Hydrosnipe", + effect: "A Water-type attack that Gigantamax Inteleon use. This move can be used on the target regardless of its Abilities." + }, + "gMaxOneBlow": { + name: "G-Max One Blow", + effect: "A Dark-type attack that Gigantamax Urshifu use. This single-strike move can ignore Max Guard." + }, + "gMaxRapidFlow": { + name: "G-Max Rapid Flow", + effect: "A Water-type attack that Gigantamax Urshifu use. This rapid-strike move can ignore Max Guard." + }, + "teraBlast": { + name: "Tera Blast", + effect: "If the user has Terastallized, it unleashes energy of its Tera Type. This move inflicts damage using the Attack or Sp. Atk stat-whichever is higher for the user." + }, + "silkTrap": { + name: "Silk Trap", + effect: "The user spins a silken trap, protecting itself from damage while lowering the Speed stat of any attacker that makes direct contact." + }, + "axeKick": { + name: "Axe Kick", + effect: "The user attacks by kicking up into the air and slamming its heel down upon the target. This may also confuse the target. If it misses, the user takes damage instead." + }, + "lastRespects": { + name: "Last Respects", + effect: "The user attacks to avenge its allies. The more defeated allies there are in the user's party, the greater the move's power." + }, + "luminaCrash": { + name: "Lumina Crash", + effect: "The user attacks by unleashing a peculiar light that even affects the mind. This also harshly lowers the target's Sp. Def stat." + }, + "orderUp": { + name: "Order Up", + effect: "The user attacks with elegant poise. If the user has a Tatsugiri in its mouth, this move boosts one of the user's stats based on the Tatsugiri's form." + }, + "jetPunch": { + name: "Jet Punch", + effect: "The user summons a torrent around its fist and punches at blinding speed. This move always goes first." + }, + "spicyExtract": { + name: "Spicy Extract", + effect: "The user emits an incredibly spicy extract, sharply boosting the target's Attack stat and harshly lowering the target's Defense stat." + }, + "spinOut": { + name: "Spin Out", + effect: "The user spins furiously by straining its legs, inflicting damage on the target. This also harshly lowers the user's Speed stat." + }, + "populationBomb": { + name: "Population Bomb", + effect: "The user's fellows gather in droves to perform a combo attack that hits the target one to ten times in a row." + }, + "iceSpinner": { + name: "Ice Spinner", + effect: "The user covers its feet in thin ice and twirls around, slamming into the target. This move's spinning motion also destroys the terrain." + }, + "glaiveRush": { + name: "Glaive Rush", + effect: "The user throws its entire body into a reckless charge. After this move is used, attacks on the user cannot miss and will inflict double damage until the user's next turn." + }, + "revivalBlessing": { + name: "Revival Blessing", + effect: "The user bestows a loving blessing, reviving a party Pokémon that has fainted and restoring half that Pokémon's max HP." + }, + "saltCure": { + name: "Salt Cure", + effect: "The user salt cures the target, inflicting damage every turn. Steel and Water types are more strongly affected by this move." + }, + "tripleDive": { + name: "Triple Dive", + effect: "The user performs a perfectly timed triple dive, hitting the target with splashes of water three times in a row." + }, + "mortalSpin": { + name: "Mortal Spin", + effect: "The user performs a spin attack that can also eliminate the effects of such moves as Bind, Wrap, and Leech Seed. This also poisons opposing Pokémon." + }, + "doodle": { + name: "Doodle", + effect: "The user captures the very essence of the target in a sketch. This changes the Abilities of the user and its ally Pokémon to that of the target." + }, + "filletAway": { + name: "Fillet Away", + effect: "The user sharply boosts its Attack, Sp. Atk, and Speed stats by using its own HP." + }, + "kowtowCleave": { + name: "Kowtow Cleave", + effect: "The user slashes at the target after kowtowing to make the target let down its guard. This attack never misses." + }, + "flowerTrick": { + name: "Flower Trick", + effect: "The user throws a rigged bouquet of flowers at the target. This attack never misses and always lands a critical hit." + }, + "torchSong": { + name: "Torch Song", + effect: "The user blows out raging flames as if singing a song, scorching the target. This also boosts the user's Sp. Atk stat." + }, + "aquaStep": { + name: "Aqua Step", + effect: "The user toys with the target and attacks it using light and fluid dance steps. This also boosts the user's Speed stat." + }, + "ragingBull": { + name: "Raging Bull", + effect: "The user performs a tackle like a raging bull. This move's type depends on the user's form. It can also break barriers, such as Light Screen and Reflect." + }, + "makeItRain": { + name: "Make It Rain", + effect: "The user attacks by throwing out a mass of coins. This also lowers the user's Sp. Atk stat. Money is earned after the battle." + }, + "psyblade": { + name: "Psyblade", + effect: "The user rends the target with an ethereal blade. This move's power is boosted by 50 percent if the user is on Electric Terrain." + }, + "hydroSteam": { + name: "Hydro Steam", + effect: "The user blasts the target with boiling-hot water. This move's power is not lowered in harsh sunlight but rather boosted by 50 percent." + }, + "ruination": { + name: "Ruination", + effect: "The user summons a ruinous disaster. This cuts the target's HP in half." + }, + "collisionCourse": { + name: "Collision Course", + effect: "The user transforms and crashes to the ground, causing a massive prehistoric explosion. This move's power is boosted more than usual if it's a supereffective hit." + }, + "electroDrift": { + name: "Electro Drift", + effect: "The user races forward at ultrafast speeds, piercing its target with futuristic electricity. This move's power is boosted more than usual if it's a supereffective hit." + }, + "shedTail": { + name: "Shed Tail", + effect: "The user creates a substitute for itself using its own HP before switching places with a party Pokémon in waiting." + }, + "chillyReception": { + name: "Chilly Reception", + effect: "The user tells a chillingly bad joke before switching places with a party Pokémon in waiting. This summons a snowstorm lasting five turns." + }, + "tidyUp": { + name: "Tidy Up", + effect: "The user tidies up and removes the effects of Spikes, Stealth Rock, Sticky Web, Toxic Spikes, and Substitute. This also boosts the user's Attack and Speed stats." + }, + "snowscape": { + name: "Snowscape", + effect: "The user summons a snowstorm lasting five turns. This boosts the Defense stats of Ice types." + }, + "pounce": { + name: "Pounce", + effect: "The user attacks by pouncing on the target. This also lowers the target's Speed stat." + }, + "trailblaze": { + name: "Trailblaze", + effect: "The user attacks suddenly as if leaping out from tall grass. The user's nimble footwork boosts its Speed stat." + }, + "chillingWater": { + name: "Chilling Water", + effect: "The user attacks the target by showering it with water that's so cold it saps the target's power. This also lowers the target's Attack stat." + }, + "hyperDrill": { + name: "Hyper Drill", + effect: "The user spins the pointed part of its body at high speed to pierce the target. This attack can hit a target using a move such as Protect or Detect." + }, + "twinBeam": { + name: "Twin Beam", + effect: "The user shoots mystical beams from its eyes to inflict damage. The target is hit twice in a row." + }, + "rageFist": { + name: "Rage Fist", + effect: "The user converts its rage into energy to attack. The more times the user has been hit by attacks, the greater the move's power." + }, + "armorCannon": { + name: "Armor Cannon", + effect: "The user shoots its own armor out as blazing projectiles. This also lowers the user's Defense and Sp. Def stats." + }, + "bitterBlade": { + name: "Bitter Blade", + effect: "The user focuses its bitter feelings toward the world of the living into a slashing attack. The user's HP is restored by up to half the damage taken by the target." + }, + "doubleShock": { + name: "Double Shock", + effect: "The user discharges all the electricity from its body to perform a high-damage attack. After using this move, the user will no longer be Electric type." + }, + "gigatonHammer": { + name: "Gigaton Hammer", + effect: "The user swings its whole body around to attack with its huge hammer. This move can't be used twice in a row." + }, + "comeuppance": { + name: "Comeuppance", + effect: "The user retaliates with much greater force against the opponent that last inflicted damage on it." + }, + "aquaCutter": { + name: "Aqua Cutter", + effect: "The user expels pressurized water to cut at the target like a blade. This move has a heightened chance of landing a critical hit." + }, + "blazingTorque": { + name: "Blazing Torque", + effect: "The user revs their blazing engine into the target. This may also leave the target with a burn." + }, + "wickedTorque": { + name: "Wicked Torque", + effect: "The user revs their engine into the target with malicious intent. This may put the target to sleep." + }, + "noxiousTorque": { + name: "Noxious Torque", + effect: "The user revs their poisonous engine into the target. This may also poison the target." + }, + "combatTorque": { + name: "Combat Torque", + effect: "The user revs their engine forcefully into the target. This may also leave the target with paralysis." + }, + "magicalTorque": { + name: "Magical Torque", + effect: "The user revs their fae-like engine into the target. This may also confuse the target." + }, + "bloodMoon": { + name: "Blood Moon", + effect: "The user unleashes the full brunt of its spirit from a full moon that shines as red as blood. This move can't be used twice in a row." + }, + "matchaGotcha": { + name: "Matcha Gotcha", + effect: "The user fires a blast of tea that it mixed. The user's HP is restored by up to half the damage taken by the target. This may also leave the target with a burn." + }, + "syrupBomb": { + name: "Syrup Bomb", + effect: "The user sets off an explosion of sticky candy syrup, which coats the target and causes the target's Speed stat to drop each turn for three turns." + }, + "ivyCudgel": { + name: "Ivy Cudgel", + effect: "The user strikes with an ivy-wrapped cudgel. This move's type changes depending on the mask worn by the user, and it has a heightened chance of landing a critical hit." + }, + "electroShot": { + name: "Electro Shot", + effect: "The user gathers electricity on the first turn, boosting its Sp. Atk stat, then fires a high-voltage shot on the next turn. The shot will be fired immediately in rain." + }, + "teraStarstorm": { + name: "Tera Starstorm", + effect: "With the power of its crystals, the user bombards and eliminates the target. When used by Terapagos in its Stellar Form, this move damages all opposing Pokémon." + }, + "fickleBeam": { + name: "Fickle Beam", + effect: "The user shoots a beam of light to inflict damage. Sometimes all the user's heads shoot beams in unison, doubling the move's power." + }, + "burningBulwark": { + name: "Burning Bulwark", + effect: "The user's intensely hot fur protects it from attacks and also burns any attacker that makes direct contact with it." + }, + "thunderclap": { + name: "Thunderclap", + effect: "This move enables the user to attack first with a jolt of electricity. This move fails if the target is not readying an attack." + }, + "mightyCleave": { + name: "Mighty Cleave", + effect: "The user wields the light that has accumulated atop its head to cleave the target. This move hits even if the target protects itself." + }, + "tachyonCutter": { + name: "Tachyon Cutter", + effect: "The user attacks by launching particle blades at the target twice in a row. This attack never misses." + }, + "hardPress": { + name: "Hard Press", + effect: "The target is crushed with an arm, a claw, or the like to inflict damage. The more HP the target has left, the greater the move's power." + }, + "dragonCheer": { + name: "Dragon Cheer", + effect: "The user raises its allies' morale with a draconic cry so that their future attacks have a heightened chance of landing critical hits. This rouses Dragon types more." + }, + "alluringVoice": { + name: "Alluring Voice", + effect: "The user attacks the target using its angelic voice. This also confuses the target if its stats have been boosted during the turn." + }, + "temperFlare": { + name: "Temper Flare", + effect: "Spurred by desperation, the user attacks the target. This move's power is doubled if the user's previous move failed." + }, + "supercellSlam": { + name: "Supercell Slam", + effect: "The user electrifies its body and drops onto the target to inflict damage. If this move misses, the user takes damage instead." + }, + "psychicNoise": { + name: "Psychic Noise", + effect: "The user attacks the target with unpleasant sound waves. For two turns, the target is prevented from recovering HP through moves, Abilities, or held items." + }, + "upperHand": { + name: "Upper Hand", + effect: "The user reacts to the target's movement and strikes with the heel of its palm, making the target flinch. This move fails if the target is not readying a priority move." + }, + "malignantChain": { + name: "Malignant Chain", + effect: "The user pours toxins into the target by wrapping them in a toxic, corrosive chain. This may also leave the target badly poisoned." + } +} as const; diff --git a/src/locales/ca-ES/nature.ts b/src/locales/ca-ES/nature.ts new file mode 100644 index 00000000000..9ab26f3eb2a --- /dev/null +++ b/src/locales/ca-ES/nature.ts @@ -0,0 +1,29 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const nature: SimpleTranslationEntries = { + "Hardy": "Hardy", + "Lonely": "Lonely", + "Brave": "Brave", + "Adamant": "Adamant", + "Naughty": "Naughty", + "Bold": "Bold", + "Docile": "Docile", + "Relaxed": "Relaxed", + "Impish": "Impish", + "Lax": "Lax", + "Timid": "Timid", + "Hasty": "Hasty", + "Serious": "Serious", + "Jolly": "Jolly", + "Naive": "Naive", + "Modest": "Modest", + "Mild": "Mild", + "Quiet": "Quiet", + "Bashful": "Bashful", + "Rash": "Rash", + "Calm": "Calm", + "Gentle": "Gentle", + "Sassy": "Sassy", + "Careful": "Careful", + "Quirky": "Quirky" +} as const; diff --git a/src/locales/ca-ES/party-ui-handler.ts b/src/locales/ca-ES/party-ui-handler.ts new file mode 100644 index 00000000000..4f300dd36ea --- /dev/null +++ b/src/locales/ca-ES/party-ui-handler.ts @@ -0,0 +1,54 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const partyUiHandler: SimpleTranslationEntries = { + "SEND_OUT": "Send Out", + "SUMMARY": "Summary", + "CANCEL": "Cancel", + "RELEASE": "Release", + "APPLY": "Apply", + "TEACH": "Teach", + "SPLICE": "Splice", + "UNSPLICE": "Unsplice", + "ACTIVATE": "Activate", + "DEACTIVATE": "Deactivate", + "TRANSFER": "Transfer", + "ALL": "All", + "PASS_BATON": "Pass Baton", + "UNPAUSE_EVOLUTION": "Unpause Evolution", + "REVIVE": "Revive", + "RENAME": "Rename", + + "choosePokemon": "Choose a Pokémon.", + "doWhatWithThisPokemon": "Do what with this Pokémon?", + "noEnergy": "{{pokemonName}} has no energy\nleft to battle!", + "hasEnergy": "{{pokemonName}} still has energy\nto battle!", + "cantBeUsed": "{{pokemonName}} can't be used in\nthis challenge!", + "tooManyItems": "{{pokemonName}} has too many\nof this item!", + "anyEffect": "It won't have any effect.", + "unpausedEvolutions": "Evolutions have been unpaused for {{pokemonName}}.", + "unspliceConfirmation": "Do you really want to unsplice {{fusionName}}\nfrom {{pokemonName}}? {{fusionName}} will be lost.", + "wasReverted": "{{fusionName}} was reverted to {{pokemonName}}.", + "releaseConfirmation": "Do you really want to release {{pokemonName}}?", + "releaseInBattle": "You can't release a Pokémon that's in battle!", + "selectAMove": "Select a move.", + "changeQuantity": "Select a held item to transfer.\nUse < and > to change the quantity.", + "selectAnotherPokemonToSplice": "Select another Pokémon to splice.", + "cancel": "Cancel", + + // Slot TM text + "able": "Able", + "notAble": "Not able", + "learned": "Learned", + + // Releasing messages + "goodbye": "Goodbye, {{pokemonName}}!", + "byebye": "Byebye, {{pokemonName}}!", + "farewell": "Farewell, {{pokemonName}}!", + "soLong": "So long, {{pokemonName}}!", + "thisIsWhereWePart": "This is where we part, {{pokemonName}}!", + "illMissYou": "I'll miss you, {{pokemonName}}!", + "illNeverForgetYou": "I'll never forget you, {{pokemonName}}!", + "untilWeMeetAgain": "Until we meet again, {{pokemonName}}!", + "sayonara": "Sayonara, {{pokemonName}}!", + "smellYaLater": "Smell ya later, {{pokemonName}}!", +} as const; diff --git a/src/locales/ca-ES/pokeball.ts b/src/locales/ca-ES/pokeball.ts new file mode 100644 index 00000000000..01017cac46d --- /dev/null +++ b/src/locales/ca-ES/pokeball.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const pokeball: SimpleTranslationEntries = { + "pokeBall": "Poké Ball", + "greatBall": "Great Ball", + "ultraBall": "Ultra Ball", + "rogueBall": "Rogue Ball", + "masterBall": "Master Ball", + "luxuryBall": "Luxury Ball", +} as const; diff --git a/src/locales/ca-ES/pokemon-form.ts b/src/locales/ca-ES/pokemon-form.ts new file mode 100644 index 00000000000..e8d6fb8df4a --- /dev/null +++ b/src/locales/ca-ES/pokemon-form.ts @@ -0,0 +1,197 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battlePokemonForm: SimpleTranslationEntries = { + "mega": "Mega {{pokemonName}}", + "mega-x": "Mega {{pokemonName}} X", + "mega-y": "Mega {{pokemonName}} Y", + "primal": "Primal {{pokemonName}}", + "gigantamax": "G-Max {{pokemonName}}", + "eternamax": "E-Max {{pokemonName}}", + + "megaChange": "{{preName}} Mega Evolved\ninto {{pokemonName}}!", + "gigantamaxChange": "{{preName}} Gigantamaxed\ninto {{pokemonName}}!", + "eternamaxChange": "{{preName}} Eternamaxed\ninto {{pokemonName}}!", + "revertChange": "{{pokemonName}} reverted\nto its original form!", + "formChange": "{{preName}} changed form!", +} as const; + +export const pokemonForm: SimpleTranslationEntries = { + // Starters forms + // 1G + "pikachuCosplay": "Cosplay", + "pikachuCoolCosplay": "Cool Cosplay", + "pikachuBeautyCosplay": "Beauty Cosplay", + "pikachuCuteCosplay": "Cute Cosplay", + "pikachuSmartCosplay": "Smart Cosplay", + "pikachuToughCosplay": "Tough Cosplay", + "pikachuPartner": "Partner", + "eeveePartner": "Partner", + // 2G + "pichuSpiky": "Spiky", + "unownA": "A", + "unownB": "B", + "unownC": "C", + "unownD": "D", + "unownE": "E", + "unownF": "F", + "unownG": "G", + "unownH": "H", + "unownI": "I", + "unownJ": "J", + "unownK": "K", + "unownL": "L", + "unownM": "M", + "unownN": "N", + "unownO": "O", + "unownP": "P", + "unownQ": "Q", + "unownR": "R", + "unownS": "S", + "unownT": "T", + "unownU": "U", + "unownV": "V", + "unownW": "W", + "unownX": "X", + "unownY": "Y", + "unownZ": "Z", + "unownExclamation": "!", + "unownQuestion": "?", + // 3G + "castformSunny": "Sunny", + "castformRainy": "Rainy", + "castformSnowy": "Snowy", + "deoxysNormal": "Normal", + // 4G + "burmyPlant": "Plant", + "burmySandy": "Sandy", + "burmyTrash": "Trash", + "shellosEast": "East", + "shellosWest": "West", + "rotomHeat": "Heat", + "rotomWash": "Wash", + "rotomFrost": "Frost", + "rotomFan": "Fan", + "rotomMow": "Mow", + "giratinaAltered": "Altered", + "shayminLand": "Land", + // 5G + "basculinRedStriped": "Red Striped", + "basculinBlueStriped": "Blue Striped", + "basculinWhiteStriped": "White Striped", + "deerlingSpring": "Spring", + "deerlingSummer": "Summer", + "deerlingAutumn": "Autumn", + "deerlingWinter": "Winter", + "tornadusIncarnate": "Incarnate", + "thundurusIncarnate": "Incarnate", + "landorusIncarnate": "Incarnate", + "keldeoOrdinary": "Ordinary", + "meloettaAria": "Aria", + // 6G + "froakieBattleBond": "Battle Bond", + "scatterbugMeadow": "Meadow", + "scatterbugIcySnow": "Icy Snow", + "scatterbugPolar": "Polar", + "scatterbugTundra": "Tundra", + "scatterbugContinental": "Continental", + "scatterbugGarden": "Garden", + "scatterbugElegant": "Elegant", + "scatterbugModern": "Modern", + "scatterbugMarine": "Marine", + "scatterbugArchipelago": "Archipelago", + "scatterbugHighPlains": "High Plains", + "scatterbugSandstorm": "Sandstorm", + "scatterbugRiver": "River", + "scatterbugMonsoon": "Monsoon", + "scatterbugSavanna": "Savanna", + "scatterbugSun": "Sun", + "scatterbugOcean": "Ocean", + "scatterbugJungle": "Jungle", + "scatterbugFancy": "Fancy", + "scatterbugPokeBall": "Poké Ball", + "flabebeRed": "Red", + "flabebeYellow": "Yellow", + "flabebeOrange": "Orange", + "flabebeBlue": "Blue", + "flabebeWhite": "White", + "furfrouHeart": "Heart", + "furfrouStar": "Star", + "furfrouDiamond": "Diamond", + "furfrouDebutante": "Debutante", + "furfrouMatron": "Matron", + "furfrouDandy": "Dandy", + "furfrouLaReine": "La Reine", + "furfrouKabuki": "Kabuki", + "furfrouPharaoh": "Pharaoh", + "pumpkabooSmall": "Small", + "pumpkabooLarge": "Large", + "pumpkabooSuper": "Super", + "xerneasNeutral": "Neutral", + "xerneasActive": "Active", + "zygarde50": "50% Forme", + "zygarde10": "10% Forme", + "zygarde50Pc": "50% Forme Power Construct", + "zygarde10Pc": "10% Forme Power Construct", + "zygardeComplete": "Complete Forme", + // 7G + "oricorioBaile": "Baile", + "oricorioPompom": "Pom-Pom", + "oricorioPau": "Pau", + "oricorioSensu": "Sensu", + "rockruffOwnTempo": "Own Tempo", + "miniorRedMeteor": "Red Meteor", + "miniorOrangeMeteor": "Orange Meteor", + "miniorYellowMeteor": "Yellow Meteor", + "miniorGreenMeteor": "Green Meteor", + "miniorBlueMeteor": "Blue Meteor", + "miniorIndigoMeteor": "Indigo Meteor", + "miniorVioletMeteor": "Violet Meteor", + "miniorRed": "Red", + "miniorOrange": "Orange", + "miniorYellow": "Yellow", + "miniorGreen": "Green", + "miniorBlue": "Blue", + "miniorIndigo": "Indigo", + "miniorViolet": "Violet", + "mimikyuDisguised": "Disguised", + "mimikyuBusted": "Busted", + "magearnaOriginal": "Original", + "marshadowZenith": "Zenith", + // 8G + "sinisteaPhony": "Phony", + "sinisteaAntique": "Antique", + "eiscueNoIce": "No Ice", + "indeedeeMale": "Male", + "indeedeeFemale": "Female", + "morpekoFullBelly": "Full Belly", + "zacianHeroOfManyBattles": "Hero Of Many Battles", + "zamazentaHeroOfManyBattles": "Hero Of Many Battles", + "zarudeDada": "Dada", + "enamorusIncarnate": "Incarnate", + // 9G + "squawkabillyGreenPlumage": "Green Plumage", + "squawkabillyBluePlumage": "Blue Plumage", + "squawkabillyYellowPlumage": "Yellow Plumage", + "squawkabillyWhitePlumage": "White Plumage", + "tatsugiriCurly": "Curly", + "tatsugiriDroopy": "Droopy", + "tatsugiriStretchy": "Stretchy", + "gimmighoulChest": "Chest", + "gimmighoulRoaming": "Roaming", + "koraidonApexBuild": "Apex Build", + "koraidonLimitedBuild":"Limited Build", + "koraidonSprintingBuild":"Sprinting Build", + "koraidonSwimmingBuild":"Swimming Build", + "koraidonGlidingBuild":"Gliding Build", + "miraidonUltimateMode":"Ultimate Mode", + "miraidonLowPowerMode":"Low Power Mode", + "miraidonDriveMode":"Drive Mode", + "miraidonAquaticMode":"Aquatic Mode", + "miraidonGlideMode":"Glide Mode", + "poltchageistCounterfeit": "Counterfeit", + "poltchageistArtisan": "Artisan", + "paldeaTaurosCombat": "Combat", + "paldeaTaurosBlaze": "Blaze", + "paldeaTaurosAqua": "Aqua", + +} as const; diff --git a/src/locales/ca-ES/pokemon-info-container.ts b/src/locales/ca-ES/pokemon-info-container.ts new file mode 100644 index 00000000000..fd8acfb2e3e --- /dev/null +++ b/src/locales/ca-ES/pokemon-info-container.ts @@ -0,0 +1,9 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const pokemonInfoContainer: SimpleTranslationEntries = { + "moveset": "Moveset", + "gender": "Gender:", + "ability": "Ability:", + "nature": "Nature:", + "form": "Form:" +} as const; diff --git a/src/locales/ca-ES/pokemon-info.ts b/src/locales/ca-ES/pokemon-info.ts new file mode 100644 index 00000000000..f31fdac69ab --- /dev/null +++ b/src/locales/ca-ES/pokemon-info.ts @@ -0,0 +1,43 @@ +import { PokemonInfoTranslationEntries } from "#app/interfaces/locales"; + +export const pokemonInfo: PokemonInfoTranslationEntries = { + Stat: { + "HP": "Max. HP", + "HPshortened": "MaxHP", + "ATK": "Attack", + "ATKshortened": "Atk", + "DEF": "Defense", + "DEFshortened": "Def", + "SPATK": "Sp. Atk", + "SPATKshortened": "SpAtk", + "SPDEF": "Sp. Def", + "SPDEFshortened": "SpDef", + "SPD": "Speed", + "SPDshortened": "Spd", + "ACC": "Accuracy", + "EVA": "Evasiveness" + }, + + Type: { + "UNKNOWN": "Unknown", + "NORMAL": "Normal", + "FIGHTING": "Fighting", + "FLYING": "Flying", + "POISON": "Poison", + "GROUND": "Ground", + "ROCK": "Rock", + "BUG": "Bug", + "GHOST": "Ghost", + "STEEL": "Steel", + "FIRE": "Fire", + "WATER": "Water", + "GRASS": "Grass", + "ELECTRIC": "Electric", + "PSYCHIC": "Psychic", + "ICE": "Ice", + "DRAGON": "Dragon", + "DARK": "Dark", + "FAIRY": "Fairy", + "STELLAR": "Stellar", + }, +} as const; diff --git a/src/locales/ca-ES/pokemon-summary.ts b/src/locales/ca-ES/pokemon-summary.ts new file mode 100644 index 00000000000..484ea2a9d67 --- /dev/null +++ b/src/locales/ca-ES/pokemon-summary.ts @@ -0,0 +1,20 @@ +import { TranslationEntries } from "#app/interfaces/locales"; + +export const pokemonSummary: TranslationEntries = { + "pokemonInfo": "Pokémon Info", + "status": "Status", + "powerAccuracyCategory": "Power\nAccuracy\nCategory", + "type": "Type", + "unknownTrainer": "Unknown", + "ot": "OT", + "nature": "nature", + "expPoints": "Exp. Points", + "nextLv": "Next Lv.", + "cancel": "Cancel", + + "memoString": "{{natureFragment}} nature,\n{{metFragment}}", + "metFragment": { + "normal": "met at Lv{{level}},\n{{biome}}.", + "apparently": "apparently met at Lv{{level}},\n{{biome}}.", + }, +} as const; diff --git a/src/locales/ca-ES/pokemon.ts b/src/locales/ca-ES/pokemon.ts new file mode 100644 index 00000000000..297bbcc3975 --- /dev/null +++ b/src/locales/ca-ES/pokemon.ts @@ -0,0 +1,1086 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const pokemon: SimpleTranslationEntries = { + "bulbasaur": "Bulbasaur", + "ivysaur": "Ivysaur", + "venusaur": "Venusaur", + "charmander": "Charmander", + "charmeleon": "Charmeleon", + "charizard": "Charizard", + "squirtle": "Squirtle", + "wartortle": "Wartortle", + "blastoise": "Blastoise", + "caterpie": "Caterpie", + "metapod": "Metapod", + "butterfree": "Butterfree", + "weedle": "Weedle", + "kakuna": "Kakuna", + "beedrill": "Beedrill", + "pidgey": "Pidgey", + "pidgeotto": "Pidgeotto", + "pidgeot": "Pidgeot", + "rattata": "Rattata", + "raticate": "Raticate", + "spearow": "Spearow", + "fearow": "Fearow", + "ekans": "Ekans", + "arbok": "Arbok", + "pikachu": "Pikachu", + "raichu": "Raichu", + "sandshrew": "Sandshrew", + "sandslash": "Sandslash", + "nidoran_f": "Nidoran♀", + "nidorina": "Nidorina", + "nidoqueen": "Nidoqueen", + "nidoran_m": "Nidoran♂", + "nidorino": "Nidorino", + "nidoking": "Nidoking", + "clefairy": "Clefairy", + "clefable": "Clefable", + "vulpix": "Vulpix", + "ninetales": "Ninetales", + "jigglypuff": "Jigglypuff", + "wigglytuff": "Wigglytuff", + "zubat": "Zubat", + "golbat": "Golbat", + "oddish": "Oddish", + "gloom": "Gloom", + "vileplume": "Vileplume", + "paras": "Paras", + "parasect": "Parasect", + "venonat": "Venonat", + "venomoth": "Venomoth", + "diglett": "Diglett", + "dugtrio": "Dugtrio", + "meowth": "Meowth", + "persian": "Persian", + "psyduck": "Psyduck", + "golduck": "Golduck", + "mankey": "Mankey", + "primeape": "Primeape", + "growlithe": "Growlithe", + "arcanine": "Arcanine", + "poliwag": "Poliwag", + "poliwhirl": "Poliwhirl", + "poliwrath": "Poliwrath", + "abra": "Abra", + "kadabra": "Kadabra", + "alakazam": "Alakazam", + "machop": "Machop", + "machoke": "Machoke", + "machamp": "Machamp", + "bellsprout": "Bellsprout", + "weepinbell": "Weepinbell", + "victreebel": "Victreebel", + "tentacool": "Tentacool", + "tentacruel": "Tentacruel", + "geodude": "Geodude", + "graveler": "Graveler", + "golem": "Golem", + "ponyta": "Ponyta", + "rapidash": "Rapidash", + "slowpoke": "Slowpoke", + "slowbro": "Slowbro", + "magnemite": "Magnemite", + "magneton": "Magneton", + "farfetchd": "Farfetch'd", + "doduo": "Doduo", + "dodrio": "Dodrio", + "seel": "Seel", + "dewgong": "Dewgong", + "grimer": "Grimer", + "muk": "Muk", + "shellder": "Shellder", + "cloyster": "Cloyster", + "gastly": "Gastly", + "haunter": "Haunter", + "gengar": "Gengar", + "onix": "Onix", + "drowzee": "Drowzee", + "hypno": "Hypno", + "krabby": "Krabby", + "kingler": "Kingler", + "voltorb": "Voltorb", + "electrode": "Electrode", + "exeggcute": "Exeggcute", + "exeggutor": "Exeggutor", + "cubone": "Cubone", + "marowak": "Marowak", + "hitmonlee": "Hitmonlee", + "hitmonchan": "Hitmonchan", + "lickitung": "Lickitung", + "koffing": "Koffing", + "weezing": "Weezing", + "rhyhorn": "Rhyhorn", + "rhydon": "Rhydon", + "chansey": "Chansey", + "tangela": "Tangela", + "kangaskhan": "Kangaskhan", + "horsea": "Horsea", + "seadra": "Seadra", + "goldeen": "Goldeen", + "seaking": "Seaking", + "staryu": "Staryu", + "starmie": "Starmie", + "mr_mime": "Mr. Mime", + "scyther": "Scyther", + "jynx": "Jynx", + "electabuzz": "Electabuzz", + "magmar": "Magmar", + "pinsir": "Pinsir", + "tauros": "Tauros", + "magikarp": "Magikarp", + "gyarados": "Gyarados", + "lapras": "Lapras", + "ditto": "Ditto", + "eevee": "Eevee", + "vaporeon": "Vaporeon", + "jolteon": "Jolteon", + "flareon": "Flareon", + "porygon": "Porygon", + "omanyte": "Omanyte", + "omastar": "Omastar", + "kabuto": "Kabuto", + "kabutops": "Kabutops", + "aerodactyl": "Aerodactyl", + "snorlax": "Snorlax", + "articuno": "Articuno", + "zapdos": "Zapdos", + "moltres": "Moltres", + "dratini": "Dratini", + "dragonair": "Dragonair", + "dragonite": "Dragonite", + "mewtwo": "Mewtwo", + "mew": "Mew", + "chikorita": "Chikorita", + "bayleef": "Bayleef", + "meganium": "Meganium", + "cyndaquil": "Cyndaquil", + "quilava": "Quilava", + "typhlosion": "Typhlosion", + "totodile": "Totodile", + "croconaw": "Croconaw", + "feraligatr": "Feraligatr", + "sentret": "Sentret", + "furret": "Furret", + "hoothoot": "Hoothoot", + "noctowl": "Noctowl", + "ledyba": "Ledyba", + "ledian": "Ledian", + "spinarak": "Spinarak", + "ariados": "Ariados", + "crobat": "Crobat", + "chinchou": "Chinchou", + "lanturn": "Lanturn", + "pichu": "Pichu", + "cleffa": "Cleffa", + "igglybuff": "Igglybuff", + "togepi": "Togepi", + "togetic": "Togetic", + "natu": "Natu", + "xatu": "Xatu", + "mareep": "Mareep", + "flaaffy": "Flaaffy", + "ampharos": "Ampharos", + "bellossom": "Bellossom", + "marill": "Marill", + "azumarill": "Azumarill", + "sudowoodo": "Sudowoodo", + "politoed": "Politoed", + "hoppip": "Hoppip", + "skiploom": "Skiploom", + "jumpluff": "Jumpluff", + "aipom": "Aipom", + "sunkern": "Sunkern", + "sunflora": "Sunflora", + "yanma": "Yanma", + "wooper": "Wooper", + "quagsire": "Quagsire", + "espeon": "Espeon", + "umbreon": "Umbreon", + "murkrow": "Murkrow", + "slowking": "Slowking", + "misdreavus": "Misdreavus", + "unown": "Unown", + "wobbuffet": "Wobbuffet", + "girafarig": "Girafarig", + "pineco": "Pineco", + "forretress": "Forretress", + "dunsparce": "Dunsparce", + "gligar": "Gligar", + "steelix": "Steelix", + "snubbull": "Snubbull", + "granbull": "Granbull", + "qwilfish": "Qwilfish", + "scizor": "Scizor", + "shuckle": "Shuckle", + "heracross": "Heracross", + "sneasel": "Sneasel", + "teddiursa": "Teddiursa", + "ursaring": "Ursaring", + "slugma": "Slugma", + "magcargo": "Magcargo", + "swinub": "Swinub", + "piloswine": "Piloswine", + "corsola": "Corsola", + "remoraid": "Remoraid", + "octillery": "Octillery", + "delibird": "Delibird", + "mantine": "Mantine", + "skarmory": "Skarmory", + "houndour": "Houndour", + "houndoom": "Houndoom", + "kingdra": "Kingdra", + "phanpy": "Phanpy", + "donphan": "Donphan", + "porygon2": "Porygon2", + "stantler": "Stantler", + "smeargle": "Smeargle", + "tyrogue": "Tyrogue", + "hitmontop": "Hitmontop", + "smoochum": "Smoochum", + "elekid": "Elekid", + "magby": "Magby", + "miltank": "Miltank", + "blissey": "Blissey", + "raikou": "Raikou", + "entei": "Entei", + "suicune": "Suicune", + "larvitar": "Larvitar", + "pupitar": "Pupitar", + "tyranitar": "Tyranitar", + "lugia": "Lugia", + "ho_oh": "Ho-Oh", + "celebi": "Celebi", + "treecko": "Treecko", + "grovyle": "Grovyle", + "sceptile": "Sceptile", + "torchic": "Torchic", + "combusken": "Combusken", + "blaziken": "Blaziken", + "mudkip": "Mudkip", + "marshtomp": "Marshtomp", + "swampert": "Swampert", + "poochyena": "Poochyena", + "mightyena": "Mightyena", + "zigzagoon": "Zigzagoon", + "linoone": "Linoone", + "wurmple": "Wurmple", + "silcoon": "Silcoon", + "beautifly": "Beautifly", + "cascoon": "Cascoon", + "dustox": "Dustox", + "lotad": "Lotad", + "lombre": "Lombre", + "ludicolo": "Ludicolo", + "seedot": "Seedot", + "nuzleaf": "Nuzleaf", + "shiftry": "Shiftry", + "taillow": "Taillow", + "swellow": "Swellow", + "wingull": "Wingull", + "pelipper": "Pelipper", + "ralts": "Ralts", + "kirlia": "Kirlia", + "gardevoir": "Gardevoir", + "surskit": "Surskit", + "masquerain": "Masquerain", + "shroomish": "Shroomish", + "breloom": "Breloom", + "slakoth": "Slakoth", + "vigoroth": "Vigoroth", + "slaking": "Slaking", + "nincada": "Nincada", + "ninjask": "Ninjask", + "shedinja": "Shedinja", + "whismur": "Whismur", + "loudred": "Loudred", + "exploud": "Exploud", + "makuhita": "Makuhita", + "hariyama": "Hariyama", + "azurill": "Azurill", + "nosepass": "Nosepass", + "skitty": "Skitty", + "delcatty": "Delcatty", + "sableye": "Sableye", + "mawile": "Mawile", + "aron": "Aron", + "lairon": "Lairon", + "aggron": "Aggron", + "meditite": "Meditite", + "medicham": "Medicham", + "electrike": "Electrike", + "manectric": "Manectric", + "plusle": "Plusle", + "minun": "Minun", + "volbeat": "Volbeat", + "illumise": "Illumise", + "roselia": "Roselia", + "gulpin": "Gulpin", + "swalot": "Swalot", + "carvanha": "Carvanha", + "sharpedo": "Sharpedo", + "wailmer": "Wailmer", + "wailord": "Wailord", + "numel": "Numel", + "camerupt": "Camerupt", + "torkoal": "Torkoal", + "spoink": "Spoink", + "grumpig": "Grumpig", + "spinda": "Spinda", + "trapinch": "Trapinch", + "vibrava": "Vibrava", + "flygon": "Flygon", + "cacnea": "Cacnea", + "cacturne": "Cacturne", + "swablu": "Swablu", + "altaria": "Altaria", + "zangoose": "Zangoose", + "seviper": "Seviper", + "lunatone": "Lunatone", + "solrock": "Solrock", + "barboach": "Barboach", + "whiscash": "Whiscash", + "corphish": "Corphish", + "crawdaunt": "Crawdaunt", + "baltoy": "Baltoy", + "claydol": "Claydol", + "lileep": "Lileep", + "cradily": "Cradily", + "anorith": "Anorith", + "armaldo": "Armaldo", + "feebas": "Feebas", + "milotic": "Milotic", + "castform": "Castform", + "kecleon": "Kecleon", + "shuppet": "Shuppet", + "banette": "Banette", + "duskull": "Duskull", + "dusclops": "Dusclops", + "tropius": "Tropius", + "chimecho": "Chimecho", + "absol": "Absol", + "wynaut": "Wynaut", + "snorunt": "Snorunt", + "glalie": "Glalie", + "spheal": "Spheal", + "sealeo": "Sealeo", + "walrein": "Walrein", + "clamperl": "Clamperl", + "huntail": "Huntail", + "gorebyss": "Gorebyss", + "relicanth": "Relicanth", + "luvdisc": "Luvdisc", + "bagon": "Bagon", + "shelgon": "Shelgon", + "salamence": "Salamence", + "beldum": "Beldum", + "metang": "Metang", + "metagross": "Metagross", + "regirock": "Regirock", + "regice": "Regice", + "registeel": "Registeel", + "latias": "Latias", + "latios": "Latios", + "kyogre": "Kyogre", + "groudon": "Groudon", + "rayquaza": "Rayquaza", + "jirachi": "Jirachi", + "deoxys": "Deoxys", + "turtwig": "Turtwig", + "grotle": "Grotle", + "torterra": "Torterra", + "chimchar": "Chimchar", + "monferno": "Monferno", + "infernape": "Infernape", + "piplup": "Piplup", + "prinplup": "Prinplup", + "empoleon": "Empoleon", + "starly": "Starly", + "staravia": "Staravia", + "staraptor": "Staraptor", + "bidoof": "Bidoof", + "bibarel": "Bibarel", + "kricketot": "Kricketot", + "kricketune": "Kricketune", + "shinx": "Shinx", + "luxio": "Luxio", + "luxray": "Luxray", + "budew": "Budew", + "roserade": "Roserade", + "cranidos": "Cranidos", + "rampardos": "Rampardos", + "shieldon": "Shieldon", + "bastiodon": "Bastiodon", + "burmy": "Burmy", + "wormadam": "Wormadam", + "mothim": "Mothim", + "combee": "Combee", + "vespiquen": "Vespiquen", + "pachirisu": "Pachirisu", + "buizel": "Buizel", + "floatzel": "Floatzel", + "cherubi": "Cherubi", + "cherrim": "Cherrim", + "shellos": "Shellos", + "gastrodon": "Gastrodon", + "ambipom": "Ambipom", + "drifloon": "Drifloon", + "drifblim": "Drifblim", + "buneary": "Buneary", + "lopunny": "Lopunny", + "mismagius": "Mismagius", + "honchkrow": "Honchkrow", + "glameow": "Glameow", + "purugly": "Purugly", + "chingling": "Chingling", + "stunky": "Stunky", + "skuntank": "Skuntank", + "bronzor": "Bronzor", + "bronzong": "Bronzong", + "bonsly": "Bonsly", + "mime_jr": "Mime Jr.", + "happiny": "Happiny", + "chatot": "Chatot", + "spiritomb": "Spiritomb", + "gible": "Gible", + "gabite": "Gabite", + "garchomp": "Garchomp", + "munchlax": "Munchlax", + "riolu": "Riolu", + "lucario": "Lucario", + "hippopotas": "Hippopotas", + "hippowdon": "Hippowdon", + "skorupi": "Skorupi", + "drapion": "Drapion", + "croagunk": "Croagunk", + "toxicroak": "Toxicroak", + "carnivine": "Carnivine", + "finneon": "Finneon", + "lumineon": "Lumineon", + "mantyke": "Mantyke", + "snover": "Snover", + "abomasnow": "Abomasnow", + "weavile": "Weavile", + "magnezone": "Magnezone", + "lickilicky": "Lickilicky", + "rhyperior": "Rhyperior", + "tangrowth": "Tangrowth", + "electivire": "Electivire", + "magmortar": "Magmortar", + "togekiss": "Togekiss", + "yanmega": "Yanmega", + "leafeon": "Leafeon", + "glaceon": "Glaceon", + "gliscor": "Gliscor", + "mamoswine": "Mamoswine", + "porygon_z": "Porygon-Z", + "gallade": "Gallade", + "probopass": "Probopass", + "dusknoir": "Dusknoir", + "froslass": "Froslass", + "rotom": "Rotom", + "uxie": "Uxie", + "mesprit": "Mesprit", + "azelf": "Azelf", + "dialga": "Dialga", + "palkia": "Palkia", + "heatran": "Heatran", + "regigigas": "Regigigas", + "giratina": "Giratina", + "cresselia": "Cresselia", + "phione": "Phione", + "manaphy": "Manaphy", + "darkrai": "Darkrai", + "shaymin": "Shaymin", + "arceus": "Arceus", + "victini": "Victini", + "snivy": "Snivy", + "servine": "Servine", + "serperior": "Serperior", + "tepig": "Tepig", + "pignite": "Pignite", + "emboar": "Emboar", + "oshawott": "Oshawott", + "dewott": "Dewott", + "samurott": "Samurott", + "patrat": "Patrat", + "watchog": "Watchog", + "lillipup": "Lillipup", + "herdier": "Herdier", + "stoutland": "Stoutland", + "purrloin": "Purrloin", + "liepard": "Liepard", + "pansage": "Pansage", + "simisage": "Simisage", + "pansear": "Pansear", + "simisear": "Simisear", + "panpour": "Panpour", + "simipour": "Simipour", + "munna": "Munna", + "musharna": "Musharna", + "pidove": "Pidove", + "tranquill": "Tranquill", + "unfezant": "Unfezant", + "blitzle": "Blitzle", + "zebstrika": "Zebstrika", + "roggenrola": "Roggenrola", + "boldore": "Boldore", + "gigalith": "Gigalith", + "woobat": "Woobat", + "swoobat": "Swoobat", + "drilbur": "Drilbur", + "excadrill": "Excadrill", + "audino": "Audino", + "timburr": "Timburr", + "gurdurr": "Gurdurr", + "conkeldurr": "Conkeldurr", + "tympole": "Tympole", + "palpitoad": "Palpitoad", + "seismitoad": "Seismitoad", + "throh": "Throh", + "sawk": "Sawk", + "sewaddle": "Sewaddle", + "swadloon": "Swadloon", + "leavanny": "Leavanny", + "venipede": "Venipede", + "whirlipede": "Whirlipede", + "scolipede": "Scolipede", + "cottonee": "Cottonee", + "whimsicott": "Whimsicott", + "petilil": "Petilil", + "lilligant": "Lilligant", + "basculin": "Basculin", + "sandile": "Sandile", + "krokorok": "Krokorok", + "krookodile": "Krookodile", + "darumaka": "Darumaka", + "darmanitan": "Darmanitan", + "maractus": "Maractus", + "dwebble": "Dwebble", + "crustle": "Crustle", + "scraggy": "Scraggy", + "scrafty": "Scrafty", + "sigilyph": "Sigilyph", + "yamask": "Yamask", + "cofagrigus": "Cofagrigus", + "tirtouga": "Tirtouga", + "carracosta": "Carracosta", + "archen": "Archen", + "archeops": "Archeops", + "trubbish": "Trubbish", + "garbodor": "Garbodor", + "zorua": "Zorua", + "zoroark": "Zoroark", + "minccino": "Minccino", + "cinccino": "Cinccino", + "gothita": "Gothita", + "gothorita": "Gothorita", + "gothitelle": "Gothitelle", + "solosis": "Solosis", + "duosion": "Duosion", + "reuniclus": "Reuniclus", + "ducklett": "Ducklett", + "swanna": "Swanna", + "vanillite": "Vanillite", + "vanillish": "Vanillish", + "vanilluxe": "Vanilluxe", + "deerling": "Deerling", + "sawsbuck": "Sawsbuck", + "emolga": "Emolga", + "karrablast": "Karrablast", + "escavalier": "Escavalier", + "foongus": "Foongus", + "amoonguss": "Amoonguss", + "frillish": "Frillish", + "jellicent": "Jellicent", + "alomomola": "Alomomola", + "joltik": "Joltik", + "galvantula": "Galvantula", + "ferroseed": "Ferroseed", + "ferrothorn": "Ferrothorn", + "klink": "Klink", + "klang": "Klang", + "klinklang": "Klinklang", + "tynamo": "Tynamo", + "eelektrik": "Eelektrik", + "eelektross": "Eelektross", + "elgyem": "Elgyem", + "beheeyem": "Beheeyem", + "litwick": "Litwick", + "lampent": "Lampent", + "chandelure": "Chandelure", + "axew": "Axew", + "fraxure": "Fraxure", + "haxorus": "Haxorus", + "cubchoo": "Cubchoo", + "beartic": "Beartic", + "cryogonal": "Cryogonal", + "shelmet": "Shelmet", + "accelgor": "Accelgor", + "stunfisk": "Stunfisk", + "mienfoo": "Mienfoo", + "mienshao": "Mienshao", + "druddigon": "Druddigon", + "golett": "Golett", + "golurk": "Golurk", + "pawniard": "Pawniard", + "bisharp": "Bisharp", + "bouffalant": "Bouffalant", + "rufflet": "Rufflet", + "braviary": "Braviary", + "vullaby": "Vullaby", + "mandibuzz": "Mandibuzz", + "heatmor": "Heatmor", + "durant": "Durant", + "deino": "Deino", + "zweilous": "Zweilous", + "hydreigon": "Hydreigon", + "larvesta": "Larvesta", + "volcarona": "Volcarona", + "cobalion": "Cobalion", + "terrakion": "Terrakion", + "virizion": "Virizion", + "tornadus": "Tornadus", + "thundurus": "Thundurus", + "reshiram": "Reshiram", + "zekrom": "Zekrom", + "landorus": "Landorus", + "kyurem": "Kyurem", + "keldeo": "Keldeo", + "meloetta": "Meloetta", + "genesect": "Genesect", + "chespin": "Chespin", + "quilladin": "Quilladin", + "chesnaught": "Chesnaught", + "fennekin": "Fennekin", + "braixen": "Braixen", + "delphox": "Delphox", + "froakie": "Froakie", + "frogadier": "Frogadier", + "greninja": "Greninja", + "bunnelby": "Bunnelby", + "diggersby": "Diggersby", + "fletchling": "Fletchling", + "fletchinder": "Fletchinder", + "talonflame": "Talonflame", + "scatterbug": "Scatterbug", + "spewpa": "Spewpa", + "vivillon": "Vivillon", + "litleo": "Litleo", + "pyroar": "Pyroar", + "flabebe": "Flabébé", + "floette": "Floette", + "florges": "Florges", + "skiddo": "Skiddo", + "gogoat": "Gogoat", + "pancham": "Pancham", + "pangoro": "Pangoro", + "furfrou": "Furfrou", + "espurr": "Espurr", + "meowstic": "Meowstic", + "honedge": "Honedge", + "doublade": "Doublade", + "aegislash": "Aegislash", + "spritzee": "Spritzee", + "aromatisse": "Aromatisse", + "swirlix": "Swirlix", + "slurpuff": "Slurpuff", + "inkay": "Inkay", + "malamar": "Malamar", + "binacle": "Binacle", + "barbaracle": "Barbaracle", + "skrelp": "Skrelp", + "dragalge": "Dragalge", + "clauncher": "Clauncher", + "clawitzer": "Clawitzer", + "helioptile": "Helioptile", + "heliolisk": "Heliolisk", + "tyrunt": "Tyrunt", + "tyrantrum": "Tyrantrum", + "amaura": "Amaura", + "aurorus": "Aurorus", + "sylveon": "Sylveon", + "hawlucha": "Hawlucha", + "dedenne": "Dedenne", + "carbink": "Carbink", + "goomy": "Goomy", + "sliggoo": "Sliggoo", + "goodra": "Goodra", + "klefki": "Klefki", + "phantump": "Phantump", + "trevenant": "Trevenant", + "pumpkaboo": "Pumpkaboo", + "gourgeist": "Gourgeist", + "bergmite": "Bergmite", + "avalugg": "Avalugg", + "noibat": "Noibat", + "noivern": "Noivern", + "xerneas": "Xerneas", + "yveltal": "Yveltal", + "zygarde": "Zygarde", + "diancie": "Diancie", + "hoopa": "Hoopa", + "volcanion": "Volcanion", + "rowlet": "Rowlet", + "dartrix": "Dartrix", + "decidueye": "Decidueye", + "litten": "Litten", + "torracat": "Torracat", + "incineroar": "Incineroar", + "popplio": "Popplio", + "brionne": "Brionne", + "primarina": "Primarina", + "pikipek": "Pikipek", + "trumbeak": "Trumbeak", + "toucannon": "Toucannon", + "yungoos": "Yungoos", + "gumshoos": "Gumshoos", + "grubbin": "Grubbin", + "charjabug": "Charjabug", + "vikavolt": "Vikavolt", + "crabrawler": "Crabrawler", + "crabominable": "Crabominable", + "oricorio": "Oricorio", + "cutiefly": "Cutiefly", + "ribombee": "Ribombee", + "rockruff": "Rockruff", + "lycanroc": "Lycanroc", + "wishiwashi": "Wishiwashi", + "mareanie": "Mareanie", + "toxapex": "Toxapex", + "mudbray": "Mudbray", + "mudsdale": "Mudsdale", + "dewpider": "Dewpider", + "araquanid": "Araquanid", + "fomantis": "Fomantis", + "lurantis": "Lurantis", + "morelull": "Morelull", + "shiinotic": "Shiinotic", + "salandit": "Salandit", + "salazzle": "Salazzle", + "stufful": "Stufful", + "bewear": "Bewear", + "bounsweet": "Bounsweet", + "steenee": "Steenee", + "tsareena": "Tsareena", + "comfey": "Comfey", + "oranguru": "Oranguru", + "passimian": "Passimian", + "wimpod": "Wimpod", + "golisopod": "Golisopod", + "sandygast": "Sandygast", + "palossand": "Palossand", + "pyukumuku": "Pyukumuku", + "type_null": "Type: Null", + "silvally": "Silvally", + "minior": "Minior", + "komala": "Komala", + "turtonator": "Turtonator", + "togedemaru": "Togedemaru", + "mimikyu": "Mimikyu", + "bruxish": "Bruxish", + "drampa": "Drampa", + "dhelmise": "Dhelmise", + "jangmo_o": "Jangmo-o", + "hakamo_o": "Hakamo-o", + "kommo_o": "Kommo-o", + "tapu_koko": "Tapu Koko", + "tapu_lele": "Tapu Lele", + "tapu_bulu": "Tapu Bulu", + "tapu_fini": "Tapu Fini", + "cosmog": "Cosmog", + "cosmoem": "Cosmoem", + "solgaleo": "Solgaleo", + "lunala": "Lunala", + "nihilego": "Nihilego", + "buzzwole": "Buzzwole", + "pheromosa": "Pheromosa", + "xurkitree": "Xurkitree", + "celesteela": "Celesteela", + "kartana": "Kartana", + "guzzlord": "Guzzlord", + "necrozma": "Necrozma", + "magearna": "Magearna", + "marshadow": "Marshadow", + "poipole": "Poipole", + "naganadel": "Naganadel", + "stakataka": "Stakataka", + "blacephalon": "Blacephalon", + "zeraora": "Zeraora", + "meltan": "Meltan", + "melmetal": "Melmetal", + "grookey": "Grookey", + "thwackey": "Thwackey", + "rillaboom": "Rillaboom", + "scorbunny": "Scorbunny", + "raboot": "Raboot", + "cinderace": "Cinderace", + "sobble": "Sobble", + "drizzile": "Drizzile", + "inteleon": "Inteleon", + "skwovet": "Skwovet", + "greedent": "Greedent", + "rookidee": "Rookidee", + "corvisquire": "Corvisquire", + "corviknight": "Corviknight", + "blipbug": "Blipbug", + "dottler": "Dottler", + "orbeetle": "Orbeetle", + "nickit": "Nickit", + "thievul": "Thievul", + "gossifleur": "Gossifleur", + "eldegoss": "Eldegoss", + "wooloo": "Wooloo", + "dubwool": "Dubwool", + "chewtle": "Chewtle", + "drednaw": "Drednaw", + "yamper": "Yamper", + "boltund": "Boltund", + "rolycoly": "Rolycoly", + "carkol": "Carkol", + "coalossal": "Coalossal", + "applin": "Applin", + "flapple": "Flapple", + "appletun": "Appletun", + "silicobra": "Silicobra", + "sandaconda": "Sandaconda", + "cramorant": "Cramorant", + "arrokuda": "Arrokuda", + "barraskewda": "Barraskewda", + "toxel": "Toxel", + "toxtricity": "Toxtricity", + "sizzlipede": "Sizzlipede", + "centiskorch": "Centiskorch", + "clobbopus": "Clobbopus", + "grapploct": "Grapploct", + "sinistea": "Sinistea", + "polteageist": "Polteageist", + "hatenna": "Hatenna", + "hattrem": "Hattrem", + "hatterene": "Hatterene", + "impidimp": "Impidimp", + "morgrem": "Morgrem", + "grimmsnarl": "Grimmsnarl", + "obstagoon": "Obstagoon", + "perrserker": "Perrserker", + "cursola": "Cursola", + "sirfetchd": "Sirfetch'd", + "mr_rime": "Mr. Rime", + "runerigus": "Runerigus", + "milcery": "Milcery", + "alcremie": "Alcremie", + "falinks": "Falinks", + "pincurchin": "Pincurchin", + "snom": "Snom", + "frosmoth": "Frosmoth", + "stonjourner": "Stonjourner", + "eiscue": "Eiscue", + "indeedee": "Indeedee", + "morpeko": "Morpeko", + "cufant": "Cufant", + "copperajah": "Copperajah", + "dracozolt": "Dracozolt", + "arctozolt": "Arctozolt", + "dracovish": "Dracovish", + "arctovish": "Arctovish", + "duraludon": "Duraludon", + "dreepy": "Dreepy", + "drakloak": "Drakloak", + "dragapult": "Dragapult", + "zacian": "Zacian", + "zamazenta": "Zamazenta", + "eternatus": "Eternatus", + "kubfu": "Kubfu", + "urshifu": "Urshifu", + "zarude": "Zarude", + "regieleki": "Regieleki", + "regidrago": "Regidrago", + "glastrier": "Glastrier", + "spectrier": "Spectrier", + "calyrex": "Calyrex", + "wyrdeer": "Wyrdeer", + "kleavor": "Kleavor", + "ursaluna": "Ursaluna", + "basculegion": "Basculegion", + "sneasler": "Sneasler", + "overqwil": "Overqwil", + "enamorus": "Enamorus", + "sprigatito": "Sprigatito", + "floragato": "Floragato", + "meowscarada": "Meowscarada", + "fuecoco": "Fuecoco", + "crocalor": "Crocalor", + "skeledirge": "Skeledirge", + "quaxly": "Quaxly", + "quaxwell": "Quaxwell", + "quaquaval": "Quaquaval", + "lechonk": "Lechonk", + "oinkologne": "Oinkologne", + "tarountula": "Tarountula", + "spidops": "Spidops", + "nymble": "Nymble", + "lokix": "Lokix", + "pawmi": "Pawmi", + "pawmo": "Pawmo", + "pawmot": "Pawmot", + "tandemaus": "Tandemaus", + "maushold": "Maushold", + "fidough": "Fidough", + "dachsbun": "Dachsbun", + "smoliv": "Smoliv", + "dolliv": "Dolliv", + "arboliva": "Arboliva", + "squawkabilly": "Squawkabilly", + "nacli": "Nacli", + "naclstack": "Naclstack", + "garganacl": "Garganacl", + "charcadet": "Charcadet", + "armarouge": "Armarouge", + "ceruledge": "Ceruledge", + "tadbulb": "Tadbulb", + "bellibolt": "Bellibolt", + "wattrel": "Wattrel", + "kilowattrel": "Kilowattrel", + "maschiff": "Maschiff", + "mabosstiff": "Mabosstiff", + "shroodle": "Shroodle", + "grafaiai": "Grafaiai", + "bramblin": "Bramblin", + "brambleghast": "Brambleghast", + "toedscool": "Toedscool", + "toedscruel": "Toedscruel", + "klawf": "Klawf", + "capsakid": "Capsakid", + "scovillain": "Scovillain", + "rellor": "Rellor", + "rabsca": "Rabsca", + "flittle": "Flittle", + "espathra": "Espathra", + "tinkatink": "Tinkatink", + "tinkatuff": "Tinkatuff", + "tinkaton": "Tinkaton", + "wiglett": "Wiglett", + "wugtrio": "Wugtrio", + "bombirdier": "Bombirdier", + "finizen": "Finizen", + "palafin": "Palafin", + "varoom": "Varoom", + "revavroom": "Revavroom", + "cyclizar": "Cyclizar", + "orthworm": "Orthworm", + "glimmet": "Glimmet", + "glimmora": "Glimmora", + "greavard": "Greavard", + "houndstone": "Houndstone", + "flamigo": "Flamigo", + "cetoddle": "Cetoddle", + "cetitan": "Cetitan", + "veluza": "Veluza", + "dondozo": "Dondozo", + "tatsugiri": "Tatsugiri", + "annihilape": "Annihilape", + "clodsire": "Clodsire", + "farigiraf": "Farigiraf", + "dudunsparce": "Dudunsparce", + "kingambit": "Kingambit", + "great_tusk": "Great Tusk", + "scream_tail": "Scream Tail", + "brute_bonnet": "Brute Bonnet", + "flutter_mane": "Flutter Mane", + "slither_wing": "Slither Wing", + "sandy_shocks": "Sandy Shocks", + "iron_treads": "Iron Treads", + "iron_bundle": "Iron Bundle", + "iron_hands": "Iron Hands", + "iron_jugulis": "Iron Jugulis", + "iron_moth": "Iron Moth", + "iron_thorns": "Iron Thorns", + "frigibax": "Frigibax", + "arctibax": "Arctibax", + "baxcalibur": "Baxcalibur", + "gimmighoul": "Gimmighoul", + "gholdengo": "Gholdengo", + "wo_chien": "Wo-Chien", + "chien_pao": "Chien-Pao", + "ting_lu": "Ting-Lu", + "chi_yu": "Chi-Yu", + "roaring_moon": "Roaring Moon", + "iron_valiant": "Iron Valiant", + "koraidon": "Koraidon", + "miraidon": "Miraidon", + "walking_wake": "Walking Wake", + "iron_leaves": "Iron Leaves", + "dipplin": "Dipplin", + "poltchageist": "Poltchageist", + "sinistcha": "Sinistcha", + "okidogi": "Okidogi", + "munkidori": "Munkidori", + "fezandipiti": "Fezandipiti", + "ogerpon": "Ogerpon", + "archaludon": "Archaludon", + "hydrapple": "Hydrapple", + "gouging_fire": "Gouging Fire", + "raging_bolt": "Raging Bolt", + "iron_boulder": "Iron Boulder", + "iron_crown": "Iron Crown", + "terapagos": "Terapagos", + "pecharunt": "Pecharunt", + "alola_rattata": "Rattata", + "alola_raticate": "Raticate", + "alola_raichu": "Raichu", + "alola_sandshrew": "Sandshrew", + "alola_sandslash": "Sandslash", + "alola_vulpix": "Vulpix", + "alola_ninetales": "Ninetales", + "alola_diglett": "Diglett", + "alola_dugtrio": "Dugtrio", + "alola_meowth": "Meowth", + "alola_persian": "Persian", + "alola_geodude": "Geodude", + "alola_graveler": "Graveler", + "alola_golem": "Golem", + "alola_grimer": "Grimer", + "alola_muk": "Muk", + "alola_exeggutor": "Exeggutor", + "alola_marowak": "Marowak", + "eternal_floette": "Floette", + "galar_meowth": "Meowth", + "galar_ponyta": "Ponyta", + "galar_rapidash": "Rapidash", + "galar_slowpoke": "Slowpoke", + "galar_slowbro": "Slowbro", + "galar_farfetchd": "Farfetch'd", + "galar_weezing": "Weezing", + "galar_mr_mime": "Mr. Mime", + "galar_articuno": "Articuno", + "galar_zapdos": "Zapdos", + "galar_moltres": "Moltres", + "galar_slowking": "Slowking", + "galar_corsola": "Corsola", + "galar_zigzagoon": "Zigzagoon", + "galar_linoone": "Linoone", + "galar_darumaka": "Darumaka", + "galar_darmanitan": "Darmanitan", + "galar_yamask": "Yamask", + "galar_stunfisk": "Stunfisk", + "hisui_growlithe": "Growlithe", + "hisui_arcanine": "Arcanine", + "hisui_voltorb": "Voltorb", + "hisui_electrode": "Electrode", + "hisui_typhlosion": "Typhlosion", + "hisui_qwilfish": "Qwilfish", + "hisui_sneasel": "Sneasel", + "hisui_samurott": "Samurott", + "hisui_lilligant": "Lilligant", + "hisui_zorua": "Zorua", + "hisui_zoroark": "Zoroark", + "hisui_braviary": "Braviary", + "hisui_sliggoo": "Sliggoo", + "hisui_goodra": "Goodra", + "hisui_avalugg": "Avalugg", + "hisui_decidueye": "Decidueye", + "paldea_tauros": "Tauros", + "paldea_wooper": "Wooper", + "bloodmoon_ursaluna": "Ursaluna", +} as const; diff --git a/src/locales/ca-ES/save-slot-select-ui-handler.ts b/src/locales/ca-ES/save-slot-select-ui-handler.ts new file mode 100644 index 00000000000..f4efa3de734 --- /dev/null +++ b/src/locales/ca-ES/save-slot-select-ui-handler.ts @@ -0,0 +1,9 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const saveSlotSelectUiHandler: SimpleTranslationEntries = { + "overwriteData": "Overwrite the data in the selected slot?", + "loading": "Loading...", + "wave": "Wave", + "lv": "Lv", + "empty": "Empty", +} as const; diff --git a/src/locales/ca-ES/settings.ts b/src/locales/ca-ES/settings.ts new file mode 100644 index 00000000000..491bfa4a481 --- /dev/null +++ b/src/locales/ca-ES/settings.ts @@ -0,0 +1,100 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales.js"; + +export const settings: SimpleTranslationEntries = { + "boy": "Boy", + "girl": "Girl", + "general": "General", + "display": "Display", + "audio": "Audio", + "gamepad": "Gamepad", + "keyboard": "Keyboard", + "gameSpeed": "Game Speed", + "hpBarSpeed": "HP Bar Speed", + "expGainsSpeed": "EXP Gains Speed", + "expPartyDisplay": "Show EXP Party", + "skipSeenDialogues": "Skip Seen Dialogues", + "battleStyle": "Battle Style", + "enableRetries": "Enable Retries", + "tutorials": "Tutorials", + "touchControls": "Touch Controls", + "vibrations": "Vibrations", + "normal": "Normal", + "fast": "Fast", + "faster": "Faster", + "skip": "Skip", + "levelUpNotifications": "Level Up Notifications", + "on": "On", + "off": "Off", + "switch": "Switch", + "set": "Set", + "auto": "Auto", + "disabled": "Disabled", + "language": "Language", + "change": "Change", + "uiTheme": "UI Theme", + "default": "Default", + "legacy": "Legacy", + "windowType": "Window Type", + "moneyFormat": "Money Format", + "damageNumbers": "Damage Numbers", + "simple": "Simple", + "fancy": "Fancy", + "abbreviated": "Abbreviated", + "moveAnimations": "Move Animations", + "showStatsOnLevelUp": "Show Stats on Level Up", + "candyUpgradeNotification": "Candy Upgrade Notification", + "passivesOnly": "Passives Only", + "candyUpgradeDisplay": "Candy Upgrade Display", + "icon": "Icon", + "animation": "Animation", + "moveInfo": "Move Info", + "showMovesetFlyout": "Show Moveset Flyout", + "showArenaFlyout": "Show Arena Flyout", + "showTimeOfDayWidget": "Show Time of Day Widget", + "timeOfDayAnimation": "Time of Day Animation", + "bounce": "Bounce", + "timeOfDay_back": "Back", + "spriteSet": "Sprite Set", + "consistent": "Consistent", + "mixedAnimated": "Mixed Animated", + "fusionPaletteSwaps": "Fusion Palette Swaps", + "playerGender": "Player Gender", + "typeHints": "Type Hints", + "masterVolume": "Master Volume", + "bgmVolume": "BGM Volume", + "seVolume": "SE Volume", + "musicPreference": "Music Preference", + "mixed": "Mixed", + "gamepadPleasePlug": "Please Plug in a Gamepad or Press a Button", + "delete": "Delete", + "keyboardPleasePress": "Please Press a Key on Your Keyboard", + "reset": "Reset", + "requireReload": "Reload Required", + "action": "Action", + "back": "Back", + "pressToBind": "Press to Bind", + "pressButton": "Press a Button...", + "buttonUp": "Up", + "buttonDown": "Down", + "buttonLeft": "Left", + "buttonRight": "Right", + "buttonAction": "Action", + "buttonMenu": "Menu", + "buttonSubmit": "Submit", + "buttonCancel": "Cancel", + "buttonStats": "Stats", + "buttonCycleForm": "Cycle Form", + "buttonCycleShiny": "Cycle Shiny", + "buttonCycleGender": "Cycle Gender", + "buttonCycleAbility": "Cycle Ability", + "buttonCycleNature": "Cycle Nature", + "buttonCycleVariant": "Cycle Variant", + "buttonSpeedUp": "Speed Up", + "buttonSlowDown": "Slow Down", + "alt": " (Alt)", + "mute": "Mute", + "controller": "Controller", + "gamepadSupport": "Gamepad Support", + "showBgmBar": "Show Music Names", + "shopOverlayOpacity": "Shop Overlay Opacity" +} as const; diff --git a/src/locales/ca-ES/splash-messages.ts b/src/locales/ca-ES/splash-messages.ts new file mode 100644 index 00000000000..e549bc24f19 --- /dev/null +++ b/src/locales/ca-ES/splash-messages.ts @@ -0,0 +1,38 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const splashMessages: SimpleTranslationEntries = { + "battlesWon": "Battles Won!", + "joinTheDiscord": "Join the Discord!", + "infiniteLevels": "Infinite Levels!", + "everythingStacks": "Everything Stacks!", + "optionalSaveScumming": "Optional Save Scumming!", + "biomes": "35 Biomes!", + "openSource": "Open Source!", + "playWithSpeed": "Play with 5x Speed!", + "liveBugTesting": "Live Bug Testing!", + "heavyInfluence": "Heavy RoR2 Influence!", + "pokemonRiskAndPokemonRain": "Pokémon Risk and Pokémon Rain!", + "nowWithMoreSalt": "Now with 33% More Salt!", + "infiniteFusionAtHome": "Infinite Fusion at Home!", + "brokenEggMoves": "Broken Egg Moves!", + "magnificent": "Magnificent!", + "mubstitute": "Mubstitute!", + "thatsCrazy": "That's Crazy!", + "oranceJuice": "Orance Juice!", + "questionableBalancing": "Questionable Balancing!", + "coolShaders": "Cool Shaders!", + "aiFree": "AI-Free!", + "suddenDifficultySpikes": "Sudden Difficulty Spikes!", + "basedOnAnUnfinishedFlashGame": "Based on an Unfinished Flash Game!", + "moreAddictiveThanIntended": "More Addictive than Intended!", + "mostlyConsistentSeeds": "Mostly Consistent Seeds!", + "achievementPointsDontDoAnything": "Achievement Points Don't Do Anything!", + "youDoNotStartAtLevel": "You Do Not Start at Level 2000!", + "dontTalkAboutTheManaphyEggIncident": "Don't Talk About the Manaphy Egg Incident!", + "alsoTryPokengine": "Also Try Pokéngine!", + "alsoTryEmeraldRogue": "Also Try Emerald Rogue!", + "alsoTryRadicalRed": "Also Try Radical Red!", + "eeveeExpo": "Eevee Expo!", + "ynoproject": "YNOproject!", + "breedersInSpace": "Breeders in space!", +} as const; diff --git a/src/locales/ca-ES/starter-select-ui-handler.ts b/src/locales/ca-ES/starter-select-ui-handler.ts new file mode 100644 index 00000000000..deb4236c8ba --- /dev/null +++ b/src/locales/ca-ES/starter-select-ui-handler.ts @@ -0,0 +1,49 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +/** + * The menu namespace holds most miscellaneous text that isn't directly part of the game's + * contents or directly related to Pokemon data. This includes menu navigation, settings, + * account interactions, descriptive text, etc. + */ +export const starterSelectUiHandler: SimpleTranslationEntries = { + "confirmStartTeam": "Begin with these Pokémon?", + "confirmExit": "Do you want to exit?", + "invalidParty": "This is not a valid starting party!", + "gen1": "I", + "gen2": "II", + "gen3": "III", + "gen4": "IV", + "gen5": "V", + "gen6": "VI", + "gen7": "VII", + "gen8": "VIII", + "gen9": "IX", + "growthRate": "Growth Rate:", + "ability": "Ability:", + "passive": "Passive:", + "nature": "Nature:", + "eggMoves": "Egg Moves", + "addToParty": "Add to Party", + "removeFromParty": "Remove from Party", + "toggleIVs": "Toggle IVs", + "manageMoves": "Manage Moves", + "manageNature": "Manage Nature", + "useCandies": "Use Candies", + "selectNature": "Select nature.", + "selectMoveSwapOut": "Select a move to swap out.", + "selectMoveSwapWith": "Select a move to swap with", + "unlockPassive": "Unlock Passive", + "reduceCost": "Reduce Cost", + "sameSpeciesEgg": "Buy an Egg", + "cycleShiny": ": Shiny", + "cycleForm": ": Form", + "cycleGender": ": Gender", + "cycleAbility": ": Ability", + "cycleNature": ": Nature", + "cycleVariant": ": Variant", + "enablePassive": "Enable Passive", + "disablePassive": "Disable Passive", + "locked": "Locked", + "disabled": "Disabled", + "uncaught": "Uncaught" +}; diff --git a/src/locales/ca-ES/status-effect.ts b/src/locales/ca-ES/status-effect.ts new file mode 100644 index 00000000000..5914fc27298 --- /dev/null +++ b/src/locales/ca-ES/status-effect.ts @@ -0,0 +1,67 @@ +import { StatusEffectTranslationEntries } from "#app/interfaces/locales.js"; + +export const statusEffect: StatusEffectTranslationEntries = { + none: { + name: "None", + description: "", + obtain: "", + obtainSource: "", + activation: "", + overlap: "", + heal: "" + }, + poison: { + name: "Poison", + description: "poisoning", + obtain: "{{pokemonNameWithAffix}}\nwas poisoned!", + obtainSource: "{{pokemonNameWithAffix}}\nwas poisoned by the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is hurt\nby poison!", + overlap: "{{pokemonNameWithAffix}} is\nalready poisoned!", + heal: "{{pokemonNameWithAffix}} was\ncured of its poison!" + }, + toxic: { + name: "Toxic", + description: "poisoning", + obtain: "{{pokemonNameWithAffix}}\nwas badly poisoned!", + obtainSource: "{{pokemonNameWithAffix}}\nwas badly poisoned by the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is hurt\nby poison!", + overlap: "{{pokemonNameWithAffix}} is\nalready poisoned!", + heal: "{{pokemonNameWithAffix}} was\ncured of its poison!" + }, + paralysis: { + name: "Paralysis", + description: "paralysis", + obtain: "{{pokemonNameWithAffix}} was paralyzed,\nIt may be unable to move!", + obtainSource: "{{pokemonNameWithAffix}} was paralyzed by the {{sourceText}}!\nIt may be unable to move!", + activation: "{{pokemonNameWithAffix}} is paralyzed!\nIt can't move!", + overlap: "{{pokemonNameWithAffix}} is\nalready paralyzed!", + heal: "{{pokemonNameWithAffix}} was\nhealed of paralysis!" + }, + sleep: { + name: "Sleep", + description: "sleep", + obtain: "{{pokemonNameWithAffix}}\nfell asleep!", + obtainSource: "{{pokemonNameWithAffix}}\nfell asleep from the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is fast asleep.", + overlap: "{{pokemonNameWithAffix}} is\nalready asleep!", + heal: "{{pokemonNameWithAffix}} woke up!" + }, + freeze: { + name: "Freeze", + description: "freezing", + obtain: "{{pokemonNameWithAffix}}\nwas frozen solid!", + obtainSource: "{{pokemonNameWithAffix}}\nwas frozen solid by the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is\nfrozen solid!", + overlap: "{{pokemonNameWithAffix}} is\nalready frozen!", + heal: "{{pokemonNameWithAffix}} was\ndefrosted!" + }, + burn: { + name: "Burn", + description: "burn", + obtain: "{{pokemonNameWithAffix}}\nwas burned!", + obtainSource: "{{pokemonNameWithAffix}}\nwas burned by the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is hurt\nby its burn!", + overlap: "{{pokemonNameWithAffix}} is\nalready burned!", + heal: "{{pokemonNameWithAffix}} was\nhealed of its burn!" + }, +} as const; diff --git a/src/locales/ca-ES/trainers.ts b/src/locales/ca-ES/trainers.ts new file mode 100644 index 00000000000..a40fabaeacc --- /dev/null +++ b/src/locales/ca-ES/trainers.ts @@ -0,0 +1,322 @@ +import {SimpleTranslationEntries} from "#app/interfaces/locales"; + +// Titles of special trainers like gym leaders, elite four, and the champion +export const titles: SimpleTranslationEntries = { + "elite_four": "Elite Four", + "elite_four_female": "Elite Four", + "gym_leader": "Gym Leader", + "gym_leader_female": "Gym Leader", + "gym_leader_double": "Gym Leader Duo", + "champion": "Champion", + "champion_female": "Champion", + "champion_double": "Champion Duo", + "rival": "Rival", + "professor": "Professor", + "frontier_brain": "Frontier Brain", + "rocket_boss": "Team Rocket Boss", + "magma_boss": "Team Magma Boss", + "aqua_boss": "Team Aqua Boss", + "galactic_boss": "Team Galactic Boss", + "plasma_boss": "Team Plasma Boss", + "flare_boss": "Team Flare Boss", + + "rocket_admin": "Team Rocket Admin", + "rocket_admin_female": "Team Rocket Admin", + "magma_admin": "Team Magma Admin", + "magma_admin_female": "Team Magma Admin", + "aqua_admin": "Team Aqua Admin", + "aqua_admin_female": "Team Aqua Admin", + "galactic_commander": "Team Galactic Commander", + "galactic_commander_female": "Team Galactic Commander", + "plasma_sage": "Team Plasma Sage", + "plasma_admin": "Team Plasma Admin", + "flare_admin": "Team Flare Admin", + "flare_admin_female": "Team Flare Admin", + // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. +} as const; + +// Titles of trainers like "Youngster" or "Lass" +export const trainerClasses: SimpleTranslationEntries = { + "ace_trainer": "Ace Trainer", + "ace_trainer_female": "Ace Trainer", + "ace_duo": "Ace Duo", + "artist": "Artist", + "artist_female": "Artist", + "backers": "Backers", + "backpacker": "Backpacker", + "backpacker_female": "Backpacker", + "backpackers": "Backpackers", + "baker": "Baker", + "battle_girl": "Battle Girl", + "beauty": "Beauty", + "beginners": "Beginners", + "biker": "Biker", + "black_belt": "Black Belt", + "breeder": "Breeder", + "breeder_female": "Breeder", + "breeders": "Breeders", + "clerk": "Clerk", + "clerk_female": "Clerk", + "colleagues": "Colleagues", + "crush_kin": "Crush Kin", + "cyclist": "Cyclist", + "cyclist_female": "Cyclist", + "cyclists": "Cyclists", + "dancer": "Dancer", + "dancer_female": "Dancer", + "depot_agent": "Depot Agent", + "doctor": "Doctor", + "doctor_female": "Doctor", + "firebreather": "Firebreather", + "fisherman": "Fisherman", + "fisherman_female": "Fisherman", + "gentleman": "Gentleman", + "guitarist": "Guitarist", + "guitarist_female": "Guitarist", + "harlequin": "Harlequin", + "hiker": "Hiker", + "hooligans": "Hooligans", + "hoopster": "Hoopster", + "infielder": "Infielder", + "janitor": "Janitor", + "lady": "Lady", + "lass": "Lass", + "linebacker": "Linebacker", + "maid": "Maid", + "madame": "Madame", + "medical_team": "Medical Team", + "musician": "Musician", + "hex_maniac": "Hex Maniac", + "nurse": "Nurse", + "nursery_aide": "Nursery Aide", + "officer": "Officer", + "parasol_lady": "Parasol Lady", + "pilot": "Pilot", + "pokéfan": "Poké Fan", + "pokéfan_female": "Poké Fan", + "pokéfan_family": "Poké Fan Family", + "preschooler": "Preschooler", + "preschooler_female": "Preschooler", + "preschoolers": "Preschoolers", + "psychic": "Psychic", + "psychic_female": "Psychic", + "psychics": "Psychics", + "pokémon_ranger": "Pokémon Ranger", + "pokémon_ranger_female": "Pokémon Ranger", + "pokémon_rangers": "Pokémon Ranger", + "ranger": "Ranger", + "restaurant_staff": "Restaurant Staff", + "rich": "Rich", + "rich_female": "Rich", + "rich_boy": "Rich Boy", + "rich_couple": "Rich Couple", + "rich_kid": "Rich Kid", + "rich_kid_female": "Rich Kid", + "rich_kids": "Rich Kids", + "roughneck": "Roughneck", + "sailor": "Sailor", + "scientist": "Scientist", + "scientist_female": "Scientist", + "scientists": "Scientists", + "smasher": "Smasher", + "snow_worker": "Snow Worker", + "snow_worker_female": "Snow Worker", + "striker": "Striker", + "school_kid": "School Kid", + "school_kid_female": "School Kid", + "school_kids": "School Kids", + "swimmer": "Swimmer", + "swimmer_female": "Swimmer", + "swimmers": "Swimmers", + "twins": "Twins", + "veteran": "Veteran", + "veteran_female": "Veteran", + "veteran_duo": "Veteran Duo", + "waiter": "Waiter", + "waitress": "Waitress", + "worker": "Worker", + "worker_female": "Worker", + "workers": "Workers", + "youngster": "Youngster", + "rocket_grunt": "Rocket Grunt", + "rocket_grunts": "Rocket Grunts", + "rocket_grunt_female": "Rocket Grunt", + "magma_grunt": "Magma Grunt", + "magma_grunt_female": "Magma Grunt", + "magma_grunts": "Magma Grunts", + "aqua_grunt": "Aqua Grunt", + "aqua_grunt_female": "Aqua Grunt", + "aqua_grunts": "Aqua Grunts", + "galactic_grunt": "Galactic Grunt", + "galactic_grunt_female": "Galactic Grunt", + "galactic_grunts": "Galactic Grunts", + "plasma_grunt": "Plasma Grunt", + "plasma_grunt_female": "Plasma Grunt", + "plasma_grunts": "Plasma Grunts", + "flare_grunt": "Flare Grunt", + "flare_grunt_female": "Flare Grunt", + "flare_grunts": "Flare Grunts", +} as const; + +// Names of special trainers like gym leaders, elite four, and the champion +export const trainerNames: SimpleTranslationEntries = { + "brock": "Brock", + "misty": "Misty", + "lt_surge": "Lt Surge", + "erika": "Erika", + "janine": "Janine", + "sabrina": "Sabrina", + "blaine": "Blaine", + "giovanni": "Giovanni", + "falkner": "Falkner", + "bugsy": "Bugsy", + "whitney": "Whitney", + "morty": "Morty", + "chuck": "Chuck", + "jasmine": "Jasmine", + "pryce": "Pryce", + "clair": "Clair", + "roxanne": "Roxanne", + "brawly": "Brawly", + "wattson": "Wattson", + "flannery": "Flannery", + "norman": "Norman", + "winona": "Winona", + "tate": "Tate", + "liza": "Liza", + "juan": "Juan", + "roark": "Roark", + "gardenia": "Gardenia", + "maylene": "Maylene", + "crasher_wake": "Crasher Wake", + "fantina": "Fantina", + "byron": "Byron", + "candice": "Candice", + "volkner": "Volkner", + "cilan": "Cilan", + "chili": "Chili", + "cress": "Cress", + "cheren": "Cheren", + "lenora": "Lenora", + "roxie": "Roxie", + "burgh": "Burgh", + "elesa": "Elesa", + "clay": "Clay", + "skyla": "Skyla", + "brycen": "Brycen", + "drayden": "Drayden", + "marlon": "Marlon", + "viola": "Viola", + "grant": "Grant", + "korrina": "Korrina", + "ramos": "Ramos", + "clemont": "Clemont", + "valerie": "Valerie", + "olympia": "Olympia", + "wulfric": "Wulfric", + "milo": "Milo", + "nessa": "Nessa", + "kabu": "Kabu", + "bea": "Bea", + "allister": "Allister", + "opal": "Opal", + "bede": "Bede", + "gordie": "Gordie", + "melony": "Melony", + "piers": "Piers", + "marnie": "Marnie", + "raihan": "Raihan", + "katy": "Katy", + "brassius": "Brassius", + "iono": "Iono", + "kofu": "Kofu", + "larry": "Larry", + "ryme": "Ryme", + "tulip": "Tulip", + "grusha": "Grusha", + "lorelei": "Lorelei", + "bruno": "Bruno", + "agatha": "Agatha", + "lance": "Lance", + "will": "Will", + "koga": "Koga", + "karen": "Karen", + "sidney": "Sidney", + "phoebe": "Phoebe", + "glacia": "Glacia", + "drake": "Drake", + "aaron": "Aaron", + "bertha": "Bertha", + "flint": "Flint", + "lucian": "Lucian", + "shauntal": "Shauntal", + "marshal": "Marshal", + "grimsley": "Grimsley", + "caitlin": "Caitlin", + "malva": "Malva", + "siebold": "Siebold", + "wikstrom": "Wikstrom", + "drasna": "Drasna", + "hala": "Hala", + "molayne": "Molayne", + "olivia": "Olivia", + "acerola": "Acerola", + "kahili": "Kahili", + "rika": "Rika", + "poppy": "Poppy", + "hassel": "Hassel", + "crispin": "Crispin", + "amarys": "Amarys", + "lacey": "Lacey", + "drayton": "Drayton", + "blue": "Blue", + "red": "Red", + "steven": "Steven", + "wallace": "Wallace", + "cynthia": "Cynthia", + "alder": "Alder", + "iris": "Iris", + "diantha": "Diantha", + "hau": "Hau", + "geeta": "Geeta", + "nemona": "Nemona", + "kieran": "Kieran", + "leon": "Leon", + "rival": "Finn", + "rival_female": "Ivy", + + // Evil Team Admins + "archer": "Archer", + "ariana": "Ariana", + "proton": "Proton", + "petrel": "Petrel", + "tabitha": "Tabitha", + "courtney": "Courtney", + "shelly": "Shelly", + "matt": "Matt", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturn", + "zinzolin": "Zinzolin", + "rood": "Rood", + "xerosic": "Xerosic", + "bryony": "Bryony", + + "maxie": "Maxie", + "archie": "Archie", + "cyrus": "Cyrus", + "ghetsis": "Ghetsis", + "lysandre": "Lysandre", + + // Double Names + "blue_red_double": "Blue & Red", + "red_blue_double": "Red & Blue", + "tate_liza_double": "Tate & Liza", + "liza_tate_double": "Liza & Tate", + "steven_wallace_double": "Steven & Wallace", + "wallace_steven_double": "Wallace & Steven", + "alder_iris_double": "Alder & Iris", + "iris_alder_double": "Iris & Alder", + "marnie_piers_double": "Marnie & Piers", + "piers_marnie_double": "Piers & Marnie", +} as const; diff --git a/src/locales/ca-ES/tutorial.ts b/src/locales/ca-ES/tutorial.ts new file mode 100644 index 00000000000..3c4aa2b46f6 --- /dev/null +++ b/src/locales/ca-ES/tutorial.ts @@ -0,0 +1,44 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const tutorial: SimpleTranslationEntries = { + "intro": `Welcome to PokéRogue! This is a battle-focused Pokémon fangame with roguelite elements. + $This game is not monetized and we claim no ownership of Pokémon nor of the copyrighted assets used. + $The game is a work in progress, but fully playable.\nFor bug reports, please use the Discord community. + $If the game runs slowly, please ensure 'Hardware Acceleration' is turned on in your browser settings.`, + + "accessMenu": "To access the menu, press M or Escape while awaiting input.\nThe menu contains settings and various features.", + + "menu": `From this menu you can access the settings. + $From the settings you can change game speed, window style, and other options. + $There are also various other features here, so be sure to check them all!`, + + "starterSelect": `From this screen, you can select your starters by pressing\nZ or the Space bar. These are your initial party members. + $Each starter has a value. Your party can have up to\n6 members as long as the total does not exceed 10. + $You can also select gender, ability, and form depending on\nthe variants you've caught or hatched. + $The IVs for a species are also the best of every one you've\ncaught or hatched, so try to get lots of the same species!`, + + "pokerus": `A daily random 3 selectable starters have a purple border. + $If you see a starter you own with one of these,\ntry adding it to your party. Be sure to check its summary!`, + + "statChange": `Stat changes persist across battles as long as your Pokémon aren't recalled. + $Your Pokémon are recalled before a trainer battle and before entering a new biome. + $You can view the stat changes for any Pokémon on the field by holding C or Shift. + $You can also view the moveset for an enemy Pokémon by holding V. + $This only reveals moves that you've seen the Pokémon use this battle.`, + + "selectItem": `After every battle, you are given a choice of 3 random items.\nYou may only pick one. + $These range from consumables, to Pokémon held items, to passive permanent items. + $Most non-consumable item effects will stack in various ways. + $Some items will only show up if they can be used, such as evolution items. + $You can also transfer held items between Pokémon using the transfer option. + $The transfer option will appear in the bottom right once you have obtained a held item. + $You may purchase consumable items with money, and a larger variety will be available the further you get. + $Be sure to buy these before you pick your random item, as it will progress to the next battle once you do.`, + + "eggGacha": `From this screen, you can redeem your vouchers for\nPokémon eggs. + $Eggs have to be hatched and get closer to hatching after\nevery battle. Rarer eggs take longer to hatch. + $Hatched Pokémon also won't be added to your party, they will\nbe added to your starters. + $Pokémon hatched from eggs generally have better IVs than\nwild Pokémon. + $Some Pokémon can only even be obtained from eggs. + $There are 3 different machines to pull from with different\nbonuses, so pick the one that suits you best!`, +} as const; diff --git a/src/locales/ca-ES/voucher.ts b/src/locales/ca-ES/voucher.ts new file mode 100644 index 00000000000..b2894711020 --- /dev/null +++ b/src/locales/ca-ES/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "Vouchers", + "eggVoucher": "Egg Voucher", + "eggVoucherPlus": "Egg Voucher Plus", + "eggVoucherPremium": "Egg Voucher Premium", + "eggVoucherGold": "Egg Voucher Gold", + "locked": "Locked", + "defeatTrainer": "Defeat {{trainerName}}" +} as const; diff --git a/src/locales/ca-ES/weather.ts b/src/locales/ca-ES/weather.ts new file mode 100644 index 00000000000..8222064f341 --- /dev/null +++ b/src/locales/ca-ES/weather.ts @@ -0,0 +1,66 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +/** + * The weather namespace holds text displayed when weather is active during a battle + */ +export const weather: SimpleTranslationEntries = { + "sunnyStartMessage": "The sunlight got bright!", + "sunnyLapseMessage": "The sunlight is strong.", + "sunnyClearMessage": "The sunlight faded.", + + "rainStartMessage": "A downpour started!", + "rainLapseMessage": "The downpour continues.", + "rainClearMessage": "The rain stopped.", + + "sandstormStartMessage": "A sandstorm brewed!", + "sandstormLapseMessage": "The sandstorm rages.", + "sandstormClearMessage": "The sandstorm subsided.", + "sandstormDamageMessage": "{{pokemonNameWithAffix}} is buffeted\nby the sandstorm!", + + "hailStartMessage": "It started to hail!", + "hailLapseMessage": "Hail continues to fall.", + "hailClearMessage": "The hail stopped.", + "hailDamageMessage": "{{pokemonNameWithAffix}} is pelted\nby the hail!", + + "snowStartMessage": "It started to snow!", + "snowLapseMessage": "The snow is falling down.", + "snowClearMessage": "The snow stopped.", + + "fogStartMessage": "A thick fog emerged!", + "fogLapseMessage": "The fog continues.", + "fogClearMessage": "The fog disappeared.", + + "heavyRainStartMessage": "A heavy downpour started!", + "heavyRainLapseMessage": "The heavy downpour continues.", + "heavyRainClearMessage": "The heavy rain stopped.", + + "harshSunStartMessage": "The sunlight got hot!", + "harshSunLapseMessage": "The sun is scorching hot.", + "harshSunClearMessage": "The harsh sunlight faded.", + + "strongWindsStartMessage": "A heavy wind began!", + "strongWindsLapseMessage": "The wind blows intensely.", + "strongWindsEffectMessage": "The mysterious air current weakened the attack!", + "strongWindsClearMessage": "The heavy wind stopped." +}; + +export const terrain: SimpleTranslationEntries = { + "misty": "Misty", + "mistyStartMessage": "Mist swirled around the battlefield!", + "mistyClearMessage": "The mist disappeared from the battlefield.", + "mistyBlockMessage": "{{pokemonNameWithAffix}} surrounds itself with a protective mist!", + + "electric": "Electric", + "electricStartMessage": "An electric current ran across the battlefield!", + "electricClearMessage": "The electricity disappeared from the battlefield.", + + "grassy": "Grassy", + "grassyStartMessage": "Grass grew to cover the battlefield!", + "grassyClearMessage": "The grass disappeared from the battlefield.", + + "psychic": "Psychic", + "psychicStartMessage": "The battlefield got weird!", + "psychicClearMessage": "The weirdness disappeared from the battlefield!", + + "defaultBlockMessage": "{{pokemonNameWithAffix}} is protected by the {{terrainName}} Terrain!" +}; diff --git a/src/locales/de/ability-trigger.ts b/src/locales/de/ability-trigger.ts index 5164b294570..ac80160cef6 100644 --- a/src/locales/de/ability-trigger.ts +++ b/src/locales/de/ability-trigger.ts @@ -36,12 +36,12 @@ export const abilityTriggers: SimpleTranslationEntries = { "battlerTagImmunity": "{{abilityName}} von {{pokemonNameWithAffix}} verhindert {{battlerTagName}}!", "forewarn": "Vorwarnung von {{pokemonNameWithAffix}}: Konzentraion auf {{moveName}}!", "frisk": "{{pokemonNameWithAffix}} hat die Fähigkeit {{opponentAbilityName}} von {{opponentName}} erschnüffelt!", - "postWeatherLapseHeal": "{{abilityName}} von {{pokemonName}} füllte einige KP auf!", + "postWeatherLapseHeal": "{{abilityName}} von {{pokemonNameWithAffix}} füllte einige KP auf!", "postWeatherLapseDamage": "{{pokemonNameWithAffix}} wurde durch {{abilityName}} verletzt!", "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}} hat {{berryName}} geerntet!", - "postTurnHeal": "{{abilityName}} von {{pokemonName}} füllte einige KP auf!", + "postTurnHeal": "{{abilityName}} von {{pokemonNameWithAffix}} füllte einige KP auf!", "fetchBall": "{{pokemonNameWithAffix}} hat einen {{pokeballName}} gefunden!", - "healFromBerryUse": "{{abilityName}} von {{pokemonName}} füllte einige KP auf!", + "healFromBerryUse": "{{abilityName}} von {{pokemonNameWithAffix}} füllte einige KP auf!", "arenaTrap": "{{abilityName}} von {{pokemonNameWithAffix}} verhindert den Tausch!", "postBattleLoot": "{{pokemonNameWithAffix}} hebt {{itemName}} auf!", "postFaintContactDamage": "{{abilityName}} von {{pokemonNameWithAffix}} schadet seinem Angreifer!", diff --git a/src/locales/de/achv.ts b/src/locales/de/achv.ts index 6040c3ea916..3d498d048a7 100644 --- a/src/locales/de/achv.ts +++ b/src/locales/de/achv.ts @@ -170,6 +170,11 @@ export const PGMachv: AchievementTranslationEntries = { name: "Ungeschlagen", description: "Beende den klassischen Modus erfolgreich.", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "'Bringe dein Kind mit zur Arbeit'-Tag" , + description: "Beende den klassischen Modus erfolgreich mit mindestens einem nicht entwickeltem Pokémon in deinem Team" + }, + "MONO_GEN_ONE": { name: "Der originale Rivale", description: "Schließe die 'Nur 1. Generation' Herausforderung ab.", @@ -264,6 +269,10 @@ export const PGMachv: AchievementTranslationEntries = { "MONO_FAIRY": { name: "Ein ewiges Abenteuer!", }, + "FRESH_START": { + name: "Hussa, noch einmal von vorn!", + description: "Schließe die 'Neuanfang' Herausforderung ab" + } } as const; // Achievement translations for the when the player character is female @@ -344,6 +353,7 @@ export const PGFachv: AchievementTranslationEntries = { "HIDDEN_ABILITY": PGMachv.HIDDEN_ABILITY, "PERFECT_IVS": PGMachv.PERFECT_IVS, "CLASSIC_VICTORY": PGMachv.CLASSIC_VICTORY, + "UNEVOLVED_CLASSIC_VICTORY": PGMachv.UNEVOLVED_CLASSIC_VICTORY, "MONO_GEN_ONE": PGMachv.MONO_GEN_ONE, "MONO_GEN_TWO": PGMachv.MONO_GEN_TWO, "MONO_GEN_THREE": PGMachv.MONO_GEN_THREE, @@ -373,5 +383,6 @@ export const PGFachv: AchievementTranslationEntries = { "MONO_DRAGON": PGMachv.MONO_DRAGON, "MONO_DARK": PGMachv.MONO_DARK, "MONO_FAIRY": PGMachv.MONO_FAIRY, + "FRESH_START": PGMachv.FRESH_START } as const; diff --git a/src/locales/de/arena-tag.ts b/src/locales/de/arena-tag.ts index cc0a821aade..65699742331 100644 --- a/src/locales/de/arena-tag.ts +++ b/src/locales/de/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "Die Pokémon auf der gegnerischen Seite werden von {{moveName}} behütet!", "conditionalProtectApply": "{{pokemonNameWithAffix}} wird durch {{moveName}} geschützt!", "matBlockOnAdd": "{{pokemonNameWithAffix}} bringt seinen Tatami-Schild in Position!", + "noCritOnAddPlayer": "{{moveName}} schützt dein Team vor Volltreffern!", + "noCritOnAddEnemy": "{{moveName}} schützt das gegnerische Team vor Volltreffern!", + "noCritOnRemove": "{{moveName}} von {{pokemonNameWithAffix}} hört auf zu wirken!", "wishTagOnAdd": "Der Wunschtraum von {{pokemonNameWithAffix}} erfüllt sich!", "mudSportOnAdd": "Die Stärke aller Elektro-Attacken wurde reduziert!", "mudSportOnRemove": "Lehmsuhler hört auf zu wirken!", diff --git a/src/locales/de/bgm-name.ts b/src/locales/de/bgm-name.ts index 2a247e51a44..3877aeea9bd 100644 --- a/src/locales/de/bgm-name.ts +++ b/src/locales/de/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries" : "{{name}}", "battle_kanto_champion": "S2W2 Vs. Kanto Champion", "battle_johto_champion": "S2W2 Vs. Johto Champion", - "battle_hoenn_champion": "S2W2 Vs. Hoenn Champion", + "battle_hoenn_champion_g5": "S2W2 Vs. Hoenn Champion", + "battle_hoenn_champion_g6": "ORAS Vs. Hoenn Champion", "battle_sinnoh_champion": "S2W2 Vs. Champion Cynthia", "battle_champion_alder": "SW Vs. Champion Lauro", "battle_champion_iris": "S2W2 Vs. Champion Lilia", diff --git a/src/locales/de/dialogue.ts b/src/locales/de/dialogue.ts index 6227c2d149d..a84060143fc 100644 --- a/src/locales/de/dialogue.ts +++ b/src/locales/de/dialogue.ts @@ -367,28 +367,211 @@ export const PGMdialogue: DialogueTranslationEntries = { 1: "Ich werde für das nächste Rennen tunen." }, }, - "rocket_grunt": { + "archer": { "encounter": { - 1: `Jetzt gibt es Ärger!… - $und es kommt noch härter! - $Wir wollen über die Erde regieren… - $und naja du kennst den Rest…!` + 1: "Bevor du weitergehst, lass uns sehen, wie du dich gegen uns, Team Rocket, schlägst!", + 2: `Ich habe Berichte erhalten, dass deine Fähigkeiten nicht unbedeutend sind. + $Mal sehen, ob sie wahr sind.`, + 3: `Ich bin Atlas, ein Vorstand von Team Rocket. + $Und ich mache es den Feinden unserer Organisation nicht leicht.` }, "victory": { - 1: "Das war mal wieder ein Schuss in den Ofen!" - }, + 1: "Was für ein Fehler!", + 2: "Mit meinen aktuellen Fähigkeiten war ich der Aufgabe doch nicht gewachsen.", + 3: "V-verzeih mir, Giovanni... Dass ich von einem einfachen Trainer besiegt wurde..." + } }, - "rocket_admin": { + "ariana": { "encounter": { - 1: "Oh? You managed to get this far? You must be quite the trainer.", - 2: "That's quite enough of you playing hero, kid.", - 3: "I'll show you how scary an angry adult can be!" + 1: `Halt! Wir können niemanden herumlaufen lassen. + $Es ist schädlich für den Stolz von Team Rocket, verstehst du.`, + 2: `Ich weiß nicht und es ist mir egal, ob das, was ich tue, richtig oder falsch ist... + $Ich vertraue einfach auf Giovanni und tue, was mir gesagt wird.`, + 3: "Dein Trip endet hier. Ich werde dich erledigen!" }, "victory": { - 1: "No! Forgive me Giovanni!", - 2: "How could this be?", - 3: "Urgh... You were too strong..." + 1: `Tch, du bist wirklich stark. Es ist schade. + $Wenn du Team Rocket beitreten würdest, könntest du ein Vorstand werden.`, + 2: "Ich... ich bin zerstört...", + 3: "Aaaieeeee! Das kann nicht passieren! Ich habe hart gekämpft, aber trotzdem verloren..." + } + }, + "proton": { + "encounter": { + 1: "Was willst du? Wenn du unsere Arbeit unterbrichst, erwarte keine Gnade!", + 2: `Was haben wir hier? Ich werde oft als der gruseligste und grausamste Typ bei Team Rocket bezeichnet… + $Ich rate dir dringend, dich nicht in unsere Geschäfte einzumischen!`, + 3: "Ich bin Proton, ein Admin von Team Rocket. Ich bin hier, um deinem Einmischen ein Ende zu setzen!" }, + "victory": { + 1: "Die Festung ist gefallen! Alle Mann zurückziehen!", + 2: "Du hast diesmal gewonnen… Aber alles, was du getan hast, war, den Zorn von Team Rocket zu vergrößern…", + 3: "Ich bin besiegt… Aber ich werde das nicht vergessen!" + } + }, + "petrel": { + "encounter": { + 1: `Muhahaha, wir haben auf dich gewartet. Ich? Du weißt nicht, wer ich bin? Ich bin Giovanni. + $Der majestätische Giovanni höchstpersönlich! Wahahaha! + $…Huh? Ich klinge überhaupt nicht wie Giovanni? + $Ich sehe nicht einmal aus wie Giovanni? + $Wie kommt das? Ich habe so hart daran gearbeitet, ihn nachzuahmen!`, + 2: "Ich bin Lambda, ein Admin von Team Rocket. Ich werde nicht zulassen, dass du unsere Pläne störst!", + 3: "Rocket Vorstand Lambda wird sich um diesen Eindringling kümmern!" + }, + "victory": { + 1: "OK, OK. Ich sage dir, wo er ist.", + 2: "Ich… Ich konnte nichts tun… Giovanni, bitte vergib mir…", + 3: "Nein, ich kann das nicht auf mich sitzen lassen. Ich muss die anderen informieren…" + } + }, + "tabitha": { + "encounter": { + 1: "Hehehe! Du bist also bis hierher gekommen! Aber du bist zu spät!", + 2: `Hehehe... Schon hier, oder? Wir haben dich unterschätzt! Aber das war's! + $Ich bin eine Klasse über den Rüpeln, die du bisher gesehen hast. Ich halte dich nicht hin. + $Ich werde dich zermalmen!`, + 3: "Ich werde dir eine kleine Kostprobe des Schmerzes geben! Ergebe dich!" + }, + "victory": { + 1: `Hehehe! Du hast mich vielleicht besiegt, aber du hast keine Chance gegen den Boss! + $Wenn du jetzt aufgibst, musst du dich keiner ordentlichen Tracht Prügel stellen!`, + 2: "Hehehe... Also habe ich auch verloren...", + 3: "Ahya! Wie konnte das passieren? Ein Vorstand wie ich von einem zufälligen Trainer besiegt..." + } + }, + "courtney": { + "encounter": { + 1: `Das Ding... Das Ding, das du hältst... Das ist es, was... + $Das ist es, wonach wir von Team Magma suchen...`, + 2: "... Nun dann... Auslöschen...", + 3: `...?! Du... Hm... ♪ Das trifft sich ausgezeichnet... ♪ + $Dann hole ich mir eben zuerst deine Pokémon... Her damit...` + }, + "victory": { + 1: "... ...Ändere... die Welt.", + 2: `Wie erwartet. Unerwartet. Du. Ziel erfasst... abgeschlossen. + $Beginne... Experiment. Du. Für immer. Aha... ♪`, + 3: "...Schon wieder? Das war unerwartet. ...Ich wusste es. Du... bist interessant! ...Haha. ♪" + } + }, + "shelly": { + "encounter": { + 1: `Ahahahaha! Du wirst dich in die Angelegenheiten von Team Aqua einmischen? + $Du bist entweder absolut furchtlos, einfach unwissend oder beides! + $Du bist so süß, dass es ekelhaft ist! Ich werde dich erledigen.`, + 2: "Was ist das? Wer ist dieser verwöhnte Gör?", + 3: "Beruhige dich. Sei geduldig. Ich werde dich gleich zermalmen." + }, + "victory": { + 1: `Ahahahaha! Wir wurden unerwartet gestört! Uns bleiben keine Optionen. + $Wir müssen uns zurückziehen. Aber das ist nicht das letzte Mal, dass du Team Aqua siehst! + $Wir haben andere Pläne! Vergiss das nicht!`, + 2: "Ahhh?! War ich zu nachsichtig mit dir?!", + 3: `Uh. Willst du mir sagen, dass du während des Kampfes noch besser geworden bist? + $Du bist ein Gör mit einer glänzenden Zukunft… + $Meine Pokémon und ich haben keine Kraft mehr zu kämpfen… + $Geh weiter… Geh und werde von Adrian zerstört.` + } + }, + "matt": { + "encounter": { + 1: `Hoohahaha! Was, hast du eine Schraube locker oder so? + $Sieh dich an, kleiner Makuhita-ähnlicher Trainer!`, + 2: "Oho! Du! Du bist das lustige Kind!", + 3: "Was machst du hier? Bist du uns gefolgt?" + }, + "victory": { + 1: "Na gut, bis der Boss Zeit für dich hat, werde ich dein Gegner sein!", + 2: `Ich kann es fühlen! Ich kann es spüren, das ist klar! Die Stärke, die von dir ausgeht! + $Mehr! Ich will noch mehr! Aber es sieht so aus, als hätten wir keine Zeit mehr...`, + 3: `Das war Spaß! Ich wusste, dass du mir eine gute Zeit bieten würdest! + $Ich freue mich darauf, dich eines Tages wieder zu treffen!` + } + }, + "mars": { + "encounter": { + 1: "Ich bin Mars, eine der obersten Commander von Team Galaktik.", + 2: "Die Vision von Team Galaktik für die Zukunft ist unbeirrt. Opposition wird gnadenlos zerschlagen!", + 3: "Fühlst du dich nervös? Das solltest du!" + }, + "victory": { + 1: "Das kann nicht passieren! Wie habe ich verloren?!", + 2: "Du hast etwas Können, das muss ich zugeben.", + 3: "Besiegt... Das war ein teurer Fehler." + } + }, + "jupiter": { + "encounter": { + 1: "Jupiter, Commander von Team Galaktik, zu Diensten.", + 2: "Widerstand ist zwecklos. Team Galaktik wird siegen!", + 3: "Du zitterst... Schon Angst?" + }, + "victory": { + 1: "Unmöglich... Ich habe verloren?!", + 2: "Beeindruckend, du hast Mut!", + 3: "So zu verlieren... Wie peinlich." + } + }, + "saturn": { + "encounter": { + 1: "Ich bin Saturn, Commander von Team Galaktik.", + 2: "Unsere Mission ist absolut. Jeder Widerstand wird vernichtet!", + 3: "Ist das Angst, die ich in deinen Augen sehe?" + }, + "victory": { + 1: "Unmöglich... Von dir besiegt?!", + 2: "Du hast dich als würdiger Gegner erwiesen.", + 3: "Besiegt in der Niederlage... Das ist inakzeptabel." + } + }, + "zinzolin": { + "encounter": { + 1: "Du könntest eine Bedrohung für Team Plasma werden, also werden wir dich hier und jetzt eliminieren!", + 2: "Oh, zum Heulen... Ich hatte nicht erwartet, in dieser eisigen Kälte kämpfen zu müssen!", + 3: "Du bist ein beeindruckender Trainer, dass du es so weit geschafft hast. Aber hier endet es." + }, + "victory": { + 1: "G-Cis... Ich habe versagt...", + 2: "Es ist bitterkalt. Ich zittere. Ich leide. Doch ich stehe immer noch siegreich da.", + 3: "Hm. Du bist ein klügerer Trainer, als ich erwartet habe, aber nicht klug genug." + } + }, + "rood": { + "encounter": { + 1: "Du bist eine Bedrohung für Team Plasma. Wir können dich hier und jetzt nicht laufen lassen!", + 2: "Oh, dieser eisige Wind... Ich hätte nie gedacht, dass ich hier kämpfen müsste!", + 3: "Du bist ein bemerkenswerter Trainer, dass du es bis hierher geschafft hast. Aber hier wird es enden." + }, + "victory": { + 1: "G-Cis... Ich habe meine Mission nicht erfüllt...", + 2: "Die Kälte ist durchdringend. Ich zittere. Ich leide. Doch ich habe gesiegt.", + 3: "Hm. Du bist ein talentierter Trainer, aber leider nicht talentiert genug." + } + }, + "xerosic": { + "encounter": { + 1: "Ah ha ha! Es wäre mir ein Vergnügen. Komm schon, kleiner Trainer! Zeig mir, was du drauf hast!", + 2: "Hm... Du bist mächtiger, als du aussiehst. Ich frage mich, wie viel Energie in dir steckt.", + 3: "Ich habe auf dich gewartet! Ich muss ein wenig Forschung an dir betreiben! Komm, lass uns beginnen!" + }, + "victory": { + 1: "Ah, du bist ziemlich stark. Oh ja—sehr stark, in der Tat.", + 2: "Ding-ding-ding! Du hast es geschafft! Dem Sieger gebührt die Beute!", + 3: "Wunderbar! Erstaunlich! Du hast enorme Fähigkeiten und Mut!" + } + }, + "bryony": { + "encounter": { + 1: "Ich bin Begonia, und es wäre mir ein Vergnügen, gegen dich zu kämpfen. Zeig mir, was du drauf hast.", + 2: "Beeindruckend... Du bist mächtiger, als du aussiehst. Zeig mir das wahre Ausmaß deiner Energie.", + 3: "Ich habe deine Ankunft erwartet. Es ist Zeit für einen kleinen Test. Sollen wir beginnen?" + }, + "victory": { + 1: "Du bist ziemlich stark. Oh ja—sehr stark, in der Tat.", + 2: "Ding-ding-ding! Du hast dich gut geschlagen. Der Sieg gehört dir.", + 3: "Wunderbar! Bemerkenswert! Deine Fähigkeiten und dein Mut sind lobenswert." + } }, "firebreather": { "encounter": { @@ -414,105 +597,103 @@ export const PGMdialogue: DialogueTranslationEntries = { 3: "Ich glaube, ich bin der der seekrank ist..." }, }, - "magma_grunt": { + "rocket_grunt": { "encounter": { - 1: "Keiner, der sich Team Magma in den Weg stellt, bekommt Gnade, nicht einmal Kinder!" + 1: `Jetzt gibt es Ärger!… + $und es kommt noch härter! + $Wir wollen über die Erde regieren… + $und naja du kennst den Rest…!`, + 2: "Wir führen gerade eine große Operation durch. Hast du vor uns zu stören?", + 3: "Gib uns deine Pokémon, oder stelle dich dem Zorn von Team Rocket!", + 4: "Team Rocket wird seinen Plan zur Vollendung bringen, Aus dem Weg!", + 5: "Los, gib uns deine Pokémon. Wir brauchen sie für unseren Plan!" }, "victory": { - 1: "Wie kann das sein? Ich bin Teil vom mächtigen Team Magma! Wir wollen doch nur die Welt verbessern…" + 1: "Das war mal wieder ein Schuss in den Ofen!", + 2: "Dem Boss wird das aber nicht gefallen!", + 3: "Ich habe es vermasselt!", + 4: "Meine Kollegen werden das nicht tolerieren!", + 5: "Team Rocket wird wiederkehren! Das sage ich Jessie und James!" }, }, - "magma_admin": { + "magma_grunt": { "encounter": { - 1: "Hehehe! So you've come all the way here! But you're too late!", - 2: "You're going to meddle in Team Magma's affairs? You're so cute you're disgusting! I'll put you down kiddy!", - 3: "I'm going to give you a little taste of pain! Resign yourself to it!" + 1: "Keiner, der sich Team Magma in den Weg stellt, bekommt Gnade, nicht einmal Kinder!", + 2: "Störe besser nicht unsere Pläne! Wir formen die Welt nach unseren Vorstellungen!", + 3: "Du stehst uns im Weg! Team Magma hat keine Zeit für Störenfriede wie dich!", + 4: "Bereite dich auf die Hölle vor, denn es wird bald sehr heiß!", + 5: "Wir werden die Macht des Vulkans entfesseln! Es wird gewaltig sein! Mach dich bereit!" }, "victory": { - 1: "Hehehe... So I lost...", - 2: "You're disgustingly strong!", - 3: "Ahahaha! Ouch!" + 1: "Wie kann das sein? Ich bin Teil des mächtigen Team Magma! Wir streben nach der Verbesserung der Welt...", + 2: "Unglaublich, dass ich verloren habe! Mit meinen mächtigen Pokémon.", + 3: "Das kann nicht sein! Ich hab doch viel mehr Erfahrung als du!", + 4: "Verdammt... Ich hätte sofort in unser Versteck fliehen sollen...", + 5: "Du hast mich besiegt... Der Boss wird mich dafür zur Rechenschaft ziehen." }, }, "aqua_grunt": { "encounter": { 1: "Du willst dich also mit Team Aqua anlegen? Du traust dich ja was… Dich werfe ich über Bord!", + 2: "Du hast ganz schön Mut, dich mit Team Aqua anzulegen!", + 3: "Ich hoffe du hast einen Regenschirm dabei. Hier wird es jetzt nass!", + 4: "Wir, Team Aqua, existieren zum Wohle aller!", + 5: "Bereite dich darauf vor, von den Fluten meiner Pokémon weggespült zu werden!" }, "victory": { 1: "Vielleicht sollte ich wohl lieber selber über die Planke gehen…", - }, - }, - "aqua_admin": { - "encounter": { - 1: "I'm a cut above the grunts you've seen so far. I'm going to puvlerize you!", - 2: "Hahn? What's this? Who's this spoiled brat?", - 3: "What are you doing here? Did you follow us?" - }, - "victory": { - 1: "So I lost too...", - 2: "Ahhh?! Did I go too easy on you?!", - 3: "Wh-what was that?" + 2: "Arrgh, ich habe nicht damit gerechnet, von einer Landratte gestört zu werden!", + 3: "Ich habe verloren?! Ich schätze, ich muss jetzt zurück zum Versteck schwimmen...", + 4: "Oh Mann, was für eine Katastrophe... Der Boss wird wütend sein...", + 5: "Du hast mich besiegt... Meinst du, der Boss wird mich dafür kielholen lassen?" }, }, "galactic_grunt": { "encounter": { - 1: "Team Galaktik wird die Welt in eine bessere Welt verwandeln! Und du wirst uns nicht aufhalten!" + 1: "Team Galaktik wird die Welt in eine bessere verwandeln! Und du wirst uns nicht aufhalten!", + 2: "Erlebe die Macht unserer Technologie und die Zukunft, die wir uns vorstellen!", + 3: "Im Namen von Team Galaktik werde ich jeden beseitigen, der uns im Weg steht!", + 4: "Mach dich bereit zu verlieren!", + 5: "Hoffentlich bist du bereit für eine kosmische Niederlage!" }, "victory": { - 1: "Zyrus wird uns für diese Niederlage bestrafen…" - }, - }, - "galactic_admin": { - "encounter": { - 1: "I'm one of Team Galactic's Commanders.", - 2: "Anything that opposes Team Galactic must be crushed! Even the very thought of opposition will not be tolerated!", - 3: "What's the matter? Don't tell me you're shaking?" - }, - "victory": { - 1: "This can't be?! I lost?! You... you uppity brat!", - 2: "You, my friend, are tough!", - 3: "Losing to some child... Being careless cost me too much." + 1: "Zyrus wird uns für diese Niederlage bestrafen…", + 2: "Dieser Rückschlag bedeutet nichts in Hinsicht unseres großen Plans.", + 3: "Unsere Pläne sind größer als diese Niederlage.", + 4: "Wie ist das möglich?!", + 5: "Notiz an mich selbst: Pokémon-Kämpfe üben, so bald wie möglich." }, }, "plasma_grunt": { "encounter": { - 1: "Pokémon sollten frei sein! Team Plasma wird sie befreien!" + 1: "Pokémon sollten frei sein! Team Plasma wird sie befreien!", + 2: "Wenn ich gegen dich gewinne, lass deine Pokémon frei!", + 3: "Wenn du Team Plasma im Weg stehst, werde ich mich um dich kümmern!", + 4: "Team Plasma wird Pokémon von egoistischen Menschen wie dir befreien!", + 5: "Lass dich von unserem Aussehen nicht täuschen. Unsere Kampffähigkeiten sind überragend!" }, "victory": { - 1: "Wie konnte ich verlieren? Ich dachte, ich würde die Welt retten…" - }, - }, - "plasma_sage": { - "encounter": { - 1: "You could become a threat to Team Plasma, so we will eliminate you here!", - 2: "Oh, for crying out loud... I didn't expect to have to fight!", - 3: "You're an impressive Trainer to have made it this far." - }, - "victory": { - 1: "Ghetsis...", - 2: "It's bitter cold. I'm shivering. I'm suffering.", - 3: "Hmph. You're a smarter Trainer than I expected." + 1: "Wie konnte ich verlieren? Ich dachte, ich würde die Welt retten...", + 2: "Wie konnte ich nur verlieren...", + 3: "...Dieses Pokémon ist zu schwach, ich werde stärkere beschaffen müssen!", + 4: "Große Pläne stoßen immer auf Hindernisse.", + 5: "Das ist ein schwerer Rückschlag für Team Plasma..." }, }, "flare_grunt": { "encounter": { - 1: `Ich bin ein Mitglied von Team Flare! Das sieht man mir doch an. Mein Stil ist unverkennbar! - $Du kannst definitiv ein Umstyling gebrauchen!` + 1: "Deine Pokémon haben keine Chance gegen die Überlegenheit von Team Flare.", + 2: "Mach dich bereit, denn gleich wird es hier lichterloh brennen!", + 3: "Team Flare wird die Welt von allen Makeln befreien!", + 4: "Bereite dich auf die unvergleichliche Macht von Team Flare vor!", + 5: "Unsere Mission steht über allem, sogar über der Mode!" }, "victory": { - 1: "Stil ist wohl doch nicht alles…" - }, - }, - "flare_admin": { - "encounter": { - 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", - 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", - 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" - }, - "victory": { - 1: "You're quite strong. Oh yes-very strong, indeed.", - 2: "Ding-ding-ding! Yup, you did it! To the victor goes the spoils!", - 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + 1: "Diese Niederlage wirft einen Schatten auf meine Zukunft.", + 2: "Es scheint, dass ich meine Strategien überdenken muss. Zurück ans Reißbrett.", + 3: "Unglaublich?! Ich habe verloren?!", + 4: "Selbst in der Niederlage bleibt Team Flare unübertroffen in seiner Eleganz.", + 5: "Du hast mich besiegt, aber Team Flare wird immer in Glanz und Stil erstrahlen." }, }, "rocket_boss_giovanni_1": { @@ -548,7 +729,7 @@ export const PGMdialogue: DialogueTranslationEntries = { $Wir brauchen mehr Landmassen um zu leben! Team Magma wird dieses Ziel mit aller Macht erreichen!` }, "victory": { - 1:"Ugh! Das entspricht nicht meinen Berechnungen! Wie konnte ich verlieren? Wir sehen uns wieder!" + 1: "Ugh! Das entspricht nicht meinen Berechnungen! Wie konnte ich verlieren? Wir sehen uns wieder!" }, "defeat": { 1: "Team Magma wird weiterhin die Welt verbessern!" @@ -614,7 +795,7 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "plasma_boss_ghetsis_1": { "encounter": { - 1:"Ich werde nicht zulassen, dass mich jemand aufhält! Egal wer es auch sein mag!" + 1: "Ich werde nicht zulassen, dass mich jemand aufhält! Egal wer es auch sein mag!" }, "victory": { 1: "Wie kann das sein? Ich bin der Schöpfer von Team Plasma! Ich bin perfekt!" @@ -2658,7 +2839,7 @@ export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `Es scheint, als wäre es wieder mal an der Zeit.\nDu weißt, warum du hierher kommen musst, oder? $Dich hat es hierher gezogen, du warst bereits hier.\nUnzählige Male. - $Obwohl, vielleicht doch nicht unzählig.\nUm genau zu sein, dies ist der 5.643.853te Zyklus. + $Obwohl, vielleicht doch nicht unzählig.\nUm genau zu sein, dies ist der {{cycleCount}}te Zyklus. $Du verlierst jeden Zyklus dein Gedächtnis. Trotzdem \nbleibt etwas, ein Teil deines ehemaligen Ichs, erhalten. $Bis jetzt hast du es noch nicht vollbracht zu siegen, aber dieses Mal spüre ich eine andere Präsenz in dir.\n $Du bist der Einzige hier, aber es kommt mir so vor als wäre da...jemand anderes. @@ -2676,7 +2857,7 @@ export const PGFbattleSpecDialogue: SimpleTranslationEntries = PGMbattleSpecDial // Dialogue that does not fit into any other category (e.g. tutorial messages, or the end of the game). For when the player character is male export const PGMmiscDialogue: SimpleTranslationEntries = { "ending": - `@c{smile}Oh? Du hast gewonnen?@d{96} @c{smile_eclosed}Ich schätze, das hätte ich wissen sollen. + `@c{smile}Oh? Du hast gewonnen?@d{96} @c{smile_eclosed}Ich schätze, das hätte ich wissen sollen. $Aber, du bist jetzt zurück. $@c{smile}Es ist vorbei.@d{64} Du hast die Schleife beendet. $@c{serious_smile_fists}Du hast auch deinen Traum erfüllt, nicht wahr?\nDu hast nicht einmal verloren. @@ -2687,7 +2868,7 @@ export const PGMmiscDialogue: SimpleTranslationEntries = { $@c{serious_smile_fists}Vielleicht können wir, wenn wir zurück sind, noch einen Kampf haben? $Wenn du dazu bereit bist.`, "ending_female": - `@c{shock}Du bist zurück?@d{32} Bedeutet das…@d{96} du hast gewonnen?! + `@c{shock}Du bist zurück?@d{32} Bedeutet das…@d{96} du hast gewonnen?! $@c{smile_ehalf}Ich hätte wissen sollen, dass du es in dir hast. $@c{smile_eclosed}Natürlich… ich hatte immer dieses Gefühl. $@c{smile}Es ist jetzt vorbei, richtig? Du hast die Schleife beendet. diff --git a/src/locales/de/filter-bar.ts b/src/locales/de/filter-bar.ts index 9c1009171e2..1094b0fd43a 100644 --- a/src/locales/de/filter-bar.ts +++ b/src/locales/de/filter-bar.ts @@ -3,7 +3,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "Gen.", "typeFilter": "Typ", - "dexFilter": "Dex.", + "caughtFilter": "Gefangen", "unlocksFilter": "Freisch.", "miscFilter": "Sonst.", "sortFilter": "Sort.", @@ -13,9 +13,18 @@ export const filterBar: SimpleTranslationEntries = { "passive": "Passive", "passiveUnlocked": "Passive freigeschaltet", "passiveLocked": "Passive gesperrt", + "costReduction": "Cost Reduction", + "costReductionUnlocked": "Cost Reduction Unlocked", + "costReductionLocked": "Cost Reduction Locked", "ribbon": "Band", "hasWon": "Hat Klassik-Modus gewonnen", "hasNotWon": "Hat Klassik-Modus nicht gewonnen", + "hiddenAbility": "Hidden Ability", + "hasHiddenAbility": "Hidden Ability - Yes", + "noHiddenAbility": "Hidden Ability - No", + "pokerus": "Pokerus", + "hasPokerus": "Pokerus - Yes", + "noPokerus": "Pokerus - No", "sortByNumber": "Pokédex-Nummer", "sortByCost": "Kosten", "sortByCandies": "Anzahl Bonbons", diff --git a/src/locales/de/move-trigger.ts b/src/locales/de/move-trigger.ts index cc5411af339..53c9c847b38 100644 --- a/src/locales/de/move-trigger.ts +++ b/src/locales/de/move-trigger.ts @@ -4,8 +4,8 @@ export const moveTriggers: SimpleTranslationEntries = { "hitWithRecoil" : "{{pokemonName}} erleidet Schaden durch Rückstoß!", "cutHpPowerUpMove": "{{pokemonName}} nutzt seine KP um seine Attacke zu verstärken!", "absorbedElectricity": "{{pokemonName}} absorbiert elektrische Energie!", - "switchedStatChanges": "{{pokemonName}}tauschte die Statuswerteveränderungen mit dem Ziel!", - "goingAllOutForAttack": "{{pokemonName}}legt sich ins Zeug!", + "switchedStatChanges": "{{pokemonName}} tauschte die Statuswerteveränderungen mit dem Ziel!", + "goingAllOutForAttack": "{{pokemonName}} legt sich ins Zeug!", "regainedHealth": "{{pokemonName}} erholt sich!", "keptGoingAndCrashed": "{{pokemonName}} springt daneben und verletzt sich!", "fled": "{{pokemonName}} ist geflüchtet!", @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "{{pokemonName}} leuchtet grell!", "bellChimed": "Eine Glocke läutet!", "foresawAnAttack": "{{pokemonName}} sieht einen Angriff voraus!", + "isTighteningFocus": "{{pokemonName}} konzentriert sich!", "hidUnderwater": "{{pokemonName}} taucht unter!", "soothingAromaWaftedThroughArea": "Ein wohltuendes Aroma breitet sich aus!", "sprangUp": "{{pokemonName}} springt hoch in die Luft!", @@ -35,7 +36,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isOverflowingWithSpacePower": "Kosmische Kräfte strömen aus {{pokemonName}}!", "usedUpAllElectricity": "{{pokemonName}} braucht seinen Strom komplett auf!", "stoleItem": "{{pokemonName}} hat {{targetName}} das Item {{itemName}} geklaut!", - "incineratedItem": "{{itemName}} von {{targetName}} ist verbrannt und somit nutzlos geworden!", + "incineratedItem": "{{pokemonName}} hat {{itemName}} von {{targetName}} verbrannt. Es ist somit nutzlos geworden!", "knockedOffItem": "{{pokemonName}} schlägt das Item {{itemName}} von {{targetName}} weg!", "tookMoveAttack": "{{pokemonName}} wurde von {{moveName}} getroffen!", "cutOwnHpAndMaximizedStat": "{{pokemonName}} nutzt seine KP und maximiert dadurch seinen {{statName}}-Wert!", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "{{pokemonName}} hat den Typ von {{targetPokemonName}} angenommen!", "suppressAbilities": "Die Fähigkeit von {{pokemonName}} wirkt nicht mehr!", "swapArenaTags": "{{pokemonName}} hat die Effekte, die auf den beiden Seiten des Kampffeldes wirken, miteinander getauscht!", + "exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!", } as const; diff --git a/src/locales/de/settings.ts b/src/locales/de/settings.ts index 227b8f97581..4ac69868a7e 100644 --- a/src/locales/de/settings.ts +++ b/src/locales/de/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "Gesehenen Dialog überspringen", "battleStyle": "Kampfstil", "enableRetries": "Erneut versuchen aktivieren", + "hideIvs": "IS-Scanner verstecken", "tutorials": "Tutorials", "touchControls": "Touch Steuerung", "vibrations": "Vibration", diff --git a/src/locales/de/status-effect.ts b/src/locales/de/status-effect.ts index 997d005987e..b48bd468626 100644 --- a/src/locales/de/status-effect.ts +++ b/src/locales/de/status-effect.ts @@ -33,7 +33,7 @@ export const statusEffect: StatusEffectTranslationEntries = { description: "Paralyse", obtain: "{{pokemonNameWithAffix}} wurde paralysiert!\nEs kann eventuell nicht handeln!", obtainSource: "{{pokemonNameWithAffix}} wurde durch {{sourceText}} paralysiert,\nEs kann eventuell nicht handeln!", - activation: "{{pokemonNameWithAffix}}ist paralysiert!\nEs kann nicht angreifen!", + activation: "{{pokemonNameWithAffix}} ist paralysiert!\nEs kann nicht angreifen!", overlap: "{{pokemonNameWithAffix}} ist bereits paralysiert!", heal: "Die Paralyse von {{pokemonNameWithAffix}} wurde aufgehoben!" }, diff --git a/src/locales/de/trainers.ts b/src/locales/de/trainers.ts index 72776c40e88..6bde37a4ea3 100644 --- a/src/locales/de/trainers.ts +++ b/src/locales/de/trainers.ts @@ -19,6 +19,19 @@ export const titles: SimpleTranslationEntries = { "galactic_boss": "Galaktik-Boss", "plasma_boss": "Weiser von Team Plasma", // This is on purpose, since "Ghetsis" is never mentioned as the boss of team plasma in the game but as "Weiser" "flare_boss": "Flare-Boss", + + "rocket_admin": "Team Rocket Vorstand", + "rocket_admin_female": "Team Rocket Vorstand", + "magma_admin": "Team Magma Vorstand", + "magma_admin_female": "Team Magma Vorstand", + "aqua_admin": "Team Aqua Vorstand", + "aqua_admin_female": "Team Aqua Vorstand", + "galactic_commander": "Team Galaktik Commander", + "galactic_commander_female": "Team Galaktik Commander", + "plasma_sage": "Weiser von Team Plasma", + "plasma_admin": "Team Plasma Vorstand", + "flare_admin": "Team Flare Vorstand", + "flare_admin_female": "Team Flare Vorstand", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -128,32 +141,21 @@ export const trainerClasses: SimpleTranslationEntries = { "rocket_grunt": "Rüpel von Team Rocket", "rocket_grunt_female": "Rüpel von Team Rocket", "rocket_grunts": "Rüpel von Team Rocket", - "rocket_admin": "Rocket Admin", - "rocket_admin_female": "Rocket Admin", "magma_grunt": "Rüpel von Team Magma", "magma_grunt_female": "Rüpel von Team Magma", "magma_grunts": "Rüpel von Team Magma", - "magma_admin": "Magma Admin", - "magma_admin_female": "Magma Admin", "aqua_grunt": "Rüpel von Team Aqua", "aqua_grunt_female": "Rüpel von Team Aqua", "aqua_grunts": "Rüpel von Team Aqua", - "aqua_admin": "Aqua Admin", - "aqua_admin_female": "Aqua Admin", "galactic_grunt": "Rüpel von Team Galaktik", "galactic_grunt_female": "Rüpel von Team Galaktik", "galactic_grunts": "Rüpel von Team Galaktik", - "galactic_admin": "Galactic Admin", - "galactic_admin_female": "Galactic Admin", "plasma_grunt": "Rüpel von Team Plasma", "plasma_grunt_female": "Rüpel von Team Plasma", "plasma_grunts": "Rüpel von Team Plasma", - "plasma_sage": "Plasma Sage", "flare_grunt": "Rüpel von Team Flare", "flare_grunt_female": "Rüpel von Team Flare", "flare_grunts": "Rüpel von Team Flare", - "flare_admin": "Flare Admin", - "flare_admin_female": "Flare Admin", } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -282,6 +284,24 @@ export const trainerNames: SimpleTranslationEntries = { "leon": "Delion", "rival": "Finn", "rival_female": "Ivy", + + // Evil Team Admins + "archer": "Atlas", + "ariana": "Athena", + "proton": "Lance", + "petrel": "Lambda", + "tabitha": "Kalle", + "courtney": "Jördis", + "shelly": "Kordula", + "matt": "Wolfgang", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturn", + "zinzolin": "Violaceus", + "rood": "Rubius", + "xerosic": "Xeros", + "bryony": "Begonia", + "maxie": "Marc", "archie": "Adrian", "cyrus": "Zyrus", diff --git a/src/locales/en/achv.ts b/src/locales/en/achv.ts index 3063488c659..a05c8b814ab 100644 --- a/src/locales/en/achv.ts +++ b/src/locales/en/achv.ts @@ -169,6 +169,10 @@ export const PGMachv: AchievementTranslationEntries = { name: "Undefeated", description: "Beat the game in classic mode", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Bring Your Child To Work Day", + description: "Beat the game in Classic Mode with at least one unevolved party member." + }, "MONO_GEN_ONE": { name: "The Original Rival", @@ -266,7 +270,7 @@ export const PGMachv: AchievementTranslationEntries = { }, "FRESH_START": { name: "First Try!", - description: "Complete the fresh start challenge." + description: "Complete the Fresh Start challenge." } } as const; diff --git a/src/locales/en/arena-tag.ts b/src/locales/en/arena-tag.ts index 8bc2302368a..22612795308 100644 --- a/src/locales/en/arena-tag.ts +++ b/src/locales/en/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!", "conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!", "matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!", + "noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!", + "noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!", + "noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!", "wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!", "mudSportOnAdd": "Electricity's power was weakened!", "mudSportOnRemove": "The effects of Mud Sport\nhave faded.", diff --git a/src/locales/en/bgm-name.ts b/src/locales/en/bgm-name.ts index 01fb86b281d..be9a8f621c7 100644 --- a/src/locales/en/bgm-name.ts +++ b/src/locales/en/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 Kanto Champion Battle", "battle_johto_champion": "B2W2 Johto Champion Battle", - "battle_hoenn_champion": "B2W2 Hoenn Champion Battle", + "battle_hoenn_champion_g5": "B2W2 Hoenn Champion Battle", + "battle_hoenn_champion_g6": "ORAS Hoenn Champion Battle", "battle_sinnoh_champion": "B2W2 Sinnoh Champion Battle", "battle_champion_alder": "BW Unova Champion Battle", "battle_champion_iris": "B2W2 Unova Champion Battle", diff --git a/src/locales/en/challenges.ts b/src/locales/en/challenges.ts index a3b45d31792..e3302876201 100644 --- a/src/locales/en/challenges.ts +++ b/src/locales/en/challenges.ts @@ -25,7 +25,7 @@ export const challenges: TranslationEntries = { }, "freshStart": { "name": "Fresh Start", - "desc": "You can only use the original starters, and only as if you had just started pokerogue.", + "desc": "You can only use the original starters, and only as if you had just started PokéRogue.", "value.0": "Off", "value.1": "On", } diff --git a/src/locales/en/dialogue.ts b/src/locales/en/dialogue.ts index 44693c38aa1..715f245e518 100644 --- a/src/locales/en/dialogue.ts +++ b/src/locales/en/dialogue.ts @@ -383,124 +383,299 @@ export const PGMdialogue: DialogueTranslationEntries = { 3: "I think it's me that's seasick..." }, }, - "rocket_grunt": { + + "archer": { "encounter": { - 1: "Prepare for trouble!" + 1: "Before you go any further, let's see how you far against us, Team Rocket!", + 2: "I have received reports that your skills are not insignificant. Let's see if they are true.", + 3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." }, "victory": { - 1: "Team Rocket blasting off again!" + 1: "What a blunder!", + 2: "With my current skills, I was not up to the task after all.", + 3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." }, }, - "rocket_admin": { + "ariana": { "encounter": { - 1: "Oh? You managed to get this far? You must be quite the trainer.", - 2: "That's quite enough of you playing hero, kid.", - 3: "I'll show you how scary an angry adult can be!" + 1: `Hold it right there! We can't someone on the loose." + $It's harmful to Team Rocket's pride, you see.`, + 2: `I don't know or care if what I'm doing is right or wrong... + $I just put my faith in Giovanni and do as I am told`, + 3: "Your trip ends here. I'm going to take you down!" }, "victory": { - 1: "No! Forgive me Giovanni!", - 2: "How could this be?", - 3: "Urgh... You were too strong..." + 1: `Tch, you really are strong. It's too bad. + $If you were to join Team Rocket, you could become an Executive.`, + 2: "I... I'm shattered...", + 3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" }, }, - "magma_grunt": { + "proton": { "encounter": { - 1: " If you get in the way of Team Magma, don’t expect any mercy!" + 1: "What do you want? If you interrupt our work, don't expect any mercy!", + 2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket… + $I strongly urge you not to interfere with our business!`, + 3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" }, "victory": { - 1: "Huh? I lost?!" + 1: "The fortress came down!", + 2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", + 3: "I am defeated… But I will not forget this!" }, }, - "magma_admin": { + + "petrel": { + "encounter": { + 1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. + $The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni? + $I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, + 2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + 3: "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + 1: "OK, OK. I'll tell you where he is.", + 2: "I… I couldn't do a thing… Giovanni, please forgive me…", + 3: "No, I can't let this affect me. I have to inform the others…" + }, + }, + "tabitha": { "encounter": { 1: "Hehehe! So you've come all the way here! But you're too late!", - 2: "You're going to meddle in Team Magma's affairs? You're so cute you're disgusting! I'll put you down kiddy!", + 2: `Hehehe... Got here already, did you? We underestimated you! But this is it! + $I'm a cut above the Grunts you've seen so far. I'm not stalling for time. + $I'm going to pulverize you!`, 3: "I'm going to give you a little taste of pain! Resign yourself to it!" }, "victory": { - 1: "Hehehe... So I lost...", - 2: "You're disgustingly strong!", - 3: "Ahahaha! Ouch!" + 1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! + $If you get lost now, you won't have to face a sound whipping!`, + 2: "Hehehe... So, I lost, too...", + 3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." }, }, - "aqua_grunt": { + "courtney": { "encounter": { - 1: "No one who crosses Team Aqua gets any mercy, not even kids!" + 1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", + 2: "... Well then...Deleting...", + 3: "...Ha. ...Analyzing... ...Hah♪" }, "victory": { - 1: "You're kidding me!" + 1: "... ...Change...the world.", + 2: `As anticipated. Unanticipated. You. Target lock...completed. + $Commencing...experiment. You. Forever. Aha... ♪`, + 3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" }, }, - "aqua_admin": { + "shelly": { "encounter": { - 1: "I'm a cut above the grunts you've seen so far. I'm going to puvlerize you!", - 2: "Hahn? What's this? Who's this spoiled brat?", + 1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? + $You're either absolutely fearless, simply ignorant, or both! + $You're so cute, you're disgusting! I'll put you down`, + 2: "What's this? Who's this spoiled brat?", + 3: "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + 1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. + $We'll have to pull out. But this isn't the last you'll see of Team Aqua! + $We have other plans! Don't you forget it!`, + 2: "Ahhh?! Did I go too easy on you?!", + 3: `Uh. Are you telling me you've upped your game even more during the fight? + $You're a brat with a bright future… My Pokémon and I don't have any strength left to fight… + $Go on… Go and be destroyed by Archie.` + }, + }, + "matt": { + "encounter": { + 1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", + 2: "Oho! You! You're that funny kid!", 3: "What are you doing here? Did you follow us?" }, "victory": { - 1: "So I lost too...", - 2: "Ahhh?! Did I go too easy on you?!", - 3: "Wh-what was that?" + 1: "All right then, until the Boss has time for you, I'll be your opponent!", + 2: `I can feel it! I can feel it, all right! The strength coming offa you! + $More! I still want more! But looks like we're outta time...`, + 3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" }, }, - "galactic_grunt": { + "mars": { "encounter": { - 1: "Don't mess with Team Galactic!" + 1: "I'm Mars, one of Team Galactic's top Commanders.", + 2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + 3: "Feeling nervous? You should be!" }, "victory": { - 1: "Shut down..." - }, + 1: "This can't be happening! How did I lose?!", + 2: "You have some skill, I'll give you that.", + 3: "Defeated... This was a costly mistake." + } }, - "galactic_admin": { + "jupiter": { "encounter": { - 1: "I'm one of Team Galactic's Commanders.", - 2: "Anything that opposes Team Galactic must be crushed! Even the very thought of opposition will not be tolerated!", - 3: "What's the matter? Don't tell me you're shaking?" + 1: "Jupiter, Commander of Team Galactic, at your service.", + 2: "Resistance is futile. Team Galactic will prevail!", + 3: "You're trembling... scared already?" }, "victory": { - 1: "This can't be?! I lost?! You... you uppity brat!", - 2: "You, my friend, are tough!", - 3: "Losing to some child... Being careless cost me too much." - }, + 1: "No way... I lost?!", + 2: "Impressive, you've got guts!", + 3: "Losing like this... How embarrassing." + } }, - "plasma_grunt": { + "saturn": { "encounter": { - 1: "We won't tolerate people who have different ideas!" + 1: "I am Saturn, Commander of Team Galactic.", + 2: "Our mission is absolute. Any hindrance will be obliterated!", + 3: "Is that fear I see in your eyes?" }, "victory": { - 1: "Plasmaaaaaaaaa!" - }, - }, - "plasma_sage": { + 1: "Impossible... Defeated by you?!", + 2: "You have proven yourself a worthy adversary.", + 3: "Bestowed in defeat... This is unacceptable." + }}, + "zinzolin": { "encounter": { - 1: "You could become a threat to Team Plasma, so we will eliminate you here!", - 2: "Oh, for crying out loud... I didn't expect to have to fight!", - 3: "You're an impressive Trainer to have made it this far." + 1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", + 2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", + 3: "You're an impressive Trainer to have made it this far. But it ends here." }, "victory": { - 1: "Ghetsis...", - 2: "It's bitter cold. I'm shivering. I'm suffering.", - 3: "Hmph. You're a smarter Trainer than I expected." - }, + 1: "Ghetsis... I have failed you...", + 2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", + 3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } }, - "flare_grunt": { + "rood": { "encounter": { - 1: "Fashion is most important to us!" + 1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + 2: "Oh, this icy wind... I never thought I'd have to fight here!", + 3: "You are a remarkable Trainer to have made it this far. But this is where it ends." }, "victory": { - 1: "The future doesn't look bright for me." - }, + 1: "Ghetsis... I have failed my mission...", + 2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", + 3: "Hm. You are a talented Trainer, but unfortunately not talented enough." + } }, - "flare_admin": { + "xerosic": { "encounter": { 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" }, "victory": { - 1: "You're quite strong. Oh yes-very strong, indeed.", - 2: "Ding-ding-ding! Yup, you did it! To the victor goes the spoils!", + 1: "Ah, you're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You did it! To the victor go the spoils!", 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + 1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + 2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + 3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + 1: "You're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You've done well. Victory is yours.", + 3: "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, + "rocket_grunt": { + "encounter": { + 1: "Prepare for trouble!", + 2: "We're pulling a big job here! Get lost, kid!", + 3: "Hand over your Pokémon, or face the wrath of Team Rocket!", + 4: "You're about to experience the true terror of Team Rocket!", + 5: "Hey, kid! Me am a Team Rocket member kind of guy!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "Team Rocket blasting off again!", + 2: "Oh no! I dropped the Lift Key!", + 3: "I blew it!", + 4: "My associates won't stand for this!", + 5: "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" //Use of wrong grammar is deliberate. + }, + }, + "magma_grunt": { + "encounter": { + 1: "If you get in the way of Team Magma, don’t expect any mercy!", + 2: "You'd better not interfere with our plans! We're making the world a better place!", + 3: "You're in the way! Team Magma has no time for kids like you!", + 4: "I hope you brought marshmallows because things are about to heat up!", + 5: "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" + }, + "victory": { + 1: "Huh? I lost?!", + 2: "I can't believe I lost! I even skipped lunch for this", + 3: "No way! You're just a kid!", + 4: "Urrrgh... I should've ducked into our hideout right away...", + 5: "You beat me... Do you think the boss will dock my pay for this?" + }, + }, + + "aqua_grunt": { + "encounter": { + 1: "No one who crosses Team Aqua gets any mercy, not even kids!", + 2: "Grrr... You've got some nerve meddling with Team Aqua!", + 3: "You're about to get soaked! And not just from my water Pokémon!", + 4: "We, Team Aqua, exist for the good of all!", + 5: "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" + }, + "victory": { + 1: "You're kidding me!", + 2: "Arrgh, I didn't count on being meddled with by some meddling kid!", + 3: "I lost?! Guess I'll have to swim back to the hideout now...", + 4: "Oh, man, what a disaster... The boss is going to be furious...", + 5: "You beat me... Do you think the boss will make me walk the plank for this?" + }, + }, + "galactic_grunt": { + "encounter": { + 1: "Don't mess with Team Galactic!", + 2: "Witness the power of our technology and the future we envision!", + 3: "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + 4: "Get ready to lose!", + 5: "Hope you're ready for a cosmic beatdown!" + }, + "victory": { + 1: "Shut down...", + 2: "This setback means nothing in the grand scheme.", + 3: "Our plans are bigger than this defeat.", + 4: "How?!", + 5: "Note to self: practice Pokémon battling, ASAP." + }, + }, + "plasma_grunt": { + "encounter": { + 1: "We won't tolerate people who have different ideas!", + 2: "If I win against you, release your Pokémon!", + 3: "If you get in the way of Team Plasma, I'll take care of you!", + 4: "Team Plasma will liberate Pokémon from selfish humans like you!", + 5: "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." + }, + "victory": { + 1: "Plasmaaaaaaaaa!", + 2: "How could I lose...", + 3: "...What a weak Pokémon, I'll just have to go steal some better ones!", + 4: "Great plans are always interrupted.", + 5: "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" + }, + }, + "flare_grunt": { + "encounter": { + 1: "Your Pokémon are no match for the elegance of Team Flare.", + 2: "Hope you brought your sunglasses, because things are about to get bright!", + 3: "Team Flare will cleanse the world of imperfection!", + 4: "Prepare to face the brilliance of Team Flare!", + 5: "Fashion is most important to us!" + }, + "victory": { + 1: "The future doesn't look bright for me.", + 2: "Perhaps there's more to battling than I thought. Back to the drawing board.", + 3: "Gahh?! I lost?!", + 4: "Even in defeat, Team Flare's elegance shines through.", + 5: "You may have beaten me, but when I lose, I go out in style!" }, }, "rocket_boss_giovanni_1": { @@ -2588,11 +2763,11 @@ export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `It appears the time has finally come once again.\nYou know why you have come here, do you not? $You were drawn here, because you have been here before.\nCountless times. - $Though, perhaps it can be counted.\nTo be precise, this is in fact your 5,643,853rd cycle. + $Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle. $Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain. $Until now you have yet to succeed, but I sense a different presence in you this time.\n $You are the only one here, though it is as if there is… another. - $Will you finally prove a formidable challenge to me?\nThe challenge I have longed for for millennia? + $Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia? $We begin.`, "firstStageWin": `I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back. $Do not disappoint me.`, diff --git a/src/locales/en/filter-bar.ts b/src/locales/en/filter-bar.ts index 18b6ba77e21..0961df9f058 100644 --- a/src/locales/en/filter-bar.ts +++ b/src/locales/en/filter-bar.ts @@ -3,19 +3,28 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "Gen", "typeFilter": "Type", - "dexFilter": "Dex", + "caughtFilter": "Caught", "unlocksFilter": "Unlocks", "miscFilter": "Misc", "sortFilter": "Sort", "all": "All", - "normal": "Normal", + "normal": "Not Shiny", "uncaught": "Uncaught", "passive": "Passive", "passiveUnlocked": "Passive Unlocked", "passiveLocked": "Passive Locked", + "costReduction": "Cost Reduction", + "costReductionUnlocked": "Cost Reduction Unlocked", + "costReductionLocked": "Cost Reduction Locked", "ribbon": "Ribbon", "hasWon": "Ribbon - Yes", "hasNotWon": "Ribbon - No", + "hiddenAbility": "Hidden Ability", + "hasHiddenAbility": "Hidden Ability - Yes", + "noHiddenAbility": "Hidden Ability - No", + "pokerus": "Pokerus", + "hasPokerus": "Pokerus - Yes", + "noPokerus": "Pokerus - No", "sortByNumber": "No.", "sortByCost": "Cost", "sortByCandies": "Candy Count", diff --git a/src/locales/en/move-trigger.ts b/src/locales/en/move-trigger.ts index 1d9d6459d83..b85f27228be 100644 --- a/src/locales/en/move-trigger.ts +++ b/src/locales/en/move-trigger.ts @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "{{pokemonName}} became cloaked in a harsh light!", "bellChimed": "A bell chimed!", "foresawAnAttack": "{{pokemonName}} foresaw\nan attack!", + "isTighteningFocus": "{{pokemonName}} is\ntightening its focus!", "hidUnderwater": "{{pokemonName}} hid\nunderwater!", "soothingAromaWaftedThroughArea": "A soothing aroma wafted through the area!", "sprangUp": "{{pokemonName}} sprang up!", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "{{pokemonName}}'s type became the same as\n{{targetPokemonName}}'s type!", "suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!", "swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!", + "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!", } as const; diff --git a/src/locales/en/pokemon-info.ts b/src/locales/en/pokemon-info.ts index f31fdac69ab..70a06294c76 100644 --- a/src/locales/en/pokemon-info.ts +++ b/src/locales/en/pokemon-info.ts @@ -15,7 +15,8 @@ export const pokemonInfo: PokemonInfoTranslationEntries = { "SPD": "Speed", "SPDshortened": "Spd", "ACC": "Accuracy", - "EVA": "Evasiveness" + "EVA": "Evasiveness", + "HPStat": "HP" }, Type: { diff --git a/src/locales/en/settings.ts b/src/locales/en/settings.ts index 491bfa4a481..0b88f5e82c8 100644 --- a/src/locales/en/settings.ts +++ b/src/locales/en/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "Skip Seen Dialogues", "battleStyle": "Battle Style", "enableRetries": "Enable Retries", + "hideIvs": "Hide IV scanner", "tutorials": "Tutorials", "touchControls": "Touch Controls", "vibrations": "Vibrations", diff --git a/src/locales/en/trainers.ts b/src/locales/en/trainers.ts index 00367865d14..a40fabaeacc 100644 --- a/src/locales/en/trainers.ts +++ b/src/locales/en/trainers.ts @@ -19,6 +19,19 @@ export const titles: SimpleTranslationEntries = { "galactic_boss": "Team Galactic Boss", "plasma_boss": "Team Plasma Boss", "flare_boss": "Team Flare Boss", + + "rocket_admin": "Team Rocket Admin", + "rocket_admin_female": "Team Rocket Admin", + "magma_admin": "Team Magma Admin", + "magma_admin_female": "Team Magma Admin", + "aqua_admin": "Team Aqua Admin", + "aqua_admin_female": "Team Aqua Admin", + "galactic_commander": "Team Galactic Commander", + "galactic_commander_female": "Team Galactic Commander", + "plasma_sage": "Team Plasma Sage", + "plasma_admin": "Team Plasma Admin", + "flare_admin": "Team Flare Admin", + "flare_admin_female": "Team Flare Admin", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -128,32 +141,21 @@ export const trainerClasses: SimpleTranslationEntries = { "rocket_grunt": "Rocket Grunt", "rocket_grunts": "Rocket Grunts", "rocket_grunt_female": "Rocket Grunt", - "rocket_admin": "Rocket Admin", - "rocket_admin_female": "Rocket Admin", "magma_grunt": "Magma Grunt", "magma_grunt_female": "Magma Grunt", "magma_grunts": "Magma Grunts", - "magma_admin": "Magma Admin", - "magma_admin_female": "Magma Admin", "aqua_grunt": "Aqua Grunt", "aqua_grunt_female": "Aqua Grunt", "aqua_grunts": "Aqua Grunts", - "aqua_admin": "Aqua Admin", - "aqua_admin_female": "Aqua Admin", "galactic_grunt": "Galactic Grunt", "galactic_grunt_female": "Galactic Grunt", "galactic_grunts": "Galactic Grunts", - "galactic_admin": "Galactic Admin", - "galactic_admin_female": "Galactic Admin", "plasma_grunt": "Plasma Grunt", "plasma_grunt_female": "Plasma Grunt", "plasma_grunts": "Plasma Grunts", - "plasma_sage": "Plasma Sage", "flare_grunt": "Flare Grunt", "flare_grunt_female": "Flare Grunt", "flare_grunts": "Flare Grunts", - "flare_admin": "Flare Admin", - "flare_admin_female": "Flare Admin", } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -282,6 +284,24 @@ export const trainerNames: SimpleTranslationEntries = { "leon": "Leon", "rival": "Finn", "rival_female": "Ivy", + + // Evil Team Admins + "archer": "Archer", + "ariana": "Ariana", + "proton": "Proton", + "petrel": "Petrel", + "tabitha": "Tabitha", + "courtney": "Courtney", + "shelly": "Shelly", + "matt": "Matt", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturn", + "zinzolin": "Zinzolin", + "rood": "Rood", + "xerosic": "Xerosic", + "bryony": "Bryony", + "maxie": "Maxie", "archie": "Archie", "cyrus": "Cyrus", diff --git a/src/locales/es/achv.ts b/src/locales/es/achv.ts index 47348903b28..e291d93add2 100644 --- a/src/locales/es/achv.ts +++ b/src/locales/es/achv.ts @@ -169,6 +169,10 @@ export const PGMachv: AchievementTranslationEntries = { name: "Imbatible", description: "Completa el juego en modo clásico.", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Bring Your Child To Work Day", + description: "Beat the game in Classic Mode with at least one unevolved party member." + }, "MONO_GEN_ONE": { name: "The Original Rival", @@ -264,6 +268,10 @@ export const PGMachv: AchievementTranslationEntries = { "MONO_FAIRY": { name: "Mono FAIRY", }, + "FRESH_START": { + name: "First Try!", + description: "Complete the Fresh Start challenge." + } } as const; // Achievement translations for the when the player character is female (it for now uses the same translations as the male version) diff --git a/src/locales/es/arena-tag.ts b/src/locales/es/arena-tag.ts index 8bc2302368a..22612795308 100644 --- a/src/locales/es/arena-tag.ts +++ b/src/locales/es/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!", "conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!", "matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!", + "noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!", + "noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!", + "noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!", "wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!", "mudSportOnAdd": "Electricity's power was weakened!", "mudSportOnRemove": "The effects of Mud Sport\nhave faded.", diff --git a/src/locales/es/bgm-name.ts b/src/locales/es/bgm-name.ts index ab6de0b81b5..f7316ca1166 100644 --- a/src/locales/es/bgm-name.ts +++ b/src/locales/es/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 - ¡Vs Campeón de Kanto!", "battle_johto_champion": "B2W2 - ¡Vs Campeón de Johto!", - "battle_hoenn_champion": "B2W2 - ¡Vs Campeón de Hoenn!", + "battle_hoenn_champion_g5": "B2W2 - ¡Vs Campeón de Hoenn!", + "battle_hoenn_champion_g6": "ORAS - ¡Vs Campeón de Hoenn!", "battle_sinnoh_champion": "B2W2 - ¡Vs Campeón de Sinnoh!", "battle_champion_alder": "BW - ¡Vs Campeón de Teselia!", "battle_champion_iris": "B2W2 - ¡Vs Campeón de Teselia!", diff --git a/src/locales/es/challenges.ts b/src/locales/es/challenges.ts index 711be39b116..063933a5e27 100644 --- a/src/locales/es/challenges.ts +++ b/src/locales/es/challenges.ts @@ -22,4 +22,10 @@ export const challenges: TranslationEntries = { "desc": "Solo puedes usar Pokémon with the {{type}} type.", "desc_default": "Solo puedes usar Pokémon del tipo elegido.", }, + "freshStart": { + "name": "Fresh Start", + "desc": "You can only use the original starters, and only as if you had just started PokéRogue.", + "value.0": "Off", + "value.1": "On", + } } as const; diff --git a/src/locales/es/dialogue.ts b/src/locales/es/dialogue.ts index d19acc3ec0f..187127d1d39 100644 --- a/src/locales/es/dialogue.ts +++ b/src/locales/es/dialogue.ts @@ -383,52 +383,297 @@ export const PGMdialogue: DialogueTranslationEntries = { 3: "Creo que soy yo quien está mareado..." }, }, - "rocket_grunt": { + "archer": { "encounter": { - 1: "¡Ríndete ahora, o prepárate para luchar!" + 1: "Before you go any further, let's see how you far against us, Team Rocket!", + 2: "I have received reports that your skills are not insignificant. Let's see if they are true.", + 3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." }, "victory": { - 1: "¡El Team Rocket despega de nuevo!" + 1: "What a blunder!", + 2: "With my current skills, I was not up to the task after all.", + 3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." + }, + }, + "ariana": { + "encounter": { + 1: `Hold it right there! We can't someone on the loose." + $It's harmful to Team Rocket's pride, you see.`, + 2: `I don't know or care if what I'm doing is right or wrong... + $I just put my faith in Giovanni and do as I am told`, + 3: "Your trip ends here. I'm going to take you down!" + }, + "victory": { + 1: `Tch, you really are strong. It's too bad. + $If you were to join Team Rocket, you could become an Executive.`, + 2: "I... I'm shattered...", + 3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" + }, + }, + "proton": { + "encounter": { + 1: "What do you want? If you interrupt our work, don't expect any mercy!", + 2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket… + $I strongly urge you not to interfere with our business!`, + 3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" + }, + "victory": { + 1: "The fortress came down!", + 2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", + 3: "I am defeated… But I will not forget this!" + }, + }, + + "petrel": { + "encounter": { + 1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. + $The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni? + $I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, + 2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + 3: "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + 1: "OK, OK. I'll tell you where he is.", + 2: "I… I couldn't do a thing… Giovanni, please forgive me…", + 3: "No, I can't let this affect me. I have to inform the others…" + }, + }, + "tabitha": { + "encounter": { + 1: "Hehehe! So you've come all the way here! But you're too late!", + 2: `Hehehe... Got here already, did you? We underestimated you! But this is it! + $I'm a cut above the Grunts you've seen so far. I'm not stalling for time. + $I'm going to pulverize you!`, + 3: "I'm going to give you a little taste of pain! Resign yourself to it!" + }, + "victory": { + 1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! + $If you get lost now, you won't have to face a sound whipping!`, + 2: "Hehehe... So, I lost, too...", + 3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." + }, + }, + "courtney": { + "encounter": { + 1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", + 2: "... Well then...Deleting...", + 3: "...Ha. ...Analyzing... ...Hah♪" + }, + "victory": { + 1: "... ...Change...the world.", + 2: `As anticipated. Unanticipated. You. Target lock...completed. + $Commencing...experiment. You. Forever. Aha... ♪`, + 3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" + }, + }, + "shelly": { + "encounter": { + 1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? + $You're either absolutely fearless, simply ignorant, or both! + $You're so cute, you're disgusting! I'll put you down`, + 2: "What's this? Who's this spoiled brat?", + 3: "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + 1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. + $We'll have to pull out. But this isn't the last you'll see of Team Aqua! + $We have other plans! Don't you forget it!`, + 2: "Ahhh?! Did I go too easy on you?!", + 3: `Uh. Are you telling me you've upped your game even more during the fight? + $You're a brat with a bright future… My Pokémon and I don't have any strength left to fight… + $Go on… Go and be destroyed by Archie.` + }, + }, + "matt": { + "encounter": { + 1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", + 2: "Oho! You! You're that funny kid!", + 3: "What are you doing here? Did you follow us?" + }, + "victory": { + 1: "All right then, until the Boss has time for you, I'll be your opponent!", + 2: `I can feel it! I can feel it, all right! The strength coming offa you! + $More! I still want more! But looks like we're outta time...`, + 3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" + }, + }, + "mars": { + "encounter": { + 1: "I'm Mars, one of Team Galactic's top Commanders.", + 2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + 3: "Feeling nervous? You should be!" + }, + "victory": { + 1: "This can't be happening! How did I lose?!", + 2: "You have some skill, I'll give you that.", + 3: "Defeated... This was a costly mistake." + } + }, + "jupiter": { + "encounter": { + 1: "Jupiter, Commander of Team Galactic, at your service.", + 2: "Resistance is futile. Team Galactic will prevail!", + 3: "You're trembling... scared already?" + }, + "victory": { + 1: "No way... I lost?!", + 2: "Impressive, you've got guts!", + 3: "Losing like this... How embarrassing." + } + }, + "saturn": { + "encounter": { + 1: "I am Saturn, Commander of Team Galactic.", + 2: "Our mission is absolute. Any hindrance will be obliterated!", + 3: "Is that fear I see in your eyes?" + }, + "victory": { + 1: "Impossible... Defeated by you?!", + 2: "You have proven yourself a worthy adversary.", + 3: "Bestowed in defeat... This is unacceptable." + }}, + "zinzolin": { + "encounter": { + 1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", + 2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", + 3: "You're an impressive Trainer to have made it this far. But it ends here." + }, + "victory": { + 1: "Ghetsis... I have failed you...", + 2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", + 3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } + }, + "rood": { + "encounter": { + 1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + 2: "Oh, this icy wind... I never thought I'd have to fight here!", + 3: "You are a remarkable Trainer to have made it this far. But this is where it ends." + }, + "victory": { + 1: "Ghetsis... I have failed my mission...", + 2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", + 3: "Hm. You are a talented Trainer, but unfortunately not talented enough." + } + }, + "xerosic": { + "encounter": { + 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", + 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", + 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" + }, + "victory": { + 1: "Ah, you're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You did it! To the victor go the spoils!", + 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + 1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + 2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + 3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + 1: "You're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You've done well. Victory is yours.", + 3: "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, + "rocket_grunt": { + "encounter": { + 1: "¡Ríndete ahora, o prepárate para luchar!", + 2: "We're pulling a big job here! Get lost, kid!", + 3: "Hand over your Pokémon, or face the wrath of Team Rocket!", + 4: "You're about to experience the true terror of Team Rocket!", + 5: "Hey, kid! Me am a Team Rocket member kind of guy!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "¡El Team Rocket despega de nuevo!", + 2: "Oh no! I dropped the Lift Key!", + 3: "I blew it!", + 4: "My associates won't stand for this!", + 5: "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" //Use of wrong grammar is deliberate. }, }, "magma_grunt": { "encounter": { - 1: "¡No esperes piedad si te interpones al Team Magma!" + 1: "¡No esperes piedad si te interpones al Team Magma!", + 2: "You'd better not interfere with our plans! We're making the world a better place!", + 3: "You're in the way! Team Magma has no time for kids like you!", + 4: "I hope you brought marshmallows because things are about to heat up!", + 5: "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" }, "victory": { - 1: "¿Eh? ¿He perdido?" + 1: "¿Eh? ¿He perdido?", + 2: "I can't believe I lost! I even skipped lunch for this", + 3: "No way! You're just a kid!", + 4: "Urrrgh... I should've ducked into our hideout right away...", + 5: "You beat me... Do you think the boss will dock my pay for this?" }, }, "aqua_grunt": { "encounter": { - 1: "El Team Aqua no muestra piedad, ¡ni siquiera a los niños!" + 1: "El Team Aqua no muestra piedad, ¡ni siquiera a los niños!", + 2: "Grrr... You've got some nerve meddling with Team Aqua!", + 3: "You're about to get soaked! And not just from my water Pokémon!", + 4: "We, Team Aqua, exist for the good of all!", + 5: "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" }, "victory": { - 1: "¡Bromeas! ¡No me lo creo!" + 1: "¡Bromeas! ¡No me lo creo!", + 2: "Arrgh, I didn't count on being meddled with by some meddling kid!", + 3: "I lost?! Guess I'll have to swim back to the hideout now...", + 4: "Oh, man, what a disaster... The boss is going to be furious...", + 5: "You beat me... Do you think the boss will make me walk the plank for this?" }, }, "galactic_grunt": { "encounter": { - 1: "¡No desafíes al Equipo Galaxia, te arrepentirás!" + 1: "¡No desafíes al Equipo Galaxia, te arrepentirás!", + 2: "Witness the power of our technology and the future we envision!", + 3: "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + 4: "Get ready to lose!", + 5: "Hope you're ready for a cosmic beatdown!" }, "victory": { - 1: "Me callaste la boca..." + 1: "Me callaste la boca...", + 2: "This setback means nothing in the grand scheme.", + 3: "Our plans are bigger than this defeat.", + 4: "How?!", + 5: "Note to self: practice Pokémon battling, ASAP." }, }, "plasma_grunt": { "encounter": { - 1: "¡El Equipo Plasma no tolerará otros ideales!" + 1: "¡El Equipo Plasma no tolerará otros ideales!", + 2: "If I win against you, release your Pokémon!", + 3: "If you get in the way of Team Plasma, I'll take care of you!", + 4: "Team Plasma will liberate Pokémon from selfish humans like you!", + 5: "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." }, "victory": { - 1: "Plasmaaaaaaaaa!" + 1: "Plasmaaaaaaaaa!", + 2: "How could I lose...", + 3: "...What a weak Pokémon, I'll just have to go steal some better ones!", + 4: "Great plans are always interrupted.", + 5: "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" }, }, "flare_grunt": { "encounter": { - 1: "¡La moda es lo más importante para nosotros!" + 1: "Your Pokémon are no match for the elegance of Team Flare.", + 2: "Hope you brought your sunglasses, because things are about to get bright!", + 3: "Team Flare will cleanse the world of imperfection!", + 4: "Prepare to face the brilliance of Team Flare!", + 5: "Fashion is most important to us!" }, "victory": { - 1: "Me temo que se me avecina un futuro oscuro..." + 1: "The future doesn't look bright for me.", + 2: "Perhaps there's more to battling than I thought. Back to the drawing board.", + 3: "Gahh?! I lost?!", + 4: "Even in defeat, Team Flare's elegance shines through.", + 5: "You may have beaten me, but when I lose, I go out in style!" }, }, "rocket_boss_giovanni_1": { @@ -2513,11 +2758,11 @@ export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `It appears the time has finally come once again.\nYou know why you have come here, do you not? $You were drawn here, because you have been here before.\nCountless times. - $Though, perhaps it can be counted.\nTo be precise, this is in fact your 5,643,853rd cycle. + $Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle. $Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain. $Until now you have yet to succeed, but I sense a different presence in you this time.\n $You are the only one here, though it is as if there is… another. - $Will you finally prove a formidable challenge to me?\nThe challenge I have longed for for millennia? + $Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia? $We begin.`, "firstStageWin": `I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back. $Do not disappoint me.`, diff --git a/src/locales/es/filter-bar.ts b/src/locales/es/filter-bar.ts index 33b60cfa427..b55a35c1adf 100644 --- a/src/locales/es/filter-bar.ts +++ b/src/locales/es/filter-bar.ts @@ -3,7 +3,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "Gen.", "typeFilter": "Tipo", - "dexFilter": "Dex", + "caughtFilter": "Caught", "unlocksFilter": "Otros", "miscFilter": "Misc", "sortFilter": "Orden", @@ -13,9 +13,18 @@ export const filterBar: SimpleTranslationEntries = { "passive": "Passive", "passiveUnlocked": "Pasiva Desbloq.", "passiveLocked": "Pasiva Bloq.", + "costReduction": "Cost Reduction", + "costReductionUnlocked": "Cost Reduction Unlocked", + "costReductionLocked": "Cost Reduction Locked", "ribbon": "Ribbon", "hasWon": "Ya ha ganado", "hasNotWon": "Aún no ha ganado", + "hiddenAbility": "Hidden Ability", + "hasHiddenAbility": "Hidden Ability - Yes", + "noHiddenAbility": "Hidden Ability - No", + "pokerus": "Pokerus", + "hasPokerus": "Pokerus - Yes", + "noPokerus": "Pokerus - No", "sortByNumber": "Núm.", "sortByCost": "Coste", "sortByCandies": "# Caramelos", diff --git a/src/locales/es/move-trigger.ts b/src/locales/es/move-trigger.ts index 3ff93997cc2..a1d9f2e3185 100644 --- a/src/locales/es/move-trigger.ts +++ b/src/locales/es/move-trigger.ts @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "{{pokemonName}} became cloaked in a harsh light!", "bellChimed": "A bell chimed!", "foresawAnAttack": "{{pokemonName}} foresaw\nan attack!", + "isTighteningFocus": "{{pokemonName}} is\ntightening its focus!", "hidUnderwater": "{{pokemonName}} hid\nunderwater!", "soothingAromaWaftedThroughArea": "A soothing aroma wafted through the area!", "sprangUp": "{{pokemonName}} sprang up!", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "{{pokemonName}}'s type\nchanged to match {{targetPokemonName}}'s!", "suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!", "swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!", + "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!", } as const; diff --git a/src/locales/es/settings.ts b/src/locales/es/settings.ts index 407bfab602f..cac4b25c689 100644 --- a/src/locales/es/settings.ts +++ b/src/locales/es/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "Skip Seen Dialogues", "battleStyle": "Battle Style", "enableRetries": "Enable Retries", + "hideIvs": "Hide IV scanner", "tutorials": "Tutorials", "touchControls": "Touch Controls", "vibrations": "Vibrations", diff --git a/src/locales/es/trainers.ts b/src/locales/es/trainers.ts index f881fc04f94..32f966c6359 100644 --- a/src/locales/es/trainers.ts +++ b/src/locales/es/trainers.ts @@ -13,6 +13,25 @@ export const titles: SimpleTranslationEntries = { "rival": "Rival", "professor": "Profesor", "frontier_brain": "As del Frente Batalla", + "rocket_boss": "Team Rocket Boss", + "magma_boss": "Team Magma Boss", + "aqua_boss": "Team Aqua Boss", + "galactic_boss": "Team Galactic Boss", + "plasma_boss": "Team Plasma Boss", + "flare_boss": "Team Flare Boss", + + "rocket_admin": "Team Rocket Admin", + "rocket_admin_female": "Team Rocket Admin", + "magma_admin": "Team Magma Admin", + "magma_admin_female": "Team Magma Admin", + "aqua_admin": "Team Aqua Admin", + "aqua_admin_female": "Team Aqua Admin", + "galactic_commander": "Team Galactic Commander", + "galactic_commander_female": "Team Galactic Commander", + "plasma_sage": "Team Plasma Sage", + "plasma_admin": "Team Plasma Admin", + "flare_admin": "Team Flare Admin", + "flare_admin_female": "Team Flare Admin", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -121,31 +140,21 @@ export const trainerClasses: SimpleTranslationEntries = { "rocket_grunt": "Rocket Grunt", "rocket_grunts": "Rocket Grunts", "rocket_grunt_female": "Rocket Grunt", - "rocket_admin": "Rocket Admin", - "rocket_admin_female": "Rocket Admin", "magma_grunt": "Magma Grunt", "magma_grunt_female": "Magma Grunt", "magma_grunts": "Magma Grunts", - "magma_admin": "Magma Admin", - "magma_admin_female": "Magma Admin", "aqua_grunt": "Aqua Grunt", "aqua_grunt_female": "Aqua Grunt", "aqua_grunts": "Aqua Grunts", - "aqua_admin": "Aqua Admin", - "aqua_admin_female": "Aqua Admin", "galactic_grunt": "Galactic Grunt", "galactic_grunt_female": "Galactic Grunt", "galactic_grunts": "Galactic Grunts", - "galactic_admin": "Galactic Admin", - "galactic_admin_female": "Galactic Admin", "plasma_grunt": "Plasma Grunt", "plasma_grunt_female": "Plasma Grunt", "plasma_grunts": "Plasma Grunts", "flare_grunt": "Flare Grunt", "flare_grunt_female": "Flare Grunt", "flare_grunts": "Flare Grunts", - "flare_admin": "Flare Admin", - "flare_admin_female": "Flare Admin", } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -275,6 +284,30 @@ export const trainerNames: SimpleTranslationEntries = { "rival": "Finn", "rival_female": "Ivy", + // Evil Team Admins + "archer": "Archer", + "ariana": "Ariana", + "proton": "Proton", + "petrel": "Petrel", + "tabitha": "Tabitha", + "courtney": "Courtney", + "shelly": "Shelly", + "matt": "Matt", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturn", + "zinzolin": "Zinzolin", + "rood": "Rood", + "xerosic": "Xerosic", + "bryony": "Bryony", + + "maxie": "Maxie", + "archie": "Archie", + "cyrus": "Cyrus", + "ghetsis": "Ghetsis", + "lysandre": "Lysandre", + + // Double Names "blue_red_double": "Azul y Rojo", "red_blue_double": "Rojo y Azul", diff --git a/src/locales/fr/achv.ts b/src/locales/fr/achv.ts index d5b80700493..a2d7d2378a1 100644 --- a/src/locales/fr/achv.ts +++ b/src/locales/fr/achv.ts @@ -169,6 +169,10 @@ export const PGMachv: AchievementTranslationEntries = { name: "Invaincu", description: "Terminer le jeu en mode classique", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Le stagiaire de 3e", + description: "Terminer le mode Classique avec au moins un Pokémon non-évolué dans l’équipe." + }, "MONO_GEN_ONE": { name: "Le rival originel", @@ -439,6 +443,10 @@ export const PGFachv: AchievementTranslationEntries = { name: "Invaincue", description: "Terminer le jeu en mode classique", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Le stagiaire de 3e", + description: "Terminer le mode Classique avec au moins un Pokémon non-évolué dans l’équipe." + }, "MONO_GEN_ONE": { name: "Le rival originel", diff --git a/src/locales/fr/arena-tag.ts b/src/locales/fr/arena-tag.ts index cc97cb4e34f..62ef203cf68 100644 --- a/src/locales/fr/arena-tag.ts +++ b/src/locales/fr/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "La capacité {{moveName}}\nprotège l’équipe ennemie !", "conditionalProtectApply": "{{pokemonNameWithAffix}} est protégé\npar {{moveName}} !", "matBlockOnAdd": "{{pokemonNameWithAffix}} se prépare\nà utiliser un tatami pour bloquer les attaques !", + "noCritOnAddPlayer": "{{moveName}} immunise votre équipe\ncontre les coups critiques !", + "noCritOnAddEnemy": "{{moveName}} immunise l’équipe ennemie\ncontre les coups critiques !", + "noCritOnRemove": "Les effets d’{{moveName}}\nsur {{pokemonNameWithAffix}} prennent fin !", "wishTagOnAdd": "Le vœu de{{pokemonNameWithAffix}}\nse réalise !", "mudSportOnAdd": "La puissance des capacités\nde type Électrik diminue !", "mudSportOnRemove": "L’effet de Lance-Boue se dissipe !", diff --git a/src/locales/fr/battle.ts b/src/locales/fr/battle.ts index 861dc6fd73c..6a209eb9f08 100644 --- a/src/locales/fr/battle.ts +++ b/src/locales/fr/battle.ts @@ -61,7 +61,7 @@ export const battle: SimpleTranslationEntries = { "hpIsFull": "Les PV de {{pokemonName}}\nsont au maximum !", "skipItemQuestion": "Êtes-vous sûr·e de ne pas vouloir prendre d’objet ?", "eggHatching": "Hein ?", - "ivScannerUseQuestion": "Utiliser le Scanner d’IV sur {{pokemonName}} ?", + "ivScannerUseQuestion": "Utiliser le Scanner d’IV\nsur {{pokemonName}} ?", "wildPokemonWithAffix": "{{pokemonName}} sauvage", "foePokemonWithAffix": "{{pokemonName}} ennemi", "useMove": "{{pokemonNameWithAffix}} utilise\n{{moveName}} !", @@ -74,21 +74,21 @@ export const battle: SimpleTranslationEntries = { "statsAnd": "et", "stats": "Les stats", "statRose_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente !", - "statRose_other": "{{stats}} de {{pokemonNameWithAffix}}\naugmentent !", + "statRose_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent !", "statSharplyRose_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente beaucoup !", - "statSharplyRose_other": "{{stats}} de {{pokemonNameWithAffix}}\naugmentent beaucoup !", + "statSharplyRose_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent beaucoup !", "statRoseDrastically_one": "{{stats}} de {{pokemonNameWithAffix}}\naugmente énormément !", - "statRoseDrastically_other": "{{stats}} de {{pokemonNameWithAffix}}\naugmentent énormément !", + "statRoseDrastically_other": "{{stats}}\nde {{pokemonNameWithAffix}} augmentent énormément !", "statWontGoAnyHigher_one": "{{stats}} de {{pokemonNameWithAffix}}\nne peut plus augmenter !", - "statWontGoAnyHigher_other": "{{stats}} de {{pokemonNameWithAffix}}\nne peuvent plus augmenter !", + "statWontGoAnyHigher_other": "{{stats}}\nde {{pokemonNameWithAffix}} ne peuvent plus augmenter !", "statFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse !", - "statFell_other": "{{stats}} de {{pokemonNameWithAffix}}\nbaissent !", + "statFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent !", "statHarshlyFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse beaucoup !", - "statHarshlyFell_other": "{{stats}} de {{pokemonNameWithAffix}}\nbaissent beaucoup !", + "statHarshlyFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent beaucoup !", "statSeverelyFell_one": "{{stats}} de {{pokemonNameWithAffix}}\nbaisse énormément !", - "statSeverelyFell_other": "{{stats}} de {{pokemonNameWithAffix}}\nbaissent énormément !", + "statSeverelyFell_other": "{{stats}}\nde {{pokemonNameWithAffix}} baissent énormément !", "statWontGoAnyLower_one": "{{stats}} de {{pokemonNameWithAffix}}\nne peut plus baisser !", - "statWontGoAnyLower_other": "{{stats}} de {{pokemonNameWithAffix}}\nne peuvent plus baisser !", + "statWontGoAnyLower_other": "{{stats}}\nde {{pokemonNameWithAffix}} ne peuvent plus baisser !", "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "ppReduced": "Les PP de la capacité {{moveName}}\nde {{targetName}} baissent de {{reduction}} !", "retryBattle": "Voulez-vous réessayer depuis le début du combat ?", diff --git a/src/locales/fr/bgm-name.ts b/src/locales/fr/bgm-name.ts index da344fe001e..e66c8102e46 100644 --- a/src/locales/fr/bgm-name.ts +++ b/src/locales/fr/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries" : "{{name}}", "battle_kanto_champion": "N2B2 - Vs. Maitre de Kanto", "battle_johto_champion": "N2B2 - Vs. Maitre de Johto", - "battle_hoenn_champion": "N2B2 - Vs. Maitre de Hoenn", + "battle_hoenn_champion_g5": "N2B2 - Vs. Maitre de Hoenn", + "battle_hoenn_champion_g6": "ROSA - Vs. Maitre de Hoenn", "battle_sinnoh_champion": "N2B2 - Vs. Maitresse de Sinnoh", "battle_champion_alder": "NB - Vs. Maitre d’Unys", "battle_champion_iris": "N2B2 - Vs. Maitresse d’Unys", diff --git a/src/locales/fr/dialogue.ts b/src/locales/fr/dialogue.ts index ac68aa6413b..8a8707dc4e5 100644 --- a/src/locales/fr/dialogue.ts +++ b/src/locales/fr/dialogue.ts @@ -385,50 +385,98 @@ export const PGMdialogue: DialogueTranslationEntries = { }, "rocket_grunt": { "encounter": { - 1: "Nous sommes de retour !" + 1: "Nous sommes de retour !", + 2: "We're pulling a big job here! Get lost, kid!", + 3: "Hand over your Pokémon, or face the wrath of Team Rocket!", + 4: "You're about to experience the true terror of Team Rocket!", + 5: "Hey, kid! Me am a Team Rocket member kind of guy!" //Use of wrong grammar is deliberate }, "victory": { - 1: "Une fois de plus la Team Rocket s’envole vers d’autres cieux !" + 1: "Une fois de plus la Team Rocket s’envole vers d’autres cieux !", + 2: "Oh no! I dropped the Lift Key!", + 3: "I blew it!", + 4: "My associates won't stand for this!", + 5: "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" //Use of wrong grammar is deliberate. }, }, "magma_grunt": { "encounter": { - 1: "N’espère pas recevoir de la pitié si tu te mets sur le chemin de la Team Magma !" + 1: "N’espère pas recevoir de la pitié si tu te mets sur le chemin de la Team Magma !", + 2: "You'd better not interfere with our plans! We're making the world a better place!", + 3: "You're in the way! Team Magma has no time for kids like you!", + 4: "I hope you brought marshmallows because things are about to heat up!", + 5: "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" }, "victory": { - 1: "Je…?\nJ’ai perdu ?!" + 1: "Je…?\nJ’ai perdu ?!", + 2: "I can't believe I lost! I even skipped lunch for this", + 3: "No way! You're just a kid!", + 4: "Urrrgh... I should've ducked into our hideout right away...", + 5: "You beat me... Do you think the boss will dock my pay for this?" }, }, "aqua_grunt": { "encounter": { - 1: "Aucune pitié si tu te mets sur le chemin de la Team Aqua, même pour un gamin !" + 1: "Aucune pitié si tu te mets sur le chemin de la Team Aqua, même pour un gamin !", + 2: "Grrr... You've got some nerve meddling with Team Aqua!", + 3: "You're about to get soaked! And not just from my water Pokémon!", + 4: "We, Team Aqua, exist for the good of all!", + 5: "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" }, "victory": { - 1: "Comment ça ?" + 1: "Comment ça ?", + 2: "Arrgh, I didn't count on being meddled with by some meddling kid!", + 3: "I lost?! Guess I'll have to swim back to the hideout now...", + 4: "Oh, man, what a disaster... The boss is going to be furious...", + 5: "You beat me... Do you think the boss will make me walk the plank for this?" }, }, "galactic_grunt": { "encounter": { - 1: "Ne te mets pas en travers de la Team Galaxie !" + 1: "Ne te mets pas en travers de la Team Galaxie !", + 2: "Witness the power of our technology and the future we envision!", + 3: "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + 4: "Get ready to lose!", + 5: "Hope you're ready for a cosmic beatdown!" }, "victory": { - 1: "Désactivation…" + 1: "Désactivation…", + 2: "This setback means nothing in the grand scheme.", + 3: "Our plans are bigger than this defeat.", + 4: "How?!", + 5: "Note to self: practice Pokémon battling, ASAP." }, }, "plasma_grunt": { "encounter": { - 1: "Pas de quatiers à ceux qui ne suivent pas notre idéal !" + 1: "Pas de quatiers à ceux qui ne suivent pas notre idéal !", + 2: "If I win against you, release your Pokémon!", + 3: "If you get in the way of Team Plasma, I'll take care of you!", + 4: "Team Plasma will liberate Pokémon from selfish humans like you!", + 5: "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." }, "victory": { - 1: "Plasmaaaaaaaaa !" + 1: "Plasmaaaaaaaaa !", + 2: "How could I lose...", + 3: "...What a weak Pokémon, I'll just have to go steal some better ones!", + 4: "Great plans are always interrupted.", + 5: "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" }, }, "flare_grunt": { "encounter": { - 1: "Le style et le bon gout, il n’y a que ça qui compte !" + 1: "Your Pokémon are no match for the elegance of Team Flare.", + 2: "Hope you brought your sunglasses, because things are about to get bright!", + 3: "Team Flare will cleanse the world of imperfection!", + 4: "Prepare to face the brilliance of Team Flare!", + 5: "Fashion is most important to us!" }, "victory": { - 1: "Mon futur me semble guère radieux." + 1: "The future doesn't look bright for me.", + 2: "Perhaps there's more to battling than I thought. Back to the drawing board.", + 3: "Gahh?! I lost?!", + 4: "Even in defeat, Team Flare's elegance shines through.", + 5: "You may have beaten me, but when I lose, I go out in style!" }, }, "rocket_boss_giovanni_1": { @@ -2890,6 +2938,203 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "I think it's me that's seasick..." }, }, + "archer": { + "encounter": { + 1: "Before you go any further, let's see how you far against us, Team Rocket!", + 2: "I have received reports that your skills are not insignificant. Let's see if they are true.", + 3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." + }, + "victory": { + 1: "What a blunder!", + 2: "With my current skills, I was not up to the task after all.", + 3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." + }, + }, + "ariana": { + "encounter": { + 1: `Hold it right there! We can't someone on the loose." + $It's harmful to Team Rocket's pride, you see.`, + 2: `I don't know or care if what I'm doing is right or wrong... + $I just put my faith in Giovanni and do as I am told`, + 3: "Your trip ends here. I'm going to take you down!" + }, + "victory": { + 1: `Tch, you really are strong. It's too bad. + $If you were to join Team Rocket, you could become an Executive.`, + 2: "I... I'm shattered...", + 3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" + }, + }, + "proton": { + "encounter": { + 1: "What do you want? If you interrupt our work, don't expect any mercy!", + 2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket… + $I strongly urge you not to interfere with our business!`, + 3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" + }, + "victory": { + 1: "The fortress came down!", + 2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", + 3: "I am defeated… But I will not forget this!" + }, + }, + + "petrel": { + "encounter": { + 1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. + $The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni? + $I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, + 2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + 3: "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + 1: "OK, OK. I'll tell you where he is.", + 2: "I… I couldn't do a thing… Giovanni, please forgive me…", + 3: "No, I can't let this affect me. I have to inform the others…" + }, + }, + "tabitha": { + "encounter": { + 1: "Hehehe! So you've come all the way here! But you're too late!", + 2: `Hehehe... Got here already, did you? We underestimated you! But this is it! + $I'm a cut above the Grunts you've seen so far. I'm not stalling for time. + $I'm going to pulverize you!`, + 3: "I'm going to give you a little taste of pain! Resign yourself to it!" + }, + "victory": { + 1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! + $If you get lost now, you won't have to face a sound whipping!`, + 2: "Hehehe... So, I lost, too...", + 3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." + }, + }, + "courtney": { + "encounter": { + 1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", + 2: "... Well then...Deleting...", + 3: "...Ha. ...Analyzing... ...Hah♪" + }, + "victory": { + 1: "... ...Change...the world.", + 2: `As anticipated. Unanticipated. You. Target lock...completed. + $Commencing...experiment. You. Forever. Aha... ♪`, + 3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" + }, + }, + "shelly": { + "encounter": { + 1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? + $You're either absolutely fearless, simply ignorant, or both! + $You're so cute, you're disgusting! I'll put you down`, + 2: "What's this? Who's this spoiled brat?", + 3: "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + 1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. + $We'll have to pull out. But this isn't the last you'll see of Team Aqua! + $We have other plans! Don't you forget it!`, + 2: "Ahhh?! Did I go too easy on you?!", + 3: `Uh. Are you telling me you've upped your game even more during the fight? + $You're a brat with a bright future… My Pokémon and I don't have any strength left to fight… + $Go on… Go and be destroyed by Archie.` + }, + }, + "matt": { + "encounter": { + 1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", + 2: "Oho! You! You're that funny kid!", + 3: "What are you doing here? Did you follow us?" + }, + "victory": { + 1: "All right then, until the Boss has time for you, I'll be your opponent!", + 2: `I can feel it! I can feel it, all right! The strength coming offa you! + $More! I still want more! But looks like we're outta time...`, + 3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" + }, + }, + "mars": { + "encounter": { + 1: "I'm Mars, one of Team Galactic's top Commanders.", + 2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + 3: "Feeling nervous? You should be!" + }, + "victory": { + 1: "This can't be happening! How did I lose?!", + 2: "You have some skill, I'll give you that.", + 3: "Defeated... This was a costly mistake." + } + }, + "jupiter": { + "encounter": { + 1: "Jupiter, Commander of Team Galactic, at your service.", + 2: "Resistance is futile. Team Galactic will prevail!", + 3: "You're trembling... scared already?" + }, + "victory": { + 1: "No way... I lost?!", + 2: "Impressive, you've got guts!", + 3: "Losing like this... How embarrassing." + } + }, + "saturn": { + "encounter": { + 1: "I am Saturn, Commander of Team Galactic.", + 2: "Our mission is absolute. Any hindrance will be obliterated!", + 3: "Is that fear I see in your eyes?" + }, + "victory": { + 1: "Impossible... Defeated by you?!", + 2: "You have proven yourself a worthy adversary.", + 3: "Bestowed in defeat... This is unacceptable." + }}, + "zinzolin": { + "encounter": { + 1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", + 2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", + 3: "You're an impressive Trainer to have made it this far. But it ends here." + }, + "victory": { + 1: "Ghetsis... I have failed you...", + 2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", + 3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } + }, + "rood": { + "encounter": { + 1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + 2: "Oh, this icy wind... I never thought I'd have to fight here!", + 3: "You are a remarkable Trainer to have made it this far. But this is where it ends." + }, + "victory": { + 1: "Ghetsis... I have failed my mission...", + 2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", + 3: "Hm. You are a talented Trainer, but unfortunately not talented enough." + } + }, + "xerosic": { + "encounter": { + 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", + 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", + 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" + }, + "victory": { + 1: "Ah, you're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You did it! To the victor go the spoils!", + 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + 1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + 2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + 3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + 1: "You're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You've done well. Victory is yours.", + 3: "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, "rocket_grunt": { "encounter": { 1: "Nous sommes de retour !" @@ -2898,18 +3143,6 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Une fois de plus la Team Rocket s’envole vers d’autres cieux !" }, }, - "rocket_admin": { - "encounter": { - 1: "Oh? You managed to get this far? You must be quite the trainer.", - 2: "That's quite enough of you playing hero, kid.", - 3: "I'll show you how scary an angry adult can be!" - }, - "victory": { - 1: "No! Forgive me Giovanni!", - 2: "How could this be?", - 3: "Urgh... You were too strong..." - }, - }, "magma_grunt": { "encounter": { 1: "N’espère pas recevoir de la pitié si tu te mets sur le chemin de la Team Magma !" @@ -2918,18 +3151,6 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Je…?\nJ’ai perdu ?!" }, }, - "magma_admin": { - "encounter": { - 1: "Hehehe! So you've come all the way here! But you're too late!", - 2: "You're going to meddle in Team Magma's affairs? You're so cute you're disgusting! I'll put you down kiddy!", - 3: "I'm going to give you a little taste of pain! Resign yourself to it!" - }, - "victory": { - 1: "Hehehe... So I lost...", - 2: "You're disgustingly strong!", - 3: "Ahahaha! Ouch!" - }, - }, "aqua_grunt": { "encounter": { 1: "Aucune pitié si tu te mets sur le chemin de la Team Aqua, même pour une gamine !" @@ -2938,18 +3159,6 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Comment ça ?" }, }, - "aqua_admin": { - "encounter": { - 1: "I'm a cut above the grunts you've seen so far. I'm going to puvlerize you!", - 2: "Hahn? What's this? Who's this spoiled brat?", - 3: "What are you doing here? Did you follow us?" - }, - "victory": { - 1: "So I lost too...", - 2: "Ahhh?! Did I go too easy on you?!", - 3: "Wh-what was that?" - }, - }, "galactic_grunt": { "encounter": { 1: "Ne te mets pas en travers de la Team Galaxie !" @@ -2958,18 +3167,6 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Désactivation…" }, }, - "galactic_admin": { - "encounter": { - 1: "I'm one of Team Galactic's Commanders.", - 2: "Anything that opposes Team Galactic must be crushed! Even the very thought of opposition will not be tolerated!", - 3: "What's the matter? Don't tell me you're shaking?" - }, - "victory": { - 1: "This can't be?! I lost?! You... you uppity brat!", - 2: "You, my friend, are tough!", - 3: "Losing to some child... Being careless cost me too much." - }, - }, "plasma_grunt": { "encounter": { 1: "Pas de quatiers à ceux qui ne suivent pas notre idéal !" @@ -2978,18 +3175,6 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Plasmaaaaaaaaa !" }, }, - "plasma_sage": { - "encounter": { - 1: "You could become a threat to Team Plasma, so we will eliminate you here!", - 2: "Oh, for crying out loud... I didn't expect to have to fight!", - 3: "You're an impressive Trainer to have made it this far." - }, - "victory": { - 1: "Ghetsis...", - 2: "It's bitter cold. I'm shivering. I'm suffering.", - 3: "Hmph. You're a smarter Trainer than I expected." - }, - }, "flare_grunt": { "encounter": { 1: "Le style et le bon gout, il n’y a que ça qui compte !" @@ -2998,18 +3183,6 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Mon futur me semble guère radieux." }, }, - "flare_admin": { - "encounter": { - 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", - 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", - 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" - }, - "victory": { - 1: "You're quite strong. Oh yes-very strong, indeed.", - 2: "Ding-ding-ding! Yup, you did it! To the victor goes the spoils!", - 3: "Wonderful! Amazing! You have tremendous skill and bravery!" - }, - }, "rocket_boss_giovanni_1": { "encounter": { 1: "Bien. Je dois admettre que je suis impressionné de te voir ici !" @@ -5063,8 +5236,8 @@ export const PGFdialogue: DialogueTranslationEntries = { // Dialogue of the endboss of the game when the player character is male (Or unset) export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `Une fois de plus, te revoilà.\nSais-tu que ce n’est point là ta première venue ? - $Tu a été appelé ici parce que t’y est déjà venu.\nUn nombre inimaginable de fois. - $Mais allons-y, faisons le décompte.\nTu en es très précisément à ton 5 643 853e cycle. + $Tu as été appelé ici parce que t’y es déjà venu.\nUn nombre inimaginable de fois. + $Mais allons-y, faisons le décompte.\nTu en es très précisément à ton {{cycleCount}}e cycle. $Chaque cycle réinitialise ton souvenir du précédent.\nMais étrangement, des bribes subsistent en toi. $Jusqu’à maintenant, tu as toujours échoué. Mais je ressens quelque chose de différent cette fois-ci.\n $Tu es la seule présence ici, bien que j’ai le sentiment d’en ressentir… une autre. @@ -5078,8 +5251,8 @@ export const PGMbattleSpecDialogue: SimpleTranslationEntries = { // Dialogue of the endboss of the game when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMbattleSpecDialogue. export const PGFbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `Une fois de plus, te revoilà.\nSais-tu que ce n’est point là ta première venue ? - $Tu a été appelée ici parce que t’y est déjà venue.\nUn nombre inimaginable de fois. - $Mais allons-y, faisons le décompte.\nTu en es très précisément à ton 5 643 853e cycle. + $Tu as été appelée ici parce que t’y es déjà venue.\nUn nombre inimaginable de fois. + $Mais allons-y, faisons le décompte.\nTu en es très précisément à ton {{cycleCount}}e cycle. $Chaque cycle réinitialise ton souvenir du précédent.\nMais étrangement, des bribes subsistent en toi. $Jusqu’à maintenant, tu as toujours échoué. Mais je ressens quelque chose de différent cette fois-ci.\n $Tu es la seule présence ici, bien que j’ai le sentiment d’en ressentir… une autre. diff --git a/src/locales/fr/filter-bar.ts b/src/locales/fr/filter-bar.ts index acbb34e18e8..13656192116 100644 --- a/src/locales/fr/filter-bar.ts +++ b/src/locales/fr/filter-bar.ts @@ -3,7 +3,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "Gen", "typeFilter": "Type", - "dexFilter": "Dex", + "caughtFilter": "Capturés", "unlocksFilter": "Débloq.", "miscFilter": "Divers", "sortFilter": "Tri", @@ -13,12 +13,21 @@ export const filterBar: SimpleTranslationEntries = { "passive": "Passif", "passiveUnlocked": "Passif débloqué", "passiveLocked": "Passif verrouillé", - "ribbon": "Médaille", - "hasWon": "Médaille - Oui", - "hasNotWon": "Médaille - Non", + "costReduction": "Cost Reduction", + "costReductionUnlocked": "Cost Reduction Unlocked", + "costReductionLocked": "Cost Reduction Locked", + "ribbon": "Ruban", + "hasWon": "Ruban - Oui", + "hasNotWon": "Ruban - Non", + "hiddenAbility": "Hidden Ability", + "hasHiddenAbility": "Hidden Ability - Yes", + "noHiddenAbility": "Hidden Ability - No", + "pokerus": "Pokerus", + "hasPokerus": "Pokerus - Yes", + "noPokerus": "Pokerus - No", "sortByNumber": "Par N°", "sortByCost": "Par cout", - "sortByCandies": "Par # bonbons", + "sortByCandies": "Par bonbons", "sortByIVs": "Par IV", "sortByName": "Par nom", }; diff --git a/src/locales/fr/modifier-type.ts b/src/locales/fr/modifier-type.ts index 671a122f992..3635b318336 100644 --- a/src/locales/fr/modifier-type.ts +++ b/src/locales/fr/modifier-type.ts @@ -24,7 +24,7 @@ export const modifierType: ModifierTypeTranslationEntries = { } }, "PokemonReviveModifierType": { - description: "Réanime un Pokémon et restaure {{restorePercent}}% de ses PV.", + description: "Ranime un Pokémon et restaure {{restorePercent}}% de ses PV.", }, "PokemonStatusHealModifierType": { description: "Soigne tous les problèmes de statut d’un Pokémon.", @@ -64,13 +64,13 @@ export const modifierType: ModifierTypeTranslationEntries = { description: "Fait monter toute l’équipe de {{levels}} niveau·x.", }, "PokemonBaseStatBoosterModifierType": { - description: "Augmente de 10% {{statName}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter.", + description: "Augmente de 10% {{statName}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter.", }, "AllPokemonFullHpRestoreModifierType": { - description: "Restaure tous les PV de toute l'équipe.", + description: "Restaure tous les PV de toute l’équipe.", }, "AllPokemonFullReviveModifierType": { - description: "Réanime et restaure tous les PV de tous les Pokémon K.O.", + description: "Ranime et restaure tous les PV de tous les Pokémon K.O. .", }, "MoneyRewardModifierType": { description: "Octroie une {{moneyMultiplier}} somme d’argent.\n({{moneyAmount}} ₽)", @@ -151,9 +151,9 @@ export const modifierType: ModifierTypeTranslationEntries = { "SACRED_ASH": { name: "Cendre Sacrée" }, - "REVIVER_SEED": { name: "Résugraine", description: "Réanime et restaure la moitié des PV de son porteur s’il est mis K.O. par une capacité directe." }, + "REVIVER_SEED": { name: "Résugraine", description: "Ranime et restaure la moitié des PV de son porteur s’il est mis K.O. par une capacité directe." }, - "WHITE_HERB": { name: "White Herb", description: "An item to be held by a Pokémon. It will restore any lowered stat in battle." }, + "WHITE_HERB": { name: "Herbe Blanche", description: "Restaure toute stat ayant subi une baisse en combat." }, "ETHER": { name: "Huile" }, "MAX_ETHER": { name: "Huile Max" }, @@ -184,12 +184,12 @@ export const modifierType: ModifierTypeTranslationEntries = { "SOOTHE_BELL": { name: "Grelot Zen" }, - "SCOPE_LENS": { name: "Lentilscope", description: "Une lentille qui augmente le taux de critiques du porteur." }, - "LEEK": { name: "Poireau", description: "Objet à faire tenir à Canarticho. Un poireau très long et solide qui augmente son taux de critiques." }, + "SCOPE_LENS": { name: "Lentilscope", description: "Une lentille qui augmente d’un cran le taux de critiques du porteur." }, + "LEEK": { name: "Poireau", description: "À faire tenir à Canarticho ou Palarticho. Un poireau très long et solide qui augmente de 2 crans le taux de critiques." }, - "EVIOLITE": { name: "Évoluroc", description: "Un étrange concentré d’évolution qui augmente la Défense et la Défense Spéciale d’un Pokémon pouvant évoluer." }, + "EVIOLITE": { name: "Évoluroc", description: "Augmente de 50% la Défense et Déf. Spé. si le porteur peut évoluer, 25% aux fusions dont une moitié le peut encore." }, - "SOUL_DEW": { name: "Rosée Âme", description: "Augmente de 10% l’influence de la nature d’un Pokémon sur ses statistiques (cumulatif)." }, + "SOUL_DEW": { name: "Rosée Âme", description: "Augmente de 10% l’influence de la nature d’un Pokémon sur ses statistiques. Effet cumulatif." }, "NUGGET": { name: "Pépite" }, "BIG_NUGGET": { name: "Maxi Pépite" }, @@ -199,7 +199,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "GOLDEN_PUNCH": { name: "Poing Doré", description: "50% des dégâts infligés sont convertis en argent." }, "COIN_CASE": { name: "Boite Jetons", description: "Tous les 10 combats, recevez 10% de votre argent en intérêts." }, - "LOCK_CAPSULE": { name: "Poké Écrin", description: "Permet de verrouiller des objets rares si vous relancez les objets proposés." }, + "LOCK_CAPSULE": { name: "Poké Écrin", description: "Permet de conserver la rareté des objets si vous relancez les objets proposés." }, "GRIP_CLAW": { name: "Accro Griffe" }, "WIDE_LENS": { name: "Loupe" }, @@ -220,8 +220,8 @@ export const modifierType: ModifierTypeTranslationEntries = { "LEFTOVERS": { name: "Restes", description: "Soigne à chaque tour 1/16 des PV max d’un Pokémon." }, "SHELL_BELL": { name: "Grelot Coque", description: "Soigne son porteur avec 1/8 des dégâts qu’il inflige à un Pokémon." }, - "TOXIC_ORB": { name: "Orbe Toxique", description: "Un orbe bizarre qui empoisonne gravement son porteur durant le combat." }, - "FLAME_ORB": { name: "Orbe Flamme", description: "Un orbe bizarre qui brule son porteur durant le combat." }, + "TOXIC_ORB": { name: "Orbe Toxique", description: "Empoisonne gravement son porteur à la fin du tour s’il n’a pas déjà de problème de statut." }, + "FLAME_ORB": { name: "Orbe Flamme", description: "Brule son porteur à la fin du tour s’il n’a pas déjà de problème de statut." }, "BATON": { name: "Bâton", description: "Permet de transmettre les effets en cas de changement de Pokémon. Ignore les pièges." }, @@ -242,21 +242,21 @@ export const modifierType: ModifierTypeTranslationEntries = { "ENEMY_ATTACK_POISON_CHANCE": { name: "Jeton Poison" }, "ENEMY_ATTACK_PARALYZE_CHANCE": { name: "Jeton Paralysie" }, "ENEMY_ATTACK_BURN_CHANCE": { name: "Jeton Brulure" }, - "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Jeton Total Soin", description: "Ajoute 2.5% de chances à chaque tour de se soigner d’un problème de statut." }, + "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "Jeton Total Soin", description: "Ajoute 2,5% de chances à chaque tour de se soigner d’un problème de statut." }, "ENEMY_ENDURE_CHANCE": { name: "Jeton Ténacité" }, "ENEMY_FUSED_CHANCE": { name: "Jeton Fusion", description: "Ajoute 1% de chances qu’un Pokémon sauvage soit une fusion." }, }, SpeciesBoosterItem: { - "LIGHT_BALL": { name: "Balle Lumière", description: "Objet à faire tenir à Pikachu. Un orbe énigmatique qui augmente son Attaque et son Attaque Spéciale." }, - "THICK_CLUB": { name: "Masse Os", description: "Objet à faire tenir à Osselait ou Ossatueur. Un os dur qui augmente leur Attaque." }, - "METAL_POWDER": { name: "Poudre Métal", description: "Objet à faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, augmente sa Défense." }, - "QUICK_POWDER": { name: "Poudre Vite", description: "Objet à faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, augmente sa Vitesse." } + "LIGHT_BALL": { name: "Balle Lumière", description: "À faire tenir à Pikachu. Un orbe énigmatique qui double son Attaque et son Atq. Spé. ." }, + "THICK_CLUB": { name: "Masse Os", description: "À faire tenir à Osselait ou Ossatueur. Un os dur qui double leur Attaque." }, + "METAL_POWDER": { name: "Poudre Métal", description: "À faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, double sa Défense." }, + "QUICK_POWDER": { name: "Poudre Vite", description: "À faire tenir à Métamorph. Cette poudre étrange, très fine mais résistante, double sa Vitesse." } }, TempBattleStatBoosterItem: { "x_attack": "Attaque +", "x_defense": "Défense +", - "x_sp_atk": "Atq. Spé. +", - "x_sp_def": "Déf. Spé. +", + "x_sp_atk": "Atq. Spé. +", + "x_sp_def": "Déf. Spé. +", "x_speed": "Vitesse +", "x_accuracy": "Précision +", "dire_hit": "Muscle +", @@ -265,8 +265,8 @@ export const modifierType: ModifierTypeTranslationEntries = { TempBattleStatBoosterStatName: { "ATK": "Attaque", "DEF": "Défense", - "SPATK": "Atq. Spé.", - "SPDEF": "Déf. Spé.", + "SPATK": "Atq. Spé.", + "SPDEF": "Déf. Spé.", "SPD": "Vitesse", "ACC": "Précision", "CRIT": "Taux de critique", diff --git a/src/locales/fr/move-trigger.ts b/src/locales/fr/move-trigger.ts index d1fbda50b03..7f9f3a471c6 100644 --- a/src/locales/fr/move-trigger.ts +++ b/src/locales/fr/move-trigger.ts @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "{{pokemonName}} est entouré\nd’une lumière intense !", "bellChimed": "Un grelot sonne !", "foresawAnAttack": "{{pokemonName}}\nprévoit une attaque !", + "isTighteningFocus": "{{pokemonName}} se concentre\nau maximum !", "hidUnderwater": "{{pokemonName}}\nse cache sous l’eau !", "soothingAromaWaftedThroughArea": "Une odeur apaisante flotte dans l’air !", "sprangUp": "{{pokemonName}}\nse propulse dans les airs !", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "{{pokemonName}} prend le type\nde {{targetPokemonName}} !", "suppressAbilities": "Le talent de {{pokemonName}}\na été rendu inactif !", "swapArenaTags": "Les effets affectant chaque côté du terrain\nont été échangés par {{pokemonName}} !", + "exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !", } as const; diff --git a/src/locales/fr/settings.ts b/src/locales/fr/settings.ts index 9e3bd04923a..2ed697c4e7f 100644 --- a/src/locales/fr/settings.ts +++ b/src/locales/fr/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "Passer dialogues connus", "battleStyle": "Style de combat", "enableRetries": "Activer les réessais", + "hideIvs": "Masquer Scanner d’IV", "tutorials": "Tutoriels", "touchControls": "Contrôles tactiles", "vibrations": "Vibrations", diff --git a/src/locales/fr/starter-select-ui-handler.ts b/src/locales/fr/starter-select-ui-handler.ts index ef685598158..dbd056cbf2a 100644 --- a/src/locales/fr/starter-select-ui-handler.ts +++ b/src/locales/fr/starter-select-ui-handler.ts @@ -7,7 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "Commencer avec ces Pokémon ?", - "confirmExit": "Do you want to exit?", + "confirmExit": "Êtes-vous sûr·e de vouloir quitter ?", "invalidParty": "Cette équipe de départ est invalide !", "gen1": "1G", "gen2": "2G", @@ -35,12 +35,12 @@ export const starterSelectUiHandler: SimpleTranslationEntries = { "unlockPassive": "Débloquer Passif", "reduceCost": "Diminuer le cout", "sameSpeciesEgg": "Acheter un Œuf", - "cycleShiny": ": » Chromatiques", - "cycleForm": ": » Formes", - "cycleGender": ": » Sexes", - "cycleAbility": ": » Talents", - "cycleNature": ": » Natures", - "cycleVariant": ": » Variants", + "cycleShiny": ": Chromatique", + "cycleForm": ": Forme", + "cycleGender": ": Sexe", + "cycleAbility": ": Talent", + "cycleNature": ": Nature", + "cycleVariant": ": Variant", "enablePassive": "Activer Passif", "disablePassive": "Désactiver Passif", "locked": "Verrouillé", diff --git a/src/locales/fr/trainers.ts b/src/locales/fr/trainers.ts index 98bf8cdd2fb..e52921a5546 100644 --- a/src/locales/fr/trainers.ts +++ b/src/locales/fr/trainers.ts @@ -19,6 +19,19 @@ export const titles: SimpleTranslationEntries = { "galactic_boss": "Leader de la Team Galaxie", "plasma_boss": "Leader de la Team Plasma", "flare_boss": "Leader de la Team Flare", + + "rocket_admin": "Admin Team Rocket", + "rocket_admin_female": "Admin Team Rocket", + "magma_admin": "Admin Team Magma", + "magma_admin_female": "Admin Team Magma", + "aqua_admin": "Admin Team Aqua", + "aqua_admin_female": "Admin Team Aqua", + "galactic_commander": "Admin Team Galaxie", + "galactic_commander_female": "Admin Team Galaxie", + "plasma_sage": "Sage Plasma", + "plasma_admin": "Admin Team Plasma", + "flare_admin": "Manager de la Team Flare", + "flare_admin_female": "Manageuse de la Team Flare", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -128,32 +141,21 @@ export const trainerClasses: SimpleTranslationEntries = { "rocket_grunt": "Sbire de la Team Rocket", "rocket_grunt_female": "Sbire de la Team Rocket", "rocket_grunts": "Sbires de la Team Rocket", - "rocket_admin": "Rocket Admin", - "rocket_admin_female": "Rocket Admin", "magma_grunt": "Sbire de la Team Magma", "magma_grunt_female": "Sbire de la Team Magma", "magma_grunts": "Sbires de la Team Magma", - "magma_admin": "Magma Admin", - "magma_admin_female": "Magma Admin", "aqua_grunt": "Sbire de la Team Aqua", "aqua_grunt_female": "Sbire de la Team Aqua", "aqua_grunts": "Sbires de la Team Aqua", - "aqua_admin": "Aqua Admin", - "aqua_admin_female": "Aqua Admin", "galactic_grunt": "Sbire de la Team Galaxie", "galactic_grunt_female": "Sbire de la Team Galaxie", "galactic_grunts": "Sbires de la Team Galaxie", - "galactic_admin": "Galactic Admin", - "galactic_admin_female": "Galactic Admin", "plasma_grunt": "Sbire de la Team Plasma", "plasma_grunt_female": "Sbire de la Team Plasma", "plasma_grunts": "Sbires de la Team Plasma", - "plasma_sage": "Plasma Sage", "flare_grunt": "Sbire de la Team Flare", "flare_grunt_female": "Sbire de la Team Flare", "flare_grunts": "Sbires de la Team Flare", - "flare_admin": "Flare Admin", - "flare_admin_female": "Flare Admin", } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -284,10 +286,29 @@ export const trainerNames: SimpleTranslationEntries = { "rival_female": "Papina", //Litteral translation of ivy, also used as Female name in a North-American indigenous language "maxie": "Max", "archie": "Arthur", - "cyrus": "Hélios", + "cyrus": "Hélio", "ghetsis": "Ghetis", "lysandre": "Lysandre", + // Evil Team Admins + "archer": "Amos", + "ariana": "Ariane", + "proton": "Lance", + "petrel": "Lambda", + "tabitha": "Kelvin", + "courtney": "Courtney", + "shelly": "Sarah", + "matt": "Matthieu", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturne", + "zinzolin": "Lilien", + "rood": "Carmine", + "xerosic": "Xanthin", + "bryony": "Brasénie", + + + // Double Names "blue_red_double": "Blue & Red", "red_blue_double": "Red & Blue", diff --git a/src/locales/fr/tutorial.ts b/src/locales/fr/tutorial.ts index d8940dadd56..f44c05f7d5c 100644 --- a/src/locales/fr/tutorial.ts +++ b/src/locales/fr/tutorial.ts @@ -34,9 +34,9 @@ export const tutorial: SimpleTranslationEntries = { "selectItem": `Après chaque combat, vous avez le choix entre 3 objets\ntirés au sort. Vous ne pouvez en prendre qu’un. $Cela peut être des objets consommables, des objets à\nfaire tenir, ou des objets passifs aux effets permanents. $La plupart des effets des objets non-consommables se cumuleront de diverses manières. - $Certains objets apparaîtront s’ils peuvent être utilisés, comme les objets d’évolution. + $Certains objets apparaitront s’ils peuvent être utilisés, comme les objets d’évolution. $Vous pouvez aussi transférer des objets tenus entre Pokémon en utilisant l’option de transfert. - $L’option de transfert apparaît en bas à droite dès que vous avez obtenu un objet à faire tenir. + $L’option de transfert apparait en bas à droite dès que vous avez obtenu un objet à faire tenir. $Vous pouvez acheter des consommables avec de l’argent.\nPlus vous progressez, plus le choix sera varié. $Choisir un des objets gratuits déclenchera le prochain combat, donc faites bien tous vos achats avant.`, diff --git a/src/locales/it/achv.ts b/src/locales/it/achv.ts index 50baae41790..91222b81579 100644 --- a/src/locales/it/achv.ts +++ b/src/locales/it/achv.ts @@ -99,7 +99,7 @@ export const PGMachv: AchievementTranslationEntries = { }, "MEGA_EVOLVE": { name: "Megamorfosi", - description: "Megaevolvi un pokémon", + description: "Megaevolvi un Pokémon", }, "GIGANTAMAX": { name: "Grosso e Cattivo", @@ -169,6 +169,10 @@ export const PGMachv: AchievementTranslationEntries = { name: "Imbattuto", description: "Vinci in modalità classica", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Bring Your Child To Work Day", + description: "Beat the game in Classic Mode with at least one unevolved party member." + }, "MONO_GEN_ONE": { name: "Rivale Originale", @@ -264,6 +268,10 @@ export const PGMachv: AchievementTranslationEntries = { "MONO_FAIRY": { name: "Follettini e follettine", }, + "FRESH_START": { + name: "First Try!", + description: "Complete the Fresh Start challenge." + } } as const; // Achievement translations for the when the player character is female (it for now uses the same translations as the male version) diff --git a/src/locales/it/arena-tag.ts b/src/locales/it/arena-tag.ts index 8bc2302368a..22612795308 100644 --- a/src/locales/it/arena-tag.ts +++ b/src/locales/it/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!", "conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!", "matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!", + "noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!", + "noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!", + "noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!", "wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!", "mudSportOnAdd": "Electricity's power was weakened!", "mudSportOnRemove": "The effects of Mud Sport\nhave faded.", diff --git a/src/locales/it/bgm-name.ts b/src/locales/it/bgm-name.ts index 01fb86b281d..be9a8f621c7 100644 --- a/src/locales/it/bgm-name.ts +++ b/src/locales/it/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 Kanto Champion Battle", "battle_johto_champion": "B2W2 Johto Champion Battle", - "battle_hoenn_champion": "B2W2 Hoenn Champion Battle", + "battle_hoenn_champion_g5": "B2W2 Hoenn Champion Battle", + "battle_hoenn_champion_g6": "ORAS Hoenn Champion Battle", "battle_sinnoh_champion": "B2W2 Sinnoh Champion Battle", "battle_champion_alder": "BW Unova Champion Battle", "battle_champion_iris": "B2W2 Unova Champion Battle", diff --git a/src/locales/it/challenges.ts b/src/locales/it/challenges.ts index 2643b16d0f7..784791f5425 100644 --- a/src/locales/it/challenges.ts +++ b/src/locales/it/challenges.ts @@ -22,4 +22,10 @@ export const challenges: TranslationEntries = { "desc": "Puoi usare solo Pokémon di tipo {{type}}.", "desc_default": "Puoi usare solo Pokémon del tipo selezionato." }, + "freshStart": { + "name": "Fresh Start", + "desc": "You can only use the original starters, and only as if you had just started PokéRogue.", + "value.0": "Off", + "value.1": "On", + } } as const; diff --git a/src/locales/it/dialogue.ts b/src/locales/it/dialogue.ts index 3861d530c01..1089db4e6f8 100644 --- a/src/locales/it/dialogue.ts +++ b/src/locales/it/dialogue.ts @@ -383,124 +383,297 @@ export const PGMdialogue: DialogueTranslationEntries = { 3: "I think it's me that's seasick..." }, }, - "rocket_grunt": { + "archer": { "encounter": { - 1: "Prepare for trouble!" + 1: "Before you go any further, let's see how you far against us, Team Rocket!", + 2: "I have received reports that your skills are not insignificant. Let's see if they are true.", + 3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." }, "victory": { - 1: "Team Rocket blasting off again!" + 1: "What a blunder!", + 2: "With my current skills, I was not up to the task after all.", + 3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." }, }, - "rocket_admin": { + "ariana": { "encounter": { - 1: "Oh? You managed to get this far? You must be quite the trainer.", - 2: "That's quite enough of you playing hero, kid.", - 3: "I'll show you how scary an angry adult can be!" + 1: `Hold it right there! We can't someone on the loose." + $It's harmful to Team Rocket's pride, you see.`, + 2: `I don't know or care if what I'm doing is right or wrong... + $I just put my faith in Giovanni and do as I am told`, + 3: "Your trip ends here. I'm going to take you down!" }, "victory": { - 1: "No! Forgive me Giovanni!", - 2: "How could this be?", - 3: "Urgh... You were too strong..." + 1: `Tch, you really are strong. It's too bad. + $If you were to join Team Rocket, you could become an Executive.`, + 2: "I... I'm shattered...", + 3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" }, }, - "magma_grunt": { + "proton": { "encounter": { - 1: " If you get in the way of Team Magma, don’t expect any mercy!" + 1: "What do you want? If you interrupt our work, don't expect any mercy!", + 2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket… + $I strongly urge you not to interfere with our business!`, + 3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" }, "victory": { - 1: "Huh? I lost?!" + 1: "The fortress came down!", + 2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", + 3: "I am defeated… But I will not forget this!" }, }, - "magma_admin": { + + "petrel": { + "encounter": { + 1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. + $The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni? + $I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, + 2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + 3: "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + 1: "OK, OK. I'll tell you where he is.", + 2: "I… I couldn't do a thing… Giovanni, please forgive me…", + 3: "No, I can't let this affect me. I have to inform the others…" + }, + }, + "tabitha": { "encounter": { 1: "Hehehe! So you've come all the way here! But you're too late!", - 2: "You're going to meddle in Team Magma's affairs? You're so cute you're disgusting! I'll put you down kiddy!", + 2: `Hehehe... Got here already, did you? We underestimated you! But this is it! + $I'm a cut above the Grunts you've seen so far. I'm not stalling for time. + $I'm going to pulverize you!`, 3: "I'm going to give you a little taste of pain! Resign yourself to it!" }, "victory": { - 1: "Hehehe... So I lost...", - 2: "You're disgustingly strong!", - 3: "Ahahaha! Ouch!" + 1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! + $If you get lost now, you won't have to face a sound whipping!`, + 2: "Hehehe... So, I lost, too...", + 3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." }, }, - "aqua_grunt": { + "courtney": { "encounter": { - 1: "No one who crosses Team Aqua gets any mercy, not even kids!" + 1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", + 2: "... Well then...Deleting...", + 3: "...Ha. ...Analyzing... ...Hah♪" }, "victory": { - 1: "You're kidding me!" + 1: "... ...Change...the world.", + 2: `As anticipated. Unanticipated. You. Target lock...completed. + $Commencing...experiment. You. Forever. Aha... ♪`, + 3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" }, }, - "aqua_admin": { + "shelly": { "encounter": { - 1: "I'm a cut above the grunts you've seen so far. I'm going to puvlerize you!", - 2: "Hahn? What's this? Who's this spoiled brat?", + 1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? + $You're either absolutely fearless, simply ignorant, or both! + $You're so cute, you're disgusting! I'll put you down`, + 2: "What's this? Who's this spoiled brat?", + 3: "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + 1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. + $We'll have to pull out. But this isn't the last you'll see of Team Aqua! + $We have other plans! Don't you forget it!`, + 2: "Ahhh?! Did I go too easy on you?!", + 3: `Uh. Are you telling me you've upped your game even more during the fight? + $You're a brat with a bright future… My Pokémon and I don't have any strength left to fight… + $Go on… Go and be destroyed by Archie.` + }, + }, + "matt": { + "encounter": { + 1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", + 2: "Oho! You! You're that funny kid!", 3: "What are you doing here? Did you follow us?" }, "victory": { - 1: "So I lost too...", - 2: "Ahhh?! Did I go too easy on you?!", - 3: "Wh-what was that?" + 1: "All right then, until the Boss has time for you, I'll be your opponent!", + 2: `I can feel it! I can feel it, all right! The strength coming offa you! + $More! I still want more! But looks like we're outta time...`, + 3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" }, }, - "galactic_grunt": { + "mars": { "encounter": { - 1: "Don't mess with Team Galactic!" + 1: "I'm Mars, one of Team Galactic's top Commanders.", + 2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + 3: "Feeling nervous? You should be!" }, "victory": { - 1: "Shut down..." - }, + 1: "This can't be happening! How did I lose?!", + 2: "You have some skill, I'll give you that.", + 3: "Defeated... This was a costly mistake." + } }, - "galactic_admin": { + "jupiter": { "encounter": { - 1: "I'm one of Team Galactic's Commanders.", - 2: "Anything that opposes Team Galactic must be crushed! Even the very thought of opposition will not be tolerated!", - 3: "What's the matter? Don't tell me you're shaking?" + 1: "Jupiter, Commander of Team Galactic, at your service.", + 2: "Resistance is futile. Team Galactic will prevail!", + 3: "You're trembling... scared already?" }, "victory": { - 1: "This can't be?! I lost?! You... you uppity brat!", - 2: "You, my friend, are tough!", - 3: "Losing to some child... Being careless cost me too much." - }, + 1: "No way... I lost?!", + 2: "Impressive, you've got guts!", + 3: "Losing like this... How embarrassing." + } }, - "plasma_grunt": { + "saturn": { "encounter": { - 1: "We won't tolerate people who have different ideas!" + 1: "I am Saturn, Commander of Team Galactic.", + 2: "Our mission is absolute. Any hindrance will be obliterated!", + 3: "Is that fear I see in your eyes?" }, "victory": { - 1: "Plasmaaaaaaaaa!" - }, - }, - "plasma_sage": { + 1: "Impossible... Defeated by you?!", + 2: "You have proven yourself a worthy adversary.", + 3: "Bestowed in defeat... This is unacceptable." + }}, + "zinzolin": { "encounter": { - 1: "You could become a threat to Team Plasma, so we will eliminate you here!", - 2: "Oh, for crying out loud... I didn't expect to have to fight!", - 3: "You're an impressive Trainer to have made it this far." + 1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", + 2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", + 3: "You're an impressive Trainer to have made it this far. But it ends here." }, "victory": { - 1: "Ghetsis...", - 2: "It's bitter cold. I'm shivering. I'm suffering.", - 3: "Hmph. You're a smarter Trainer than I expected." - }, + 1: "Ghetsis... I have failed you...", + 2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", + 3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } }, - "flare_grunt": { + "rood": { "encounter": { - 1: "Fashion is most important to us!" + 1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + 2: "Oh, this icy wind... I never thought I'd have to fight here!", + 3: "You are a remarkable Trainer to have made it this far. But this is where it ends." }, "victory": { - 1: "The future doesn't look bright for me." - }, + 1: "Ghetsis... I have failed my mission...", + 2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", + 3: "Hm. You are a talented Trainer, but unfortunately not talented enough." + } }, - "flare_admin": { + "xerosic": { "encounter": { 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" }, "victory": { - 1: "You're quite strong. Oh yes-very strong, indeed.", - 2: "Ding-ding-ding! Yup, you did it! To the victor goes the spoils!", + 1: "Ah, you're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You did it! To the victor go the spoils!", 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + 1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + 2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + 3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + 1: "You're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You've done well. Victory is yours.", + 3: "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, + "rocket_grunt": { + "encounter": { + 1: "Prepare for trouble!", + 2: "We're pulling a big job here! Get lost, kid!", + 3: "Hand over your Pokémon, or face the wrath of Team Rocket!", + 4: "You're about to experience the true terror of Team Rocket!", + 5: "Hey, kid! Me am a Team Rocket member kind of guy!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "Team Rocket blasting off again!", + 2: "Oh no! I dropped the Lift Key!", + 3: "I blew it!", + 4: "My associates won't stand for this!", + 5: "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" //Use of wrong grammar is deliberate. + }, + }, + "magma_grunt": { + "encounter": { + 1: "If you get in the way of Team Magma, don’t expect any mercy!", + 2: "You'd better not interfere with our plans! We're making the world a better place!", + 3: "You're in the way! Team Magma has no time for kids like you!", + 4: "I hope you brought marshmallows because things are about to heat up!", + 5: "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" + }, + "victory": { + 1: "Huh? I lost?!", + 2: "I can't believe I lost! I even skipped lunch for this", + 3: "No way! You're just a kid!", + 4: "Urrrgh... I should've ducked into our hideout right away...", + 5: "You beat me... Do you think the boss will dock my pay for this?" + }, + }, + "aqua_grunt": { + "encounter": { + 1: "No one who crosses Team Aqua gets any mercy, not even kids!", + 2: "Grrr... You've got some nerve meddling with Team Aqua!", + 3: "You're about to get soaked! And not just from my water Pokémon!", + 4: "We, Team Aqua, exist for the good of all!", + 5: "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" + }, + "victory": { + 1: "You're kidding me!", + 2: "Arrgh, I didn't count on being meddled with by some meddling kid!", + 3: "I lost?! Guess I'll have to swim back to the hideout now...", + 4: "Oh, man, what a disaster... The boss is going to be furious...", + 5: "You beat me... Do you think the boss will make me walk the plank for this?" + }, + }, + "galactic_grunt": { + "encounter": { + 1: "Don't mess with Team Galactic!", + 2: "Witness the power of our technology and the future we envision!", + 3: "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + 4: "Get ready to lose!", + 5: "Hope you're ready for a cosmic beatdown!" + }, + "victory": { + 1: "Shut down...", + 2: "This setback means nothing in the grand scheme.", + 3: "Our plans are bigger than this defeat.", + 4: "How?!", + 5: "Note to self: practice Pokémon battling, ASAP." + }, + }, + "plasma_grunt": { + "encounter": { + 1: "We won't tolerate people who have different ideas!", + 2: "If I win against you, release your Pokémon!", + 3: "If you get in the way of Team Plasma, I'll take care of you!", + 4: "Team Plasma will liberate Pokémon from selfish humans like you!", + 5: "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." + }, + "victory": { + 1: "Plasmaaaaaaaaa!", + 2: "How could I lose...", + 3: "...What a weak Pokémon, I'll just have to go steal some better ones!", + 4: "Great plans are always interrupted.", + 5: "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" + }, + }, + "flare_grunt": { + "encounter": { + 1: "Your Pokémon are no match for the elegance of Team Flare.", + 2: "Hope you brought your sunglasses, because things are about to get bright!", + 3: "Team Flare will cleanse the world of imperfection!", + 4: "Prepare to face the brilliance of Team Flare!", + 5: "Fashion is most important to us!" + }, + "victory": { + 1: "The future doesn't look bright for me.", + 2: "Perhaps there's more to battling than I thought. Back to the drawing board.", + 3: "Gahh?! I lost?!", + 4: "Even in defeat, Team Flare's elegance shines through.", + 5: "You may have beaten me, but when I lose, I go out in style!" }, }, "rocket_boss_giovanni_1": { @@ -2585,11 +2758,11 @@ export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `It appears the time has finally come once again.\nYou know why you have come here, do you not? $You were drawn here, because you have been here before.\nCountless times. - $Though, perhaps it can be counted.\nTo be precise, this is in fact your 5,643,853rd cycle. + $Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle. $Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain. $Until now you have yet to succeed, but I sense a different presence in you this time.\n $You are the only one here, though it is as if there is… another. - $Will you finally prove a formidable challenge to me?\nThe challenge I have longed for for millennia? + $Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia? $We begin.`, "firstStageWin": `I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back. $Do not disappoint me.`, diff --git a/src/locales/it/filter-bar.ts b/src/locales/it/filter-bar.ts index 88745d2c4f8..c6fcb2f0623 100644 --- a/src/locales/it/filter-bar.ts +++ b/src/locales/it/filter-bar.ts @@ -3,7 +3,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "Gen", "typeFilter": "Tipo", - "dexFilter": "Dex", + "caughtFilter": "Caught", "unlocksFilter": "Altro", "miscFilter": "Misc", "sortFilter": "Ordina", @@ -13,9 +13,18 @@ export const filterBar: SimpleTranslationEntries = { "passive": "Passive", "passiveUnlocked": "Passiva sbloccata", "passiveLocked": "Passiva bloccata", + "costReduction": "Cost Reduction", + "costReductionUnlocked": "Cost Reduction Unlocked", + "costReductionLocked": "Cost Reduction Locked", "ribbon": "Ribbon", "hasWon": "Ribbon - Yes", "hasNotWon": "Ribbon - No", + "hiddenAbility": "Hidden Ability", + "hasHiddenAbility": "Hidden Ability - Yes", + "noHiddenAbility": "Hidden Ability - No", + "pokerus": "Pokerus", + "hasPokerus": "Pokerus - Yes", + "noPokerus": "Pokerus - No", "sortByNumber": "Num. Dex", "sortByCost": "Costo", "sortByCandies": "Caramelle", diff --git a/src/locales/it/move-trigger.ts b/src/locales/it/move-trigger.ts index 60679d844c0..badf2777d6f 100644 --- a/src/locales/it/move-trigger.ts +++ b/src/locales/it/move-trigger.ts @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "{{pokemonName}} è avvolto da una luce intensa!", "bellChimed": " Si sente suonare una campanella!", "foresawAnAttack": "{{pokemonName}} presagisce\nl’attacco imminente!", + "isTighteningFocus": "{{pokemonName}} si concentra al massimo!", "hidUnderwater": "{{pokemonName}} sparisce\nsott’acqua!", "soothingAromaWaftedThroughArea": "Un gradevole profumo si diffonde nell’aria!", "sprangUp": "{{pokemonName}} spicca un gran balzo!", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "{{pokemonName}} assume il tipo\ndi {{targetPokemonName}}!", "suppressAbilities": "L’abilità di {{pokemonName}}\nperde ogni efficacia!", "swapArenaTags": "{{pokemonName}} ha invertito gli effetti attivi\nnelle due metà del campo!", + "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!", } as const; diff --git a/src/locales/it/settings.ts b/src/locales/it/settings.ts index 07cbe6b7f46..2381987d387 100644 --- a/src/locales/it/settings.ts +++ b/src/locales/it/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "Skip Seen Dialogues", "battleStyle": "Battle Style", "enableRetries": "Enable Retries", + "hideIvs": "Hide IV scanner", "tutorials": "Tutorials", "touchControls": "Touch Controls", "vibrations": "Vibrations", diff --git a/src/locales/it/trainers.ts b/src/locales/it/trainers.ts index dde79d694ca..931d11ae216 100644 --- a/src/locales/it/trainers.ts +++ b/src/locales/it/trainers.ts @@ -13,6 +13,25 @@ export const titles: SimpleTranslationEntries = { "rival": "Rivale", "professor": "Professore", "frontier_brain": "Asso lotta", + "rocket_boss": "Team Rocket Boss", + "magma_boss": "Team Magma Boss", + "aqua_boss": "Team Aqua Boss", + "galactic_boss": "Team Galactic Boss", + "plasma_boss": "Team Plasma Boss", + "flare_boss": "Team Flare Boss", + + "rocket_admin": "Team Rocket Admin", + "rocket_admin_female": "Team Rocket Admin", + "magma_admin": "Team Magma Admin", + "magma_admin_female": "Team Magma Admin", + "aqua_admin": "Team Aqua Admin", + "aqua_admin_female": "Team Aqua Admin", + "galactic_commander": "Team Galactic Commander", + "galactic_commander_female": "Team Galactic Commander", + "plasma_sage": "Team Plasma Sage", + "plasma_admin": "Team Plasma Admin", + "flare_admin": "Team Flare Admin", + "flare_admin_female": "Team Flare Admin", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -122,31 +141,22 @@ export const trainerClasses: SimpleTranslationEntries = { "rocket_grunt": "Recluta Team Rocket", "rocket_grunt_female": "Recluta Team Rocket", "rocket_grunts": "Reclute Team Rocket", - "rocket_admin": "Rocket Admin", - "rocket_admin_female": "Rocket Admin", "magma_grunt": "Recluta Team Magma", "magma_grunt_female": "Recluta Team Magma", "magma_grunts": "Reclute Team Magma", - "magma_admin": "Magma Admin", - "magma_admin_female": "Magma Admin", "aqua_grunt": "Recluta Team Idro", "aqua_grunt_female": "Recluta Team Idro", "aqua_grunts": "Recluta Team Idro", - "aqua_admin": "Aqua Admin", - "aqua_admin_female": "Aqua Admin", "galactic_grunt": "Recluta Team Galassia", "galactic_grunt_female": "Recluta Team Galassia", "galactic_grunts": "Reclute Team Galassia", - "galactic_admin": "Galactic Admin", - "galactic_admin_female": "Galactic Admin", "plasma_grunt": "Seguace Plasma", "plasma_grunt_female": "Seguace Plasma", "plasma_grunts": "Seguaci Plasma", "flare_grunt": "Recluta Team Flare", "flare_grunt_female": "Recluta Team Flare", "flare_grunts": "Reclute Team Flare", - "flare_admin": "Flare Admin", - "flare_admin_female": "Flare Admin", + } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -276,6 +286,28 @@ export const trainerNames: SimpleTranslationEntries = { "rival": "Finn", "rival_female": "Ivy", + // Evil Team Admins + "archer": "Archer", + "ariana": "Ariana", + "proton": "Proton", + "petrel": "Petrel", + "tabitha": "Tabitha", + "courtney": "Courtney", + "shelly": "Shelly", + "matt": "Matt", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturn", + "zinzolin": "Zinzolin", + "rood": "Rood", + "xerosic": "Xerosic", + "bryony": "Bryony", + + "maxie": "Maxie", + "archie": "Archie", + "cyrus": "Cyrus", + "ghetsis": "Ghetsis", + "lysandre": "Lysandre", // Double Names "blue_red_double": "Blu & Rosso", diff --git a/src/locales/ja/ability-trigger.ts b/src/locales/ja/ability-trigger.ts new file mode 100644 index 00000000000..445f09cbab7 --- /dev/null +++ b/src/locales/ja/ability-trigger.ts @@ -0,0 +1,62 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const abilityTriggers: SimpleTranslationEntries = { + "blockRecoilDamage" : "{{pokemonName}}は {{abilityName}}で\nはんどうダメージを うけない!", + "badDreams": "{{pokemonName}}は ナイトメアに うなされている!", + "costar": "{{pokemonName}} copied {{allyName}}'s stat changes!", + "iceFaceAvoidedDamage": "{{pokemonName}}は\n{{abilityName}}で ダメージを うけない!", + "perishBody": "{{pokemonName}}の {{abilityName}}で\nおたがいは 3ターンごに ほろびてしまう!", + "poisonHeal": "{{pokemonName}}は {{abilityName}}で\nかいふくした!", + "trace": "{{pokemonName}} copied {{targetName}}'s\n{{abilityName}}!", + "windPowerCharged": "{{pokemonName}}は\n{{moveName}}を うけて じゅうでんした!", + "quickDraw": "{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", + "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", + "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", + "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", + "postDefendDisguise": "{{pokemonNameWithAffix}}'s disguise was busted!", + "moveImmunity": "It doesn't affect {{pokemonNameWithAffix}}!", + "reverseDrain": "{{pokemonNameWithAffix}} sucked up the liquid ooze!", + "postDefendTypeChange": "{{pokemonNameWithAffix}}'s {{abilityName}}\nmade it the {{typeName}} type!", + "postDefendContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", + "postDefendAbilitySwap": "{{pokemonNameWithAffix}} swapped\nabilities with its target!", + "postDefendAbilityGive": "{{pokemonNameWithAffix}} gave its target\n{{abilityName}}!", + "postDefendMoveDisable": "{{pokemonNameWithAffix}}'s {{moveName}}\nwas disabled!", + "pokemonTypeChange": "{{pokemonNameWithAffix}} transformed into the {{moveType}} type!", + "postAttackStealHeldItem": "{{pokemonNameWithAffix}} stole\n{{defenderName}}'s {{stolenItemType}}!", + "postDefendStealHeldItem": "{{pokemonNameWithAffix}} stole\n{{attackerName}}'s {{stolenItemType}}!", + "copyFaintedAllyAbility": "{{pokemonNameWithAffix}}'s {{abilityName}} was taken over!", + "intimidateImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}} prevented it from being Intimidated!", + "postSummonAllyHeal": "{{pokemonNameWithAffix}} drank down all the\nmatcha that {{pokemonName}} made!", + "postSummonClearAllyStats": "{{pokemonNameWithAffix}}'s stat changes\nwere removed!", + "postSummonTransform": "{{pokemonNameWithAffix}} transformed\ninto {{targetName}}!", + "protectStat": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents lowering its {{statName}}!", + "statusEffectImmunityWithName": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents {{statusEffectName}}!", + "statusEffectImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents status problems!", + "battlerTagImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents {{battlerTagName}}!", + "forewarn": "{{pokemonNameWithAffix}} was forewarned about {{moveName}}!", + "frisk": "{{pokemonNameWithAffix}} frisked {{opponentName}}'s {{opponentAbilityName}}!", + "postWeatherLapseHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", + "postWeatherLapseDamage": "{{pokemonNameWithAffix}} is hurt\nby its {{abilityName}}!", + "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}} harvested one {{berryName}}!", + "postTurnHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", + "fetchBall": "{{pokemonNameWithAffix}} found a\n{{pokeballName}}!", + "healFromBerryUse": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP!", + "arenaTrap": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents switching!", + "postBattleLoot": "{{pokemonNameWithAffix}} picked up\n{{itemName}}!", + "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", + "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", + "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", + "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", + "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", + "postSummonTeravolt": "{{pokemonNameWithAffix}} is radiating a bursting aura!", + "postSummonDarkAura": "{{pokemonNameWithAffix}} is radiating a Dark Aura!", + "postSummonFairyAura": "{{pokemonNameWithAffix}} is radiating a Fairy Aura!", + "postSummonNeutralizingGas": "{{pokemonNameWithAffix}}'s Neutralizing Gas filled the area!", + "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} has two Abilities!", + "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} has two Abilities!", + "postSummonVesselOfRuin": "{{pokemonNameWithAffix}}'s Vessel of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "postSummonSwordOfRuin": "{{pokemonNameWithAffix}}'s Sword of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}'s Tablets of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}'s Beads of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", +} as const; diff --git a/src/locales/ja/ability.ts b/src/locales/ja/ability.ts new file mode 100644 index 00000000000..043ca6a53ac --- /dev/null +++ b/src/locales/ja/ability.ts @@ -0,0 +1,1244 @@ +import { AbilityTranslationEntries } from "#app/interfaces/locales"; + +export const ability: AbilityTranslationEntries = { + stench: { + name: "あくしゅう", + description: "臭い においを 放つことによって 攻撃した ときに 相手を ひるませることが ある。", + }, + drizzle: { + name: "あめふらし", + description: "登場 したときに 天気を 雨に する。", + }, + speedBoost: { + name: "かそく", + description: "毎ターン 素早さが 上がる。", + }, + battleArmor: { + name: "カブトアーマー", + description: "硬い 甲羅に 守られて 相手の 攻撃が 急所に 当たらない。", + }, + sturdy: { + name: "がんじょう", + description: "相手の 技を 受けても 一撃で 倒されることが ない。 一撃必殺技も 効かない。", + }, + damp: { + name: "しめりけ", + description: "あたりを 湿らせることに よって じばく などの 爆発する 技を だれも 使えなくなる。", + }, + limber: { + name: "じゅうなん", + description: "柔軟な 体によって まひ状態に ならない。", + }, + sandVeil: { + name: "すながくれ", + description: "砂あらしの とき 回避率が 上がる。", + }, + static: { + name: "せいでんき", + description: "静電気を 体に まとい 触った 相手を まひさせる ことがある。", + }, + voltAbsorb: { + name: "ちくでん", + description: "でんきタイプの 技を 受けると ダメージを 受けずに 回復する。", + }, + waterAbsorb: { + name: "ちょすい", + description: "みずタイプの 技を 受けると ダメージを 受けずに 回復する。", + }, + oblivious: { + name: "どんかん", + description: "鈍感なので メロメロや ちょうはつ状態に ならない。", + }, + cloudNine: { + name: "ノーてんき", + description: "あらゆる 天気の 影響が なくなって しまう。", + }, + compoundEyes: { + name: "ふくがん", + description: "複眼を 持っているため 技の 命中率が 上がる。", + }, + insomnia: { + name: "ふみん", + description: "眠れない 体質 なので ねむり状態に ならない。", + }, + colorChange: { + name: "へんしょく", + description: "相手から 受けた 技の タイプに 自分の タイプが 変化 する。", + }, + immunity: { + name: "めんえき", + description: "体内に 免疫を 持っているため どく状態に ならない。", + }, + flashFire: { + name: "もらいび", + description: "ほのおタイプの 技を 受けると 炎を もらい 自分が 出す ほのおタイプの 技が 強くなる。", + }, + shieldDust: { + name: "りんぷん", + description: "りんぷんに 守られて 技の 追加効果を 受けなくなる。", + }, + ownTempo: { + name: "マイペース", + description: "マイペースなので こんらん状態に ならない。", + }, + suctionCups: { + name: "きゅうばん", + description: "吸盤で 地面に 張り付き ポケモンを 入れ替えさせる 技や 道具が 効かなくなる。", + }, + intimidate: { + name: "いかく", + description: "登場 したとき 威嚇して 相手を 萎縮させ 相手の 攻撃を 下げて しまう。", + }, + shadowTag: { + name: "かげふみ", + description: "相手の 影を 踏み 逃げたり 交代 できなくする。", + }, + roughSkin: { + name: "さめはだ", + description: "攻撃を 受けたとき 自分に 触れた 相手を ざらざらの 肌で キズつける。", + }, + wonderGuard: { + name: "ふしぎなまもり", + description: "効果バツグンの 技しか 当たらない 不思議な 力。", + }, + levitate: { + name: "ふゆう", + description: "地面から 浮くことによって じめんタイプの 技を 受けない。", + }, + effectSpore: { + name: "ほうし", + description: "攻撃で 自分に 触れた 相手を どくや まひや ねむり状態に する ことがある。", + }, + synchronize: { + name: "シンクロ", + description: "自分が なってしまった どくや まひや やけどを 相手に うつす。", + }, + clearBody: { + name: "クリアボディ", + description: "相手の 技や 特性で 能力を 下げられない。", + }, + naturalCure: { + name: "しぜんかいふく", + description: "手持ちに ひっこむと 状態異常が 治る。", + }, + lightningRod: { + name: "ひらいしん", + description: "でんきタイプの 技を 自分に 寄せつけ ダメージを 受けずに 特攻が 上がる。", + }, + sereneGrace: { + name: "てんのめぐみ", + description: "天の恵みの おかげで 技の 追加効果が でやすい。", + }, + swiftSwim: { + name: "すいすい", + description: "天気が 雨のとき 素早さが 上がる。", + }, + chlorophyll: { + name: "ようりょくそ", + description: "天気が 晴れのとき 素早さが 上がる。", + }, + illuminate: { + name: "はっこう", + description: "あたりを 明るくすることで 命中率を 下げられない。", + }, + trace: { + name: "トレース", + description: "登場 したとき 相手の 特性を トレースして 同じ 特性に なる。", + }, + hugePower: { + name: "ちからもち", + description: "物理攻撃の 威力が 2倍になる。", + }, + poisonPoint: { + name: "どくのトゲ", + description: "自分に 触った 相手を どく状態に することがある。", + }, + innerFocus: { + name: "せいしんりょく", + description: "鍛えられた 精神に よって 相手の 攻撃に ひるまない。", + }, + magmaArmor: { + name: "マグマのよろい", + description: "熱い マグマを 身にまとい こおり状態に ならない。", + }, + waterVeil: { + name: "みずのベール", + description: "水のベールを 身にまとい やけど状態に ならない。", + }, + magnetPull: { + name: "じりょく", + description: "はがねタイプの ポケモンを 磁力で 引きつけて 逃げられなくする。", + }, + soundproof: { + name: "ぼうおん", + description: "音を 遮断 することに よって 音の 攻撃を 受けない。", + }, + rainDish: { + name: "あめうけざら", + description: "天気が 雨のとき 少しずつ HPを 回復する。", + }, + sandStream: { + name: "すなおこし", + description: "登場 したとき 天気を 砂あらしにする。", + }, + pressure: { + name: "プレッシャー", + description: "プレッシャーを あたえて 相手の 使う 技の PPを 多く 減らす。", + }, + thickFat: { + name: "あついしぼう", + description: "厚い 脂肪で 守られているので ほのおタイプと こおりタイプの 技の ダメージを 半減させる。", + }, + earlyBird: { + name: "はやおき", + description: "ねむり状態に なっても 2倍の 早さで 目覚める ことが できる。", + }, + flameBody: { + name: "ほのおのからだ", + description: "自分に 触った 相手を やけど状態に する ことがある。", + }, + runAway: { + name: "にげあし", + description: "野生の ポケモンから 必ず 逃げられる。", + }, + keenEye: { + name: "するどいめ", + description: "鋭い 目の おかげで 命中率を 下げられない。", + }, + hyperCutter: { + name: "かいりきバサミ", + description: "力自慢の ハサミを 持っているので 相手に 攻撃を 下げられない。", + }, + pickup: { + name: "ものひろい", + description: "戦闘が 終わったとき 相手の 持った 道具を 一つ 拾ってくることが ある。", + }, + truant: { + name: "なまけ", + description: "技を 出すと 次の ターンは 休んでしまう。", + }, + hustle: { + name: "はりきり", + description: "自分の 攻撃が 高くなるが 命中率が 下がる。", + }, + cuteCharm: { + name: "メロメロボディ", + description: "自分に 触った 相手を メロメロに することが ある。", + }, + plus: { + name: "プラス", + description: "プラスか マイナスの 特性を 持つ ポケモンが 仲間に いると 自分の 特攻が 上がる。", + }, + minus: { + name: "マイナス", + description: "プラスか マイナスの 特性を 持つ ポケモンが 仲間に いると 自分の 特攻が 上がる。", + }, + forecast: { + name: "てんきや", + description: "天気の 影響を 受けて みずタイプ ほのおタイプ こおりタイプの どれかに 変化する。", + }, + stickyHold: { + name: "ねんちゃく", + description: "粘着質の 体に 道具が くっついているため 相手に 道具を 奪われない。", + }, + shedSkin: { + name: "だっぴ", + description: "体の 皮を 脱ぎ捨てることで 状態異常を 治すことが ある。", + }, + guts: { + name: "こんじょう", + description: "状態異常に なると 根性を だして 攻撃が 上がる。", + }, + marvelScale: { + name: "ふしぎなうろこ", + description: "状態異常に なると 不思議なウロコが 反応して 防御が 上がる。", + }, + liquidOoze: { + name: "ヘドロえき", + description: "ヘドロ液を 吸い取った 相手は 強烈な 悪臭で ダメージを 受けて HPを 減らす。", + }, + overgrow: { + name: "しんりょく", + description: "HPが 減ったとき くさタイプの 技の 威力が 上がる。", + }, + blaze: { + name: "もうか", + description: "HPが 減ったとき ほのおタイプの 技の 威力が 上がる。", + }, + torrent: { + name: "げきりゅう", + description: "HPが 減ったとき みずタイプの 技の 威力が 上がる。", + }, + swarm: { + name: "むしのしらせ", + description: "HPが 減ったとき むしタイプの 技の 威力が 上がる。", + }, + rockHead: { + name: "いしあたま", + description: "反動を 受ける 技を 出しても HPが 減らない。", + }, + drought: { + name: "ひでり", + description: "登場 したときに 天気を 晴れに する。", + }, + arenaTrap: { + name: "ありじごく", + description: "戦闘で 相手を 逃げられなくする。", + }, + vitalSpirit: { + name: "やるき", + description: "やる気を だすことに よって ねむり状態に ならない。", + }, + whiteSmoke: { + name: "しろいけむり", + description: "白い煙に 守られて 相手に 能力を 下げられない。", + }, + purePower: { + name: "ヨガパワー", + description: "ヨガの 力で 物理攻撃の 威力が 2倍に なる。", + }, + shellArmor: { + name: "シェルアーマー", + description: "硬い 殻に 守られ 相手の 攻撃が 急所に 当たらない。", + }, + airLock: { + name: "エアロック", + description: "あらゆる 天気の 影響が なくなって しまう。", + }, + tangledFeet: { + name: "ちどりあし", + description: "こんらん状態の ときは 回避率が アップする。", + }, + motorDrive: { + name: "でんきエンジン", + description: "でんきタイプの 技を 受けると ダメージを 受けずに 素早さが 上がる。", + }, + rivalry: { + name: "とうそうしん", + description: "性別が 同じだと 闘争心を 燃やして 強くなる。 性別が 違うと 弱くなる。", + }, + steadfast: { + name: "ふくつのこころ", + description: "ひるむ たびに 不屈の心を 燃やして 素早さが 上がる。", + }, + snowCloak: { + name: "ゆきがくれ", + description: "天気が ゆきのとき 回避率が 上がる。", + }, + gluttony: { + name: "くいしんぼう", + description: "HPが 少なくなったら 食べる きのみを HP 半分の 時に 食べてしまう。", + }, + angerPoint: { + name: "いかりのつぼ", + description: "急所に 攻撃が 当たると 怒りくるって 攻撃力が 最大に なる。", + }, + unburden: { + name: "かるわざ", + description: "持っていた 道具が なくなると 素早さが 上がる。", + }, + heatproof: { + name: "たいねつ", + description: "耐熱の 体に よって ほのおタイプの 技の 威力を 半減させる。", + }, + simple: { + name: "たんじゅん", + description: "能力 変化が いつもの 2倍に なる。", + }, + drySkin: { + name: "かんそうはだ", + description: "天気が 雨の時や みずタイプの 技で HPが 回復し はれの時や ほのおタイプの 技で 減ってしまう。", + }, + download: { + name: "ダウンロード", + description: "相手の 防御と 特防を くらべて 低い ほうの 能力に あわせて 自分の 攻撃か 特攻を 上げる。", + }, + ironFist: { + name: "てつのこぶし", + description: "パンチを 使う 技の 威力が 上がる。", + }, + poisonHeal: { + name: "ポイズンヒール", + description: "どく状態に なると HPが 減らずに 増えていく。", + }, + adaptability: { + name: "てきおうりょく", + description: "自分と おなじ タイプの 技の 威力が 上がる。", + }, + skillLink: { + name: "スキルリンク", + description: "連続技を 使うと いつも 最高回数 出すことが できる。", + }, + hydration: { + name: "うるおいボディ", + description: "天気が 雨のとき 状態異常が 治る。", + }, + solarPower: { + name: "サンパワー", + description: "天気が 晴れると 特攻が 上がるが 毎ターン HPが 減る。", + }, + quickFeet: { + name: "はやあし", + description: "状態異常に なると 素早さが 上がる。", + }, + normalize: { + name: "ノーマルスキン", + description: "どんな タイプの 技でも すべて ノーマルタイプに なる。 威力が 少し 上がる。", + }, + sniper: { + name: "スナイパー", + description: "攻撃を 急所に 当てると 威力が さらに 上がる。", + }, + magicGuard: { + name: "マジックガード", + description: "攻撃 以外では ダメージを 受けない。", + }, + noGuard: { + name: "ノーガード", + description: "ノーガード戦法に よって お互いの 出す 技が かならず 当たる ようになる。", + }, + stall: { + name: "あとだし", + description: "技を 出す 順番が かならず 最後に なる。", + }, + technician: { + name: "テクニシャン", + description: "威力が 低い 技の 威力を 高くして 攻撃できる。", + }, + leafGuard: { + name: "リーフガード", + description: "天気が 晴れのときは 状態異常に ならない。", + }, + klutz: { + name: "ぶきよう", + description: "持っている 道具を 使うことが できない。", + }, + moldBreaker: { + name: "かたやぶり", + description: "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。", + }, + superLuck: { + name: "きょううん", + description: "強運を 持っているため 相手の 急所に 攻撃が 当たりやすい。", + }, + aftermath: { + name: "ゆうばく", + description: "ひんしに なったとき 触った 相手に ダメージを あたえる。", + }, + anticipation: { + name: "きけんよち", + description: "相手の 持つ 危険な 技を 察知する ことができる。", + }, + forewarn: { + name: "よちむ", + description: "登場 したとき 相手の 持つ 技を ひとつだけ 読み取る。", + }, + unaware: { + name: "てんねん", + description: "相手の 能力の 変化を 無視して 攻撃が できる。", + }, + tintedLens: { + name: "いろめがね", + description: "効果が いまひとつの 技を 通常の 威力で 出すことが できる。", + }, + filter: { + name: "フィルター", + description: "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。", + }, + slowStart: { + name: "スロースタート", + description: "5ターンの あいだ 攻撃と 素早さが 半分に なる。", + }, + scrappy: { + name: "きもったま", + description: "ゴーストタイプの ポケモンに ノーマルタイプと かくとうタイプの 技を 当てることが できる。", + }, + stormDrain: { + name: "よびみず", + description: "みずタイプの 技を 自分に よせつけ ダメージは 受けずに 特攻が 上がる。", + }, + iceBody: { + name: "アイスボディ", + description: "天気が ゆきのとき HPを 少しずつ 回復 する。", + }, + solidRock: { + name: "ハードロック", + description: "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。", + }, + snowWarning: { + name: "ゆきふらし", + description: "登場 したときに 天気を ゆきに する。", + }, + honeyGather: { + name: "みつあつめ", + description: "戦闘が 終わったとき あまいミツを 拾う。そのあまいミツが 売られて お金を もらう。", + }, + frisk: { + name: "おみとおし", + description: "登場 したとき 相手の 持ち物を 見通すことが できる。", + }, + reckless: { + name: "すてみ", + description: "反動で ダメージを 受ける 技の 威力が 上がる。", + }, + multitype: { + name: "マルチタイプ", + description: "持っている プレートや Zクリスタルの タイプによって 自分の タイプが 変わる。", + }, + flowerGift: { + name: "フラワーギフト", + description: "天気が 晴れのとき 自分と 味方の 攻撃と 特防の 能力が 上がる。", + }, + badDreams: { + name: "ナイトメア", + description: "ねむり状態の 相手に ダメージを あたえる。", + }, + pickpocket: { + name: "わるいてぐせ", + description: "触られた 相手の 道具を 盗んで しまう。", + }, + sheerForce: { + name: "ちからずく", + description: "技の 追加効果は なくなるが そのぶん 高い 威力で 技を 出すことが できる。", + }, + contrary: { + name: "あまのじゃく", + description: "能力の 変化が 逆転して 上がるときに 下がり 下がるときに 上がる。", + }, + unnerve: { + name: "きんちょうかん", + description: "相手を 緊張させて きのみを 食べられなく させる。", + }, + defiant: { + name: "まけんき", + description: "能力を 下げられると 攻撃が ぐーんと 上がる。", + }, + defeatist: { + name: "よわき", + description: "HPが 半分に なると 弱気に なって 攻撃と 特攻が 半減する。", + }, + cursedBody: { + name: "のろわれボディ", + description: "攻撃を 受けると 相手の 技を かなしばり状態に することが ある。", + }, + healer: { + name: "いやしのこころ", + description: "状態異常の 味方を たまに 治してあげる。", + }, + friendGuard: { + name: "フレンドガード", + description: "味方の ダメージを 減らすことが できる。", + }, + weakArmor: { + name: "くだけるよろい", + description: "物理技で ダメージを 受けると 防御が 下がり 素早さが ぐーんと 上がる。", + }, + heavyMetal: { + name: "ヘヴィメタル", + description: "自分の 重さが 2倍に なる。", + }, + lightMetal: { + name: "ライトメタル", + description: "自分の 重さが 半分に なる。", + }, + multiscale: { + name: "マルチスケイル", + description: "HPが 満タンの ときに 受ける ダメージが 少なくなる。", + }, + toxicBoost: { + name: "どくぼうそう", + description: "どく状態に なったとき 物理技の 威力が 上がる。", + }, + flareBoost: { + name: "ねつぼうそう", + description: "やけど状態に なったとき 特殊技の 威力が 上がる。", + }, + harvest: { + name: "しゅうかく", + description: "使った きのみを 何回も 作りだす。", + }, + telepathy: { + name: "テレパシー", + description: "味方の 攻撃を 読み取って 技を 回避する。", + }, + moody: { + name: "ムラっけ", + description: "毎ターン 能力の どれかが ぐーんと 上がって どれかが 下がる。", + }, + overcoat: { + name: "ぼうじん", + description: "すなあらしや あられなどの ダメージを 受けない。 粉の 技を 受けない。", + }, + poisonTouch: { + name: "どくしゅ", + description: "触る だけで 相手を どく 状態に することがある。", + }, + regenerator: { + name: "さいせいりょく", + description: "手持ちに 引っ込むと HPが 少し 回復する。", + }, + bigPecks: { + name: "はとむね", + description: "防御を 下げる 効果を 受けない。", + }, + sandRush: { + name: "すなかき", + description: "天気が すなあらし のとき 素早さが 上がる。", + }, + wonderSkin: { + name: "ミラクルスキン", + description: "変化技を 受けにくい 体に なっている。", + }, + analytic: { + name: "アナライズ", + description: "いちばん 最後に 技を 出すと 技の 威力が 上がる。", + }, + illusion: { + name: "イリュージョン", + description: "手持ちの いちばん うしろに いる ポケモンに なりきって 登場して 相手を 化かす。", + }, + imposter: { + name: "かわりもの", + description: "目の前の ポケモンに 変身 してしまう。", + }, + infiltrator: { + name: "すりぬけ", + description: "相手の 壁や 身代わりを すりぬけて 攻撃 できる", + }, + mummy: { + name: "ミイラ", + description: "相手に 触られると 相手を ミイラに してしまう。", + }, + moxie: { + name: "じしんかじょう", + description: "相手を 倒すと 自信が ついて 攻撃が 上がる。", + }, + justified: { + name: "せいぎのこころ", + description: "あくタイプの 攻撃を 受けると 正義感で 攻撃が 上がる。", + }, + rattled: { + name: "びびり", + description: "あく ゴースト むしタイプの 攻撃を 受けたり いかくを されると びびって 素早さが 上がる。", + }, + magicBounce: { + name: "マジックミラー", + description: "相手に だされた 変化技を 受けずに そのまま 返す ことが できる。", + }, + sapSipper: { + name: "そうしょく", + description: "くさタイプの 技を 受けると ダメージを 受けずに 攻撃が 上がる。", + }, + prankster: { + name: "いたずらごころ", + description: "変化技を 先制で 出すことが できる。", + }, + sandForce: { + name: "すなのちから", + description: "天気が すなあらしの とき いわタイプと じめんタイプと はがねタイプの 威力が 上がる。", + }, + ironBarbs: { + name: "てつのトゲ", + description: "自分に 触った 相手に 鉄のトゲで ダメージを あたえる。", + }, + zenMode: { + name: "ダルマモード", + description: "HPが 半分 以下に なると 姿が 変化する。", + }, + victoryStar: { + name: "しょうりのほし", + description: "自分や 味方の 命中率が 上がる。", + }, + turboblaze: { + name: "ターボブレイズ", + description: "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。", + }, + teravolt: { + name: "テラボルテージ", + description: "相手の 特性に ジャマされる ことなく 相手に 技を 出すことが できる。", + }, + aromaVeil: { + name: "アロマベール", + description: "自分と 味方への メンタル 攻撃を 防ぐことが できる。", + }, + flowerVeil: { + name: "フラワーベール", + description: "味方の 草ポケモンは 能力が 下がらず 状態異常にも ならない。", + }, + cheekPouch: { + name: "ほおぶくろ", + description: "どんな きのみでも 食べると HPも 回復する。", + }, + protean: { + name: "へんげんじざい", + description: "自分が 出す 技と 同じ タイプに 変化する。", + }, + furCoat: { + name: "ファーコート", + description: "相手から 受ける 物理技の ダメージが 半分に なる。", + }, + magician: { + name: "マジシャン", + description: "技を 当てた 相手の 道具を 奪ってしまう。", + }, + bulletproof: { + name: "ぼうだん", + description: "相手の 弾や 爆弾などの 技を 防ぐことが できる。", + }, + competitive: { + name: "かちき", + description: "能力を 下げられると 特攻が ぐーんと 上がる。", + }, + strongJaw: { + name: "がんじょうあご", + description: "あごが 頑丈で 噛む 技の 威力が 高くなる。", + }, + refrigerate: { + name: "フリーズスキン", + description: "ノーマルタイプの 技が こおりタイプに なる。 威力が 少し 上がる。", + }, + sweetVeil: { + name: "スイートベール", + description: "味方の ポケモンは 眠らなくなる。", + }, + stanceChange: { + name: "バトルスイッチ", + description: "攻撃技を 出すと ブレードフォルムに 技 キングシールドを 出すと シールドフォルムに 変化する。", + }, + galeWings: { + name: "はやてのつばさ", + description: "HPが 満タン だと ひこうタイプの 技を 先制で 出すことが できる。", + }, + megaLauncher: { + name: "メガランチャー", + description: "波動の 技の 威力が 高くなる。", + }, + grassPelt: { + name: "くさのけがわ", + description: "グラスフィールドのとき 防御が 上がる。", + }, + symbiosis: { + name: "きょうせい", + description: "味方が 道具を 使うと 自分の 持っている 道具を 味方に 渡す。", + }, + toughClaws: { + name: "かたいツメ", + description: "相手に 接触する 技の 威力が 高くなる。", + }, + pixilate: { + name: "フェアリースキン", + description: "ノーマルタイプの 技が フェアリータイプになる。 威力が 少し 上がる。", + }, + gooey: { + name: "ぬめぬめ", + description: "攻撃で 自分に 触れた 相手の 素早さを 下げる。", + }, + aerilate: { + name: "スカイスキン", + description: "ノーマルタイプの 技が ひこうタイプになる。 威力が 少し 上がる。", + }, + parentalBond: { + name: "おやこあい", + description: "親子 2匹で 2回 攻撃することが できる。", + }, + darkAura: { + name: "ダークオーラ", + description: "全員の あくタイプの 技が 強くなる。", + }, + fairyAura: { + name: "フェアリーオーラ", + description: "全員の フェアリータイプの 技が 強くなる。", + }, + auraBreak: { + name: "オーラブレイク", + description: "オーラの 効果を 逆転させて 威力を 下げる。", + }, + primordialSea: { + name: "はじまりのうみ", + description: "ほのおタイプの 攻撃を 受けない 天気にする。", + }, + desolateLand: { + name: "おわりのだいち", + description: "みずタイプの 攻撃を 受けない 天気にする。", + }, + deltaStream: { + name: "デルタストリーム", + description: "ひこうタイプの 弱点が なくなる 天気にする。", + }, + stamina: { + name: "じきゅうりょく", + description: "攻撃を 受けると 防御が 上がる。", + }, + wimpOut: { + name: "にげごし", + description: "HPが 半分に なると あわてて 逃げ出して 手持ちに 引っ込んで しまう。", + }, + emergencyExit: { + name: "ききかいひ", + description: "HPが 半分に なると 危険を 回避するため 手持ちに 引っ込んで しまう。", + }, + waterCompaction: { + name: "みずがため", + description: "みずタイプの 技を 受けると 防御が ぐーんと 上がる。", + }, + merciless: { + name: "ひとでなし", + description: "どく状態の 相手を 攻撃すると かならず 急所に 当たる。", + }, + shieldsDown: { + name: "リミットシールド", + description: "HPが 半分に なると 殻が 壊れて 攻撃的に なる。", + }, + stakeout: { + name: "はりこみ", + description: "交代で 出てきた 相手に 2倍の ダメージで 攻撃 できる。", + }, + waterBubble: { + name: "すいほう", + description: "自分に 対する ほのおタイプの 技の 威力を 下げる。 やけど しない。", + }, + steelworker: { + name: "はがねつかい", + description: "はがねタイプの 技の 威力が 上がる。", + }, + berserk: { + name: "ぎゃくじょう", + description: "相手の 攻撃で HPが 半分に なると 特攻が 上がる。", + }, + slushRush: { + name: "ゆきかき", + description: "天気が ゆき のとき 素早さが 上がる。", + }, + longReach: { + name: "えんかく", + description: "すべての 技を 相手に 接触 しないで 出すことが できる。", + }, + liquidVoice: { + name: "うるおいボイス", + description: "すべての 音技が みずタイプに なる。", + }, + triage: { + name: "ヒーリングシフト", + description: "回復技を 先制で 出すことが できる。", + }, + galvanize: { + name: "エレキスキン", + description: "ノーマルタイプの 技が でんきタイプになる。 威力が 少し 上がる。", + }, + surgeSurfer: { + name: "サーフテール", + description: "エレキフィールド のとき 素早さが 2倍に なる。", + }, + schooling: { + name: "ぎょぐん", + description: "HPが 多いときは 群れて 強くなる。 HPの 残りが 少なくなると 群れは 散り散りに なってしまう。", + }, + disguise: { + name: "ばけのかわ", + description: "体を 被う 化けの皮で 1回 攻撃を 防ぐことが できる。", + }, + battleBond: { + name: "きずなへんげ", + description: "相手を 倒すと トレーナーとの キズナが 深まり サトシゲッコウガに 変化する。みずしゅりけんが 強くなる。", + }, + powerConstruct: { + name: "スワームチェンジ", + description: "HPが 半分に なると セルたちが 応援に 駆けつけ パーフェクトフォルムに 姿を 変える。", + }, + corrosion: { + name: "ふしょく", + description: "はがねタイプや どくタイプも どく状態に することが できる。", + }, + comatose: { + name: "ぜったいねむり", + description: "つねに 夢うつつの 状態で 絶対に 目覚めない。 眠ったまま 攻撃が できる。", + }, + queenlyMajesty: { + name: "じょおうのいげん", + description: "相手に 威圧感を あたえ こちらに むかって 先制技を 出せない ようにする。", + }, + innardsOut: { + name: "とびだすなかみ", + description: "相手に 倒されたとき HPの 残りの ぶんだけ 相手に ダメージを あたえる。", + }, + dancer: { + name: "おどりこ", + description: "だれかが 踊り技を 使うと 自分も それに 続いて 踊り技を 出すことが できる。", + }, + battery: { + name: "バッテリー", + description: "味方の 特殊技の 威力を 上げる。", + }, + fluffy: { + name: "もふもふ", + description: "相手から 受けた 接触する 技の ダメージを 半減するが ほのおタイプの 技の ダメージは 2倍になる。", + }, + dazzling: { + name: "ビビッドボディ", + description: "相手を びっくり させて こちらに むかって 先制技を 出せない ようにする。", + }, + soulHeart: { + name: "ソウルハート", + description: "ポケモンが ひんしに なるたびに 特攻が 上がる。", + }, + tanglingHair: { + name: "カーリーヘアー", + description: "攻撃で 自分に 触れた 相手の 素早さを 下げる。", + }, + receiver: { + name: "レシーバー", + description: "倒された 味方の 特性を 受け継いで 同じ 特性に なる。", + }, + powerOfAlchemy: { + name: "かがくのちから", + description: "倒された 味方の 特性を 受け継いで 同じ 特性に なる。", + }, + beastBoost: { + name: "ビーストブースト", + description: "相手を 倒したとき 自分の いちばん 高い 能力が 上がる。", + }, + rksSystem: { + name: "ARシステム", + description: "持っている メモリで 自分の タイプが 変わる。", + }, + electricSurge: { + name: "エレキメイカー", + description: "登場 したときに エレキフィールドを はりめぐらせる。", + }, + psychicSurge: { + name: "サイコメイカー", + description: "登場 したときに サイコフィールドを はりめぐらせる。", + }, + mistySurge: { + name: "ミストメイカー", + description: "登場 したときに ミストフィールドを はりめぐらせる。", + }, + grassySurge: { + name: "グラスメイカー", + description: "登場 したときに グラスフィールドを はりめぐらせる。", + }, + fullMetalBody: { + name: "メタルプロテクト", + description: "相手の 技や 特性で 能力を 下げられない。", + }, + shadowShield: { + name: "ファントムガード", + description: "HPが 満タンの ときに 受ける ダメージが 少なくなる。", + }, + prismArmor: { + name: "プリズムアーマー", + description: "効果バツグンに なってしまう 攻撃の 威力を 弱める ことが できる。", + }, + neuroforce: { + name: "ブレインフォース", + description: "効果バツグンの 攻撃で 威力が さらに 上がる。", + }, + intrepidSword: { + name: "ふとうのけん", + description: "登場 したときに 攻撃が 上がる。", + }, + dauntlessShield: { + name: "ふくつのたて", + description: "登場 したときに 防御が 上がる。", + }, + libero: { + name: "リベロ", + description: "自分が 出す 技と 同じ タイプに 変化する。", + }, + ballFetch: { + name: "たまひろい", + description: "1回目に 投げて 失敗 した モンスターボールを 拾ってくる。", + }, + cottonDown: { + name: "わたげ", + description: "攻撃を 受けると わたげを ばらまいて 自分以外の ポケモン すべての 素早さを 下げる。", + }, + propellerTail: { + name: "スクリューおびれ", + description: "相手の 技を 引き受ける 特性や 技の 影響を 無視 できる。", + }, + mirrorArmor: { + name: "ミラーアーマー", + description: "自分が 受けた 能力 ダウンの 効果 だけを 跳ね返す。", + }, + gulpMissile: { + name: "うのミサイル", + description: "なみのりか ダイビングを すると 獲物を くわえてくる。 ダメージを 受けると 獲物を 吐きだして 攻撃。", + }, + stalwart: { + name: "すじがねいり", + description: "相手の 技を 引き受ける 特性や 技の 影響を 無視 できる。", + }, + steamEngine: { + name: "じょうききかん", + description: "みずタイプ ほのおタイプの 技を 受けると 素早さが ぐぐーんと 上がる。", + }, + punkRock: { + name: "パンクロック", + description: "音技の 威力が 上がる。 受けた 音技の ダメージは 半分に なる。", + }, + sandSpit: { + name: "すなはき", + description: "攻撃を 受けると 砂あらしを 起こす。", + }, + iceScales: { + name: "こおりのりんぷん", + description: "こおりのりんぷんに 守られて 特殊攻撃で 受ける ダメージが 半減 する。", + }, + ripen: { + name: "じゅくせい", + description: "熟成 させることで きのみの 効果が 倍に なる。", + }, + iceFace: { + name: "アイスフェイス", + description: "物理攻撃は 頭の 氷が みがわりに なるが 姿も 変わる。 氷は あられが 降ると 元に戻る。", + }, + powerSpot: { + name: "パワースポット", + description: "隣に いるだけで 技の 威力が 上がる。", + }, + mimicry: { + name: "ぎたい", + description: "フィールドの 状態に あわせて ポケモンの タイプが 変わる。", + }, + screenCleaner: { + name: "バリアフリー", + description: "登場 したときに 敵と 味方の ひかりのかべ リフレクター オーロラベールの 効果が 消える。", + }, + steelySpirit: { + name: "はがねのせいしん", + description: "味方の はがねタイプの 攻撃の 威力が 上がる。", + }, + perishBody: { + name: "ほろびのボディ", + description: "接触する 技を 受けると お互い 3ターン たつと ひんしになる。 交代すると 効果は なくなる。", + }, + wanderingSpirit: { + name: "さまようたましい", + description: "接触する 技で 攻撃 してきた ポケモンと 特性を 入れ替える。", + }, + gorillaTactics: { + name: "ごりむちゅう", + description: "攻撃は 上がるが 最初に 選んだ 技しか 出せなくなる。", + }, + neutralizingGas: { + name: "かがくへんかガス", + description: "かがくへんかガスの ポケモンが 場にいると すべての ポケモンの 特性の 効果が 消えたり 発動 しなくなる。", + }, + pastelVeil: { + name: "パステルベール", + description: "自分も 味方も どくの 状態異常を 受けなくなる。", + }, + hungerSwitch: { + name: "はらぺこスイッチ", + description: "ターンの 終わりに まんぷくもよう はらぺこもよう まんぷくもよう……と 交互に 姿を 変える。", + }, + quickDraw: { + name: "クイックドロウ", + description: "相手より 先に 行動できることが ある。", + }, + unseenFist: { + name: "ふかしのこぶし", + description: "相手に 接触する 技なら 守りの 効果を 無視して 攻撃することが できる。", + }, + curiousMedicine: { + name: "きみょうなくすり", + description: "登場 したときに 貝がらから 薬を 振りまいて 味方の 能力変化を 元に戻す。", + }, + transistor: { + name: "トランジスタ", + description: "でんきタイプの 技の 威力が 上がる。", + }, + dragonsMaw: { + name: "りゅうのあぎと", + description: "ドラゴンタイプの 技の 威力が 上がる。", + }, + chillingNeigh: { + name: "しろのいななき", + description: "相手を 倒すと 冷たい 声で いなないて 攻撃が 上がる。", + }, + grimNeigh: { + name: "くろのいななき", + description: "相手を 倒すと 恐ろしい 声で いなないて 特攻が 上がる。", + }, + asOneGlastrier: { + name: "じんばいったい", + description: "バドレックスの きんちょうかんと ブリザポスの しろのいななきの 二つの 特性を あわせ持つ。", + }, + asOneSpectrier: { + name: "じんばいったい", + description: "バドレックスの きんちょうかんと レイスポスの くろのいななきの 二つの 特性を あわせ持つ。", + }, + lingeringAroma: { + name: "とれないにおい", + description: "直接攻撃を受けたとき、相手の特性もとれないにおいにする。", + }, + seedSower: { + name: "こぼれダネ", + description: "攻撃を 受けると グラスフィールドに する。", + }, + thermalExchange: { + name: "ねつこうかん", + description: "ほのおタイプの 技を 受けると 攻撃が 上がる。 やけど状態に ならない。", + }, + angerShell: { + name: "いかりのこうら", + description: "相手の攻撃で HPが 半分に なると 怒りで 防御と 特防が 下がるが 攻撃 特攻 素早さが 上がる。", + }, + purifyingSalt: { + name: "きよめのしお", + description: "清らかな塩で 状態異常に ならない。 ゴーストタイプの 技の ダメージを 半減させる。", + }, + wellBakedBody: { + name: "こんがりボディ", + description: "ほのおタイプの 技を 受けると ダメージを 受けずに 防御が ぐーんと 上がる。", + }, + windRider: { + name: "かぜのり", + description: "おいかぜが 吹いたり 風技を 受けると ダメージを 受けずに 攻撃が 上がる。", + }, + guardDog: { + name: "ばんけん", + description: "いかく されると 攻撃が 上がる。 ポケモンを 入れ替えさせる 技や 道具が 効かない。", + }, + rockyPayload: { + name: "いわはこび", + description: "いわタイプの 技の 威力が 上がる。", + }, + windPower: { + name: "ふうりょくでんき", + description: "風技を 受けると じゅうでん 状態に なる。", + }, + zeroToHero: { + name: "マイティチェンジ", + description: "手持ちに ひっこむと マイティフォルムに 変化する。", + }, + commander: { + name: "しれいとう", + description: "登場したとき 味方に ヘイラッシャが いると 口の中に 入って そこから 指令を だす。", + }, + electromorphosis: { + name: "でんきにかえる", + description: "ダメージを 受けると じゅうでん 状態に なる。", + }, + protosynthesis: { + name: "こだいかっせい", + description: "ブーストエナジーを 持たせるか 天気が 晴れのとき いちばん 高い能力が 上がる。", + }, + quarkDrive: { + name: "クォークチャージ", + description: "ブーストエナジーを 持たせるか エレキフィールドのとき いちばん 高い能力が 上がる。", + }, + goodAsGold: { + name: "おうごんのからだ", + description: "酸化せず 丈夫な 黄金の体は 相手からの 変化技を 受けない。", + }, + vesselOfRuin: { + name: "わざわいのうつわ", + description: "災厄を 呼ぶ 器の力で 自分以外の 特攻が 弱くなる。", + }, + swordOfRuin: { + name: "わざわいのつるぎ", + description: "災厄を 呼ぶ 剣の力で 自分以外の 防御が 弱くなる。", + }, + tabletsOfRuin: { + name: "わざわいのおふだ", + description: "災厄を 呼ぶ 木札の力で 自分以外の 攻撃が 弱くなる。", + }, + beadsOfRuin: { + name: "わざわいのたま", + description: "災厄を 呼ぶ 勾玉の力で 自分以外の 特防が 弱くなる。", + }, + orichalcumPulse: { + name: "ひひいろのこどう", + description: "登場したとき 天気を 晴れにする。 日差しが 強いと 古代の 鼓動により 攻撃が 高まる。", + }, + hadronEngine: { + name: "ハドロンエンジン", + description: "登場したとき エレキフィールドを はる。 エレキフィールドだと 未来の 機関により 特攻が 高まる。", + }, + opportunist: { + name: "びんじょう", + description: "相手の 能力が 上がったとき 自分も 便乗して 同じように 能力を 上げる。", + }, + cudChew: { + name: "はんすう", + description: "きのみを 食べると 次のターンの 終わりに 胃から 出して もう1回だけ 食べる。", + }, + sharpness: { + name: "きれあじ", + description: "相手を 切る技の 威力が 上がる。", + }, + supremeOverlord: { + name: "そうだいしょう", + description: "登場したとき 今まで 倒された 味方の 数が 多いほど 少しずつ 攻撃と 特攻が 上がる。", + }, + costar: { + name: "きょうえん", + description: "登場 したときに 味方の 能力変化を コピーする。", + }, + toxicDebris: { + name: "どくげしょう", + description: "物理技で ダメージを 受けると 相手の 足下に どくびしが ちらばる。", + }, + armorTail: { + name: "テイルアーマー", + description: "頭を包む 謎のしっぽが こちらに むかって 先制技を 出せない ようにする。", + }, + earthEater: { + name: "どしょく", + description: "じめんタイプの 技を 受けると ダメージを 受けずに 回復する。", + }, + myceliumMight: { + name: "きんしのちから", + description: "変化技を 出すとき 必ず 行動が 遅くなるが 相手の 特性に ジャマされない。", + }, + mindsEye: { + name: "しんがん", + description: "ノーマル かくとうタイプの技を ゴーストタイプに 当てることが できる。 相手の 回避率の 変化を 無視し 命中率も 下げられない。", + }, + supersweetSyrup: { + name: "かんろなミツ", + description: "最初に 登場 したとき 甘ったるい 蜜の香りを ふりまいて 相手の 回避率を 下げる。", + }, + hospitality: { + name: "おもてなし", + description: "登場したとき 味方を もてなして HPを 少しだけ 回復してあげる。", + }, + toxicChain: { + name: "どくのくさり", + description: "毒素を ふくんだ 鎖の力で 技を 当てた 相手を 猛毒の状態に することが ある。", + }, + embodyAspectTeal: { + name: "おもかげやどし", + description: "思い出を 心に 宿すことで みどりのめんを かがやかせ 自分の 素早さを 上げる。", + }, + embodyAspectWellspring: { + name: "おもかげやどし", + description: "思い出を 心に 宿すことで いどのめんを かがやかせ 自分の 特防を 上げる。", + }, + embodyAspectHearthflame: { + name: "おもかげやどし", + description: "思い出を 心に 宿すことで かまどのめんを かがやかせ 自分の 攻撃を 上げる。", + }, + embodyAspectCornerstone: { + name: "おもかげやどし", + description: "思い出を 心に 宿すことで いしずえのめんを かがやかせ 自分の 防御を 上げる。", + }, + teraShift: { + name: "テラスチェンジ", + description: "登場したとき 周囲の エネルギーを 吸収し テラスタルフォルムに 変化する。", + }, + teraShell: { + name: "テラスシェル", + description: "全タイプの力を 秘めた甲羅は HPが 満タンの ときに 受ける ダメージを すべて 今ひとつに する。", + }, + teraformZero: { + name: "ゼロフォーミング", + description: "テラパゴスが ステラフォルムに なったとき 秘められた力で 天気と フィールドの 影響を すべて ゼロにする。", + }, + poisonPuppeteer: { + name: "どくくぐつ", + description: "モモワロウの 技によって どく状態に なった 相手は こんらん状態にも なってしまう。", + }, +} as const; diff --git a/src/locales/ja/achv.ts b/src/locales/ja/achv.ts new file mode 100644 index 00000000000..7e8d41c9808 --- /dev/null +++ b/src/locales/ja/achv.ts @@ -0,0 +1,278 @@ +import { AchievementTranslationEntries } from "#app/interfaces/locales.js"; + +// Achievement translations for the when the player character is male +export const PGMachv: AchievementTranslationEntries = { + "Achievements": { + name: "Achievements", + }, + "Locked": { + name: "Locked", + }, + + "MoneyAchv": { + description: "Accumulate a total of ₽{{moneyAmount}}", + }, + "10K_MONEY": { + name: "Money Haver", + }, + "100K_MONEY": { + name: "Rich", + }, + "1M_MONEY": { + name: "Millionaire", + }, + "10M_MONEY": { + name: "One Percenter", + }, + + "DamageAchv": { + description: "Inflict {{damageAmount}} damage in one hit", + }, + "250_DMG": { + name: "Hard Hitter", + }, + "1000_DMG": { + name: "Harder Hitter", + }, + "2500_DMG": { + name: "That's a Lotta Damage!", + }, + "10000_DMG": { + name: "One Punch Man", + }, + + "HealAchv": { + description: "Heal {{healAmount}} {{HP}} at once with a move, ability, or held item", + }, + "250_HEAL": { + name: "Novice Healer", + }, + "1000_HEAL": { + name: "Big Healer", + }, + "2500_HEAL": { + name: "Cleric", + }, + "10000_HEAL": { + name: "Recovery Master", + }, + + "LevelAchv": { + description: "Level up a Pokémon to Lv{{level}}", + }, + "LV_100": { + name: "But Wait, There's More!", + }, + "LV_250": { + name: "Elite", + }, + "LV_1000": { + name: "To Go Even Further Beyond", + }, + + "RibbonAchv": { + description: "Accumulate a total of {{ribbonAmount}} Ribbons", + }, + "10_RIBBONS": { + name: "Pokémon League Champion", + }, + "25_RIBBONS": { + name: "Great League Champion", + }, + "50_RIBBONS": { + name: "Ultra League Champion", + }, + "75_RIBBONS": { + name: "Rogue League Champion", + }, + "100_RIBBONS": { + name: "Master League Champion", + }, + + "TRANSFER_MAX_BATTLE_STAT": { + name: "Teamwork", + description: "Baton pass to another party member with at least one stat maxed out", + }, + "MAX_FRIENDSHIP": { + name: "Friendmaxxing", + description: "Reach max friendship on a Pokémon", + }, + "MEGA_EVOLVE": { + name: "Megamorph", + description: "Mega evolve a Pokémon", + }, + "GIGANTAMAX": { + name: "Absolute Unit", + description: "Gigantamax a Pokémon", + }, + "TERASTALLIZE": { + name: "STAB Enthusiast", + description: "Terastallize a Pokémon", + }, + "STELLAR_TERASTALLIZE": { + name: "The Hidden Type", + description: "Stellar Terastallize a Pokémon", + }, + "SPLICE": { + name: "Infinite Fusion", + description: "Splice two Pokémon together with DNA Splicers", + }, + "MINI_BLACK_HOLE": { + name: "A Hole Lot of Items", + description: "Acquire a Mini Black Hole", + }, + "CATCH_MYTHICAL": { + name: "Mythical", + description: "Catch a mythical Pokémon", + }, + "CATCH_SUB_LEGENDARY": { + name: "(Sub-)Legendary", + description: "Catch a sub-legendary Pokémon", + }, + "CATCH_LEGENDARY": { + name: "Legendary", + description: "Catch a legendary Pokémon", + }, + "SEE_SHINY": { + name: "Shiny", + description: "Find a shiny Pokémon in the wild", + }, + "SHINY_PARTY": { + name: "That's Dedication", + description: "Have a full party of shiny Pokémon", + }, + "HATCH_MYTHICAL": { + name: "Mythical Egg", + description: "Hatch a mythical Pokémon from an egg", + }, + "HATCH_SUB_LEGENDARY": { + name: "Sub-Legendary Egg", + description: "Hatch a sub-legendary Pokémon from an egg", + }, + "HATCH_LEGENDARY": { + name: "Legendary Egg", + description: "Hatch a legendary Pokémon from an egg", + }, + "HATCH_SHINY": { + name: "Shiny Egg", + description: "Hatch a shiny Pokémon from an egg", + }, + "HIDDEN_ABILITY": { + name: "Hidden Potential", + description: "Catch a Pokémon with a hidden ability", + }, + "PERFECT_IVS": { + name: "Certificate of Authenticity", + description: "Get perfect IVs on a Pokémon", + }, + "CLASSIC_VICTORY": { + name: "Undefeated", + description: "Beat the game in classic mode", + }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Bring Your Child To Work Day", + description: "Beat the game in Classic Mode with at least one unevolved party member." + }, + + "MONO_GEN_ONE": { + name: "The Original Rival", + description: "Complete the generation one only challenge.", + }, + "MONO_GEN_TWO": { + name: "Generation 1.5", + description: "Complete the generation two only challenge.", + }, + "MONO_GEN_THREE": { + name: "Too much water?", + description: "Complete the generation three only challenge.", + }, + "MONO_GEN_FOUR": { + name: "Is she really the hardest?", + description: "Complete the generation four only challenge.", + }, + "MONO_GEN_FIVE": { + name: "All Original", + description: "Complete the generation five only challenge.", + }, + "MONO_GEN_SIX": { + name: "Almost Royalty", + description: "Complete the generation six only challenge.", + }, + "MONO_GEN_SEVEN": { + name: "Only Technically", + description: "Complete the generation seven only challenge.", + }, + "MONO_GEN_EIGHT": { + name: "A Champion Time!", + description: "Complete the generation eight only challenge.", + }, + "MONO_GEN_NINE": { + name: "She was going easy on you", + description: "Complete the generation nine only challenge.", + }, + + "MonoType": { + description: "Complete the {{type}} monotype challenge.", + }, + "MONO_NORMAL": { + name: "Extra Ordinary", + }, + "MONO_FIGHTING": { + name: "I Know Kung Fu", + }, + "MONO_FLYING": { + name: "Angry Birds", + }, + "MONO_POISON": { + name: "Kanto's Favourite", + }, + "MONO_GROUND": { + name: "Forecast: Earthquakes", + }, + "MONO_ROCK": { + name: "Brock Hard", + }, + "MONO_BUG": { + name: "You Like Jazz?", + }, + "MONO_GHOST": { + name: "Who You Gonna Call?", + }, + "MONO_STEEL": { + name: "Iron Giant", + }, + "MONO_FIRE": { + name: "I Cast Fireball!", + }, + "MONO_WATER": { + name: "When It Rains, It Pours", + }, + "MONO_GRASS": { + name: "Can't Touch This", + }, + "MONO_ELECTRIC": { + name: "Aim For The Horn!", + }, + "MONO_PSYCHIC": { + name: "Big Brain Energy", + }, + "MONO_ICE": { + name: "Walking On Thin Ice", + }, + "MONO_DRAGON": { + name: "Pseudo-Legend Club", + }, + "MONO_DARK": { + name: "It's Just A Phase", + }, + "MONO_FAIRY": { + name: "Hey! Listen!", + }, + "FRESH_START": { + name: "First Try!", + description: "Complete the fresh start challenge." + } +} as const; + +// Achievement translations for the when the player character is female (it for now uses the same translations as the male version) +export const PGFachv: AchievementTranslationEntries = PGMachv; diff --git a/src/locales/ja/arena-flyout.ts b/src/locales/ja/arena-flyout.ts new file mode 100644 index 00000000000..8a31d37b10c --- /dev/null +++ b/src/locales/ja/arena-flyout.ts @@ -0,0 +1,49 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const arenaFlyout: SimpleTranslationEntries = { + // Title + "activeBattleEffects": "Active Battle Effects", + "player": "Player", + "neutral": "Neutral", + "enemy": "Enemy", + + // WeatherType + "sunny": "Sunny", + "rain": "Rain", + "sandstorm": "Sandstorm", + "hail": "Hail", + "snow": "Snow", + "fog": "Fog", + "heavyRain": "Heavy Rain", + "harshSun": "Harsh Sun", + "strongWinds": "Strong Winds", + + // TerrainType + "misty": "Misty Terrain", + "electric": "Electric Terrain", + "grassy": "Grassy Terrain", + "psychic": "Psychic Terrain", + + // ArenaTagType + "mudSport": "Mud Sport", + "waterSport": "Water Sport", + "spikes": "Spikes", + "toxicSpikes": "Toxic Spikes", + "mist": "Mist", + "futureSight": "Future Sight", + "doomDesire": "Doom Desire", + "wish": "Wish", + "stealthRock": "Stealth Rock", + "stickyWeb": "Sticky Web", + "trickRoom": "Trick Room", + "gravity": "Gravity", + "reflect": "Reflect", + "lightScreen": "Light Screen", + "auroraVeil": "Aurora Veil", + "quickGuard": "Quick Guard", + "wideGuard": "Wide Guard", + "matBlock": "Mat Block", + "craftyShield": "Crafty Shield", + "tailwind": "Tailwind", + "happyHour": "Happy Hour", +}; diff --git a/src/locales/ja/arena-tag.ts b/src/locales/ja/arena-tag.ts new file mode 100644 index 00000000000..22612795308 --- /dev/null +++ b/src/locales/ja/arena-tag.ts @@ -0,0 +1,53 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const arenaTag: SimpleTranslationEntries = { + "yourTeam": "your team", + "opposingTeam": "the opposing team", + "arenaOnRemove": "{{moveName}}'s effect wore off.", + "arenaOnRemovePlayer": "{{moveName}}'s effect wore off\non your side.", + "arenaOnRemoveEnemy": "{{moveName}}'s effect wore off\non the foe's side.", + "mistOnAdd": "{{pokemonNameWithAffix}}'s team became\nshrouded in mist!", + "mistApply": "The mist prevented\nthe lowering of stats!", + "reflectOnAdd": "Reflect reduced the damage of physical moves.", + "reflectOnAddPlayer": "Reflect reduced the damage of physical moves on your side.", + "reflectOnAddEnemy": "Reflect reduced the damage of physical moves on the foe's side.", + "lightScreenOnAdd": "Light Screen reduced the damage of special moves.", + "lightScreenOnAddPlayer": "Light Screen reduced the damage of special moves on your side.", + "lightScreenOnAddEnemy": "Light Screen reduced the damage of special moves on the foe's side.", + "auroraVeilOnAdd": "Aurora Veil reduced the damage of moves.", + "auroraVeilOnAddPlayer": "Aurora Veil reduced the damage of moves on your side.", + "auroraVeilOnAddEnemy": "Aurora Veil reduced the damage of moves on the foe's side.", + "conditionalProtectOnAdd": "{{moveName}} protected team!", + "conditionalProtectOnAddPlayer": "{{moveName}} protected your team!", + "conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!", + "conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!", + "matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!", + "noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!", + "noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!", + "noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!", + "wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!", + "mudSportOnAdd": "Electricity's power was weakened!", + "mudSportOnRemove": "The effects of Mud Sport\nhave faded.", + "waterSportOnAdd": "Fire's power was weakened!", + "waterSportOnRemove": "The effects of Water Sport\nhave faded.", + "spikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!", + "spikesActivateTrap": "{{pokemonNameWithAffix}} is hurt\nby the spikes!", + "toxicSpikesOnAdd": "{{moveName}} were scattered\nall around {{opponentDesc}}'s feet!", + "toxicSpikesActivateTrapPoison": "{{pokemonNameWithAffix}} absorbed the {{moveName}}!", + "stealthRockOnAdd": "Pointed stones float in the air\naround {{opponentDesc}}!", + "stealthRockActivateTrap": "Pointed stones dug into\n{{pokemonNameWithAffix}}!", + "stickyWebOnAdd": "A {{moveName}} has been laid out on the ground around the opposing team!", + "stickyWebActivateTrap": "The opposing {{pokemonName}} was caught in a sticky web!", + "trickRoomOnAdd": "{{pokemonNameWithAffix}} twisted\nthe dimensions!", + "trickRoomOnRemove": "The twisted dimensions\nreturned to normal!", + "gravityOnAdd": "Gravity intensified!", + "gravityOnRemove": "Gravity returned to normal!", + "tailwindOnAdd": "The Tailwind blew from behind team!", + "tailwindOnAddPlayer": "The Tailwind blew from behind\nyour team!", + "tailwindOnAddEnemy": "The Tailwind blew from behind\nthe opposing team!", + "tailwindOnRemove": "Team's Tailwind petered out!", + "tailwindOnRemovePlayer": "Your team's Tailwind petered out!", + "tailwindOnRemoveEnemy": "The opposing team's Tailwind petered out!", + "happyHourOnAdd": "Everyone is caught up in the happy atmosphere!", + "happyHourOnRemove": "The atmosphere returned to normal.", +} as const; diff --git a/src/locales/ja/battle-info.ts b/src/locales/ja/battle-info.ts new file mode 100644 index 00000000000..f24dad46c6c --- /dev/null +++ b/src/locales/ja/battle-info.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleInfo: SimpleTranslationEntries = { + "generation": "Generation {{generation}}", +} as const; diff --git a/src/locales/ja/battle-message-ui-handler.ts b/src/locales/ja/battle-message-ui-handler.ts new file mode 100644 index 00000000000..9e306d3b36b --- /dev/null +++ b/src/locales/ja/battle-message-ui-handler.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleMessageUiHandler: SimpleTranslationEntries = { + "ivBest": "さいこう", + "ivFantastic": "すばらしい", + "ivVeryGood": "すごくいい", + "ivPrettyGood": "かなりいい", + "ivDecent": "まぁまぁ", + "ivNoGood": "ダメかも", +} as const; diff --git a/src/locales/ja/battle.ts b/src/locales/ja/battle.ts new file mode 100644 index 00000000000..aed24a710df --- /dev/null +++ b/src/locales/ja/battle.ts @@ -0,0 +1,159 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battle: SimpleTranslationEntries = { + "bossAppeared": "{{bossName}}が あらわれた!", + "trainerAppeared": "{{trainerName}}が\nしょうぶを しかけてきた!", + "trainerAppearedDouble": "{{trainerName}}が\nしょうぶを しかけてきた!", + "trainerSendOut": "{{trainerName}}は\n{{pokemonName}}を くりだした!", + "singleWildAppeared": "あっ! やせいの\n{{pokemonName}}が とびだしてきた!", + "multiWildAppeared": "あっ! やせいの {{pokemonName1}}と\n{{pokemonName2}}が とびだしてきた!", + "playerComeBack": "{{pokemonName}}! もどれ!", + "trainerComeBack": "{{trainerName}}は\n{{pokemonName}}を ひっこめた!", + "playerGo": "ゆけっ! {{pokemonName}}!", + "trainerGo": "{{trainerName}}は\n{{pokemonName}}を くりだした!", + "switchQuestion": "{{pokemonName}}を\nいれかえますか?", + "trainerDefeated": "{{trainerName}}\nとの しょうぶに かった!", + "moneyWon": "しょうきんとして\n₽{{moneyAmount}} てにいれた!", + "moneyPickedUp": "₽{{moneyAmount}}を ひろった!", + "pokemonCaught": "{{pokemonName}}を\nつかまえたぞ!", + "addedAsAStarter": "{{pokemonName}} has been\nadded as a starter!", + "partyFull": "てもちがいっぱいです。\n{{pokemonName}}をいれるために ポケモンを ひとり てばなしますか?", + "pokemon": "ポケモン", + "sendOutPokemon": "がんばれ! {{pokemonName}}!", + "hitResultCriticalHit": "きゅうしょに あたった!", + "hitResultSuperEffective": "こうかは ばつぐんだ!", + "hitResultNotVeryEffective": "こうかは いまひとつの ようだ……", + "hitResultNoEffect": "{{pokemonName}}には こうかが ないようだ…", + "hitResultOneHitKO": "いちげき ひっさつ!", + "attackFailed": "しかし うまく きまらなかった!!", + "attackMissed": "{{pokemonNameWithAffix}}には あたらなかった!", + "attackHitsCount": "{{count}}かい あたった!", + "rewardGain": "{{modifierName}}を\nてにいれた!", + "expGain": "{{pokemonName}}は\n{{exp}}けいけんちを もらった!", + "levelUp": "{{pokemonName}}は\nレベル{{level}} に あがった!", + "learnMove": "{{pokemonName}}は あたらしく\n{{moveName}}を おぼえた!", + "learnMovePrompt": "{{pokemonName}}は あたらしく\n{{moveName}}を おぼえたい……", + "learnMoveLimitReached": "しかし {{pokemonName}}は わざを 4つ\nおぼえるので せいいっぱいだ!", + "learnMoveReplaceQuestion": "{{moveName}}の かわりに\nほかの わざを わすれさせますか?", + "learnMoveStopTeaching": "それでは…… {{moveName}}を\nおぼえるのを あきらめますか?", + "learnMoveNotLearned": "{{pokemonName}}は {{moveName}}を\nおぼえずに おわった!", + "learnMoveForgetQuestion": "どの わざを\nわすれさせたい?", + "learnMoveForgetSuccess": "{{pokemonName}}は {{moveName}}の\nつかいかたを きれいに わすれた!", + "countdownPoof": "@d{32}1 @d{15}2の @d{15}… @d{15}… @d{15}… @d{15}@s{pb_bounce_1}ポカン!", + "learnMoveAnd": "そして…", + "levelCapUp": "レベルキャップの\n{{levelCap}}に あがった!", + "moveNotImplemented": "{{moveName}}は まだじっそうされておらず、せんたくできません。", + "moveNoPP": "しかし わざの\nのこりポイントが なかった!", + "moveDisabled": "かなしばりで\n{{moveName}}が だせない!", + "noPokeballForce": "みえない ちからの せいで\nボールを なげられない!", + "noPokeballTrainer": "ひとのものを\nとったら どろぼう!", + "noPokeballMulti": "あいての ポケモンが ひとつしか\n いないまえに ボールが つかえない!", + "noPokeballStrong": "あいての ポケモンが つよすぎて つかまえられない!\nまずは よわらせよう!", + "noEscapeForce": "みえない ちからの せいで\nにげることが できない!", + "noEscapeTrainer": "ダメだ! しょうぶのさいちゅうに\nあいてに せなかを みせられない!", + "noEscapePokemon": "{{pokemonName}}の {{moveName}}で {{escapeVerb}}!", + "runAwaySuccess": " うまく にげきれた!", + "runAwayCannotEscape": "にげることが できない!", + "escapeVerbSwitch": "いれかえることが できない", + "escapeVerbFlee": "にげることが できない", + "notDisabled": "{{pokemonName}}の かなしばりが とけた!\nまた {{moveName}}が つかえられる!", + "turnEndHpRestore": "{{pokemonName}}の たいりょくが かいふくした!", + "hpIsFull": "{{pokemonName}}の\nHPが まんたんだ!", + "skipItemQuestion": "ほんとに アイテムを とらない?", + "eggHatching": "おや?", + "ivScannerUseQuestion": "{{pokemonName}}を\nこたいちスキャナーで そうさする?", + "wildPokemonWithAffix": "やせいの {{pokemonName}}", + "foePokemonWithAffix": "あいての {{pokemonName}}", + "useMove": "{{pokemonNameWithAffix}}の {{moveName}}!", + "drainMessage": "{{pokemonName}}から\nたいりょくを すいとった!", + "regainHealth": "{{pokemonName}}は\nたいりょくを かいふくした!", + "stealEatBerry": "{{pokemonName}}は {{targetName}}の\n{{berryName}}を うばって たべた!", + "ppHealBerry": "{{pokemonNameWithAffix}}は {{berryName}}で {{moveName}}のPPを かいふくした!", + "hpHealBerry": "{{pokemonNameWithAffix}}は {{berryName}}で たいりょくを かいふくした!", + "fainted": "{{pokemonNameWithAffix}}は たおれた!", + "statsAnd": "と ", + "stats": "のうりょく", + "statRose_one": "{{pokemonNameWithAffix}}の {{stats}}が あがった!", + "statRose_other": "{{pokemonNameWithAffix}}の {{stats}}が あがった!", + "statSharplyRose_one": "{{pokemonNameWithAffix}}の {{stats}}が ぐーんと あがった!", + "statSharplyRose_other": "{{pokemonNameWithAffix}}の {{stats}}が ぐーんと あがった!", + "statRoseDrastically_one": "{{pokemonNameWithAffix}}の {{stats}}が ぐぐーんと あがった!", + "statRoseDrastically_other": "{{pokemonNameWithAffix}}の {{stats}}が ぐぐーんと あがった!", + "statWontGoAnyHigher_one": "{{pokemonNameWithAffix}}の {{stats}}が もう あがらない!", + "statWontGoAnyHigher_other": "{{pokemonNameWithAffix}}の {{stats}}が もう あがらない!", + "statFell_one": "{{pokemonNameWithAffix}}の {{stats}}が さがった!", + "statFell_other": "{{pokemonNameWithAffix}}の {{stats}}が さがった!", + "statHarshlyFell_one": "{{pokemonNameWithAffix}}の {{stats}}が がくっと さがった!", + "statHarshlyFell_other": "{{pokemonNameWithAffix}}の {{stats}}が がくっと さがった!", + "statSeverelyFell_one": "{{pokemonNameWithAffix}}の {{stats}}が がくーんと さがった!", + "statSeverelyFell_other": "{{pokemonNameWithAffix}}の {{stats}}が がくーんと さがった!", + "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}の {{stats}}が もう さがらない!", + "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}の {{stats}}が もう さがらない!", + "transformedIntoType": "{{pokemonName}}は\n{{type}}タイプに へんしんした!", + "retryBattle": "このせんとうの はじまりから やりなおす?", + "unlockedSomething": "{{unlockedThing}}\nを アンロックした!", + "congratulations": "おめでとうございます!!", + "beatModeFirstTime": "はじめて {{speciesName}}が {{gameMode}}モードを クリアした!\n{{newModifier}}を てにいれた!", + "ppReduced": "{{targetName}}の {{moveName}}を {{reduction}}けずった!", + "battlerTagsRechargingLapse": "{{pokemonNameWithAffix}}は こうげきの はんどうで うごけない!", + "battlerTagsTrappedOnAdd": "{{pokemonNameWithAffix}}は もう にげられない!", + "battlerTagsTrappedOnRemove": "{{pokemonNameWithAffix}}は\n{{moveName}}の こうかが とけた!", + "battlerTagsFlinchedLapse": "{{pokemonNameWithAffix}}は ひるんで わざが だせない!", + "battlerTagsConfusedOnAdd": "{{pokemonNameWithAffix}}は こんらん した!", + "battlerTagsConfusedOnRemove": "{{pokemonNameWithAffix}}の こんらんが とけた!", + "battlerTagsConfusedOnOverlap": "{{pokemonNameWithAffix}}は すでに こんらん している!", + "battlerTagsConfusedLapse": "{{pokemonNameWithAffix}}は こんらん している!", + "battlerTagsConfusedLapseHurtItself": "わけも わからず じぶんを こうげきした!", + "battlerTagsDestinyBondLapseIsBoss": "{{pokemonNameWithAffix}}を みちづれに できない!", + "battlerTagsDestinyBondLapse": "{{pokemonNameWithAffix}}は あいてを みちづれに した!", + "battlerTagsInfatuatedOnAdd": "{{pokemonNameWithAffix}}は {{sourcePokemonName}}に メロメロに なった!", + "battlerTagsInfatuatedOnOverlap": "{{pokemonNameWithAffix}}は すでに メロメロだ!", + "battlerTagsInfatuatedLapse": "{{pokemonNameWithAffix}}は {{sourcePokemonName}}に メロメロだ!", + "battlerTagsInfatuatedLapseImmobilize": "{{pokemonNameWithAffix}}は\nメロメロで わざが だせなかった!", + "battlerTagsInfatuatedOnRemove": "{{pokemonNameWithAffix}}は メロメロじょうたいが なおった!", + "battlerTagsSeededOnAdd": "{{pokemonNameWithAffix}}に たねを うえつけた!", + "battlerTagsSeededLapse": "やどりぎが {{pokemonNameWithAffix}}の たいりょくを うばう!", + "battlerTagsSeededLapseShed": "{{pokemonNameWithAffix}}は ヘドロえきを すいとった!", + "battlerTagsNightmareOnAdd": "{{pokemonNameWithAffix}}は あくむを みはじめた!", + "battlerTagsNightmareOnOverlap": "{{pokemonNameWithAffix}}は すでに うなされている!", + "battlerTagsNightmareLapse": "{{pokemonNameWithAffix}}は あくむに うなされている!", + "battlerTagsEncoreOnAdd": "{{pokemonNameWithAffix}}は アンコールをうけた!", + "battlerTagsEncoreOnRemove": "{{pokemonNameWithAffix}}の アンコールじょうたいが とけた!", + "battlerTagsHelpingHandOnAdd": "{{pokemonNameWithAffix}}は {{pokemonName}}を\nてだすけする たいせいに はいった!", + "battlerTagsIngrainLapse": "{{pokemonNameWithAffix}}は ねから\n ようぶんを すいとった!", + "battlerTagsIngrainOnTrap": "{{pokemonNameWithAffix}}は ねを はった!", + "battlerTagsAquaRingOnAdd": "{{pokemonNameWithAffix}}は みずのリングを まとった!", + "battlerTagsAquaRingLapse": "{{pokemonName}}は {{moveName}}で\nたいりょくを かいふくした!", + "battlerTagsDrowsyOnAdd": "{{pokemonNameWithAffix}} の ねむけを さそった!", + "battlerTagsDamagingTrapLapse": "{{pokemonNameWithAffix}}は {{moveName}}の ダメージを うけた!", + "battlerTagsBindOnTrap": "{{pokemonNameWithAffix}}は {{sourcePokemonName}}に しめつけられた!", + "battlerTagsWrapOnTrap": "{{pokemonNameWithAffix}}は {{sourcePokemonName}}に まきつかれた!", + "battlerTagsVortexOnTrap": "{{pokemonNameWithAffix}}は うずの なかに とじこめられた!", + "battlerTagsClampOnTrap": "{{pokemonName}}は {{sourcePokemonNameWithAffix}}の\nからに はさまれた!", + "battlerTagsSandTombOnTrap": "{{pokemonNameWithAffix}}は {{moveName}}に とらわれた!", + "battlerTagsMagmaStormOnTrap": "{{pokemonNameWithAffix}}は マグマの\n うずに とじこめられた!", + "battlerTagsSnapTrapOnTrap": "{{pokemonNameWithAffix}}は トラバサミに とらわれた!", + "battlerTagsThunderCageOnTrap": "{{sourcePokemonNameWithAffix}}は {{pokemonNameWithAffix}}に とじこめられた!", + "battlerTagsInfestationOnTrap": "{{pokemonNameWithAffix}}は {{sourcePokemonNameWithAffix}}に まとわりつかれた!", + "battlerTagsProtectedOnAdd": "{{pokemonNameWithAffix}}は\nまもりの たいせいに はいった!", + "battlerTagsProtectedLapse": "{{pokemonNameWithAffix}}は\nこうげきから みを まもった!", + "battlerTagsEnduringOnAdd": "{{pokemonNameWithAffix}}は\nこらえる たいせいに はいった!", + "battlerTagsEnduringLapse": "{{pokemonNameWithAffix}}は\nこうげきを こらえた!", + "battlerTagsSturdyLapse": "{{pokemonNameWithAffix}}は\nこうげきを こらえた!", + "battlerTagsPerishSongLapse": "{{pokemonNameWithAffix}}の ほろびのカウントが {{turnCount}}になった!", + "battlerTagsCenterOfAttentionOnAdd": "{{pokemonNameWithAffix}}は ちゅうもくの まとになった!", + "battlerTagsTruantLapse": "{{pokemonNameWithAffix}}は なまけている!", + "battlerTagsSlowStartOnAdd": "{{pokemonNameWithAffix}}は ちょうしが あがらない!", + "battlerTagsSlowStartOnRemove": "{{pokemonNameWithAffix}}は ちょうしを とりもどした!", + "battlerTagsHighestStatBoostOnAdd": "{{pokemonNameWithAffix}}の {{statName}}が\nあがっている じょうたいに なった!", + "battlerTagsHighestStatBoostOnRemove": "{{pokemonNameWithAffix}}の {{abilityName}}の こうかが なくなった!", + "battlerTagsMagnetRisenOnAdd": "{{pokemonNameWithAffix}}は でんじりょくで うかびあがった!", + "battlerTagsMagnetRisenOnRemove": "{{pokemonNameWithAffix}}は でんじりょくが なくなった!", + "battlerTagsCritBoostOnAdd": "{{pokemonNameWithAffix}}は はりきっている!", + "battlerTagsCritBoostOnRemove": "{{pokemonNameWithAffix}}は おちついた。", + "battlerTagsSaltCuredOnAdd": "{{pokemonNameWithAffix}}は しおづけに なった!", + "battlerTagsSaltCuredLapse": "{{pokemonNameWithAffix}}は {{moveName}}の\n ダメージを うけている", + "battlerTagsCursedOnAdd": "{{pokemonNameWithAffix}}は じぶんの たいりょくを けずって\n{{pokemonName}}に のろいを かけた!", + "battlerTagsCursedLapse": "{{pokemonNameWithAffix}}は のろわれている!", + "battlerTagsStockpilingOnAdd": "{{pokemonNameWithAffix}}は {{stockpiledCount}}つ たくわえた!" +} as const; diff --git a/src/locales/ja/battler-tags.ts b/src/locales/ja/battler-tags.ts new file mode 100644 index 00000000000..1d897c70f3d --- /dev/null +++ b/src/locales/ja/battler-tags.ts @@ -0,0 +1,12 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battlerTags: SimpleTranslationEntries = { + "trappedDesc": "trapping", + "flinchedDesc": "flinching", + "confusedDesc": "confusion", + "infatuatedDesc": "infatuation", + "seedDesc": "seeding", + "nightmareDesc": "nightmares", + "ingrainDesc": "roots", + "drowsyDesc": "drowsiness", +} as const; diff --git a/src/locales/ja/berry.ts b/src/locales/ja/berry.ts new file mode 100644 index 00000000000..16608a14cca --- /dev/null +++ b/src/locales/ja/berry.ts @@ -0,0 +1,48 @@ +import { BerryTranslationEntries } from "#app/interfaces/locales"; + +export const berry: BerryTranslationEntries = { + "SITRUS": { + name: "オボンのみ", + effect: "HP 50%いかのとき HPを 25パーセント かいふくする", + }, + "LUM": { + name: "ラムのみ", + effect: "すべての じょうたい いじょうと こんらんを かいふくする", + }, + "ENIGMA": { + name: "ナゾのみ", + effect: "こうかばつぐんの わざを うけたとき HPを 25パーセント かいふくする", + }, + "LIECHI": { + name: "チイラのみ", + effect: "HP 25%いかのとき こうげきが あがる", + }, + "GANLON": { + name: "リュガのみ", + effect: "HP 25%いかのとき ぼうぎょが あがる", + }, + "PETAYA": { + name: "ヤタピのみ", + effect: "HP 25%いかのとき とくこうが あがる", + }, + "APICOT": { + name: "ズアのみ", + effect: "HP 25%いかのとき とくぼうが あがる", + }, + "SALAC": { + name: "カムラのみ", + effect: "HP 25%いかのとき すばやさが あがる", + }, + "LANSAT": { + name: "サンのみ", + effect: "HP 25%いかのとき こうげきが きゅうしょに あたりやすくなる", + }, + "STARF": { + name: "スターのみ", + effect: "HP 25%いかのとき のうりょくの どれか 1つが ぐーんと あがる", + }, + "LEPPA": { + name: "ヒメリのみ", + effect: "PPが 0に なった わざの PPを 10だけ かいふくする", + }, +} as const; diff --git a/src/locales/ja/bgm-name.ts b/src/locales/ja/bgm-name.ts new file mode 100644 index 00000000000..01fb86b281d --- /dev/null +++ b/src/locales/ja/bgm-name.ts @@ -0,0 +1,145 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const bgmName: SimpleTranslationEntries = { + "music": "Music: ", + "missing_entries" : "{{name}}", + "battle_kanto_champion": "B2W2 Kanto Champion Battle", + "battle_johto_champion": "B2W2 Johto Champion Battle", + "battle_hoenn_champion": "B2W2 Hoenn Champion Battle", + "battle_sinnoh_champion": "B2W2 Sinnoh Champion Battle", + "battle_champion_alder": "BW Unova Champion Battle", + "battle_champion_iris": "B2W2 Unova Champion Battle", + "battle_kalos_champion": "XY Kalos Champion Battle", + "battle_alola_champion": "USUM Alola Champion Battle", + "battle_galar_champion": "SWSH Galar Champion Battle", + "battle_champion_geeta": "SV Champion Geeta Battle", + "battle_champion_nemona": "SV Champion Nemona Battle", + "battle_champion_kieran": "SV Champion Kieran Battle", + "battle_hoenn_elite": "ORAS Elite Four Battle", + "battle_unova_elite": "BW Elite Four Battle", + "battle_kalos_elite": "XY Elite Four Battle", + "battle_alola_elite": "SM Elite Four Battle", + "battle_galar_elite": "SWSH League Tournament Battle", + "battle_paldea_elite": "SV Elite Four Battle", + "battle_bb_elite": "SV BB League Elite Four Battle", + "battle_final_encounter": "PMD RTDX Rayquaza's Domain", + "battle_final": "BW Ghetsis Battle", + "battle_kanto_gym": "B2W2 Kanto Gym Battle", + "battle_johto_gym": "B2W2 Johto Gym Battle", + "battle_hoenn_gym": "B2W2 Hoenn Gym Battle", + "battle_sinnoh_gym": "B2W2 Sinnoh Gym Battle", + "battle_unova_gym": "BW Unova Gym Battle", + "battle_kalos_gym": "XY Kalos Gym Battle", + "battle_galar_gym": "SWSH Galar Gym Battle", + "battle_paldea_gym": "SV Paldea Gym Battle", + "battle_legendary_kanto": "XY Kanto Legendary Battle", + "battle_legendary_raikou": "HGSS Raikou Battle", + "battle_legendary_entei": "HGSS Entei Battle", + "battle_legendary_suicune": "HGSS Suicune Battle", + "battle_legendary_lugia": "HGSS Lugia Battle", + "battle_legendary_ho_oh": "HGSS Ho-oh Battle", + "battle_legendary_regis_g5": "B2W2 Legendary Titan Battle", + "battle_legendary_regis_g6": "ORAS Legendary Titan Battle", + "battle_legendary_gro_kyo": "ORAS Groudon & Kyogre Battle", + "battle_legendary_rayquaza": "ORAS Rayquaza Battle", + "battle_legendary_deoxys": "ORAS Deoxys Battle", + "battle_legendary_lake_trio": "ORAS Lake Guardians Battle", + "battle_legendary_sinnoh": "ORAS Sinnoh Legendary Battle", + "battle_legendary_dia_pal": "ORAS Dialga & Palkia Battle", + "battle_legendary_giratina": "ORAS Giratina Battle", + "battle_legendary_arceus": "HGSS Arceus Battle", + "battle_legendary_unova": "BW Unova Legendary Battle", + "battle_legendary_kyurem": "BW Kyurem Battle", + "battle_legendary_res_zek": "BW Reshiram & Zekrom Battle", + "battle_legendary_xern_yvel": "XY Xerneas & Yveltal Battle", + "battle_legendary_tapu": "SM Tapu Battle", + "battle_legendary_sol_lun": "SM Solgaleo & Lunala Battle", + "battle_legendary_ub": "SM Ultra Beast Battle", + "battle_legendary_dusk_dawn": "USUM Dusk Mane & Dawn Wings Necrozma Battle", + "battle_legendary_ultra_nec": "USUM Ultra Necrozma Battle", + "battle_legendary_zac_zam": "SWSH Zacian & Zamazenta Battle", + "battle_legendary_glas_spec": "SWSH Glastrier & Spectrier Battle", + "battle_legendary_calyrex": "SWSH Calyrex Battle", + "battle_legendary_birds_galar": "SWSH Galarian Legendary Birds Battle", + "battle_legendary_ruinous": "SV Treasures of Ruin Battle", + "battle_legendary_kor_mir": "SV Depths of Area Zero Battle", + "battle_legendary_loyal_three": "SV Loyal Three Battle", + "battle_legendary_ogerpon": "SV Ogerpon Battle", + "battle_legendary_terapagos": "SV Terapagos Battle", + "battle_legendary_pecharunt": "SV Pecharunt Battle", + "battle_rival": "BW Rival Battle", + "battle_rival_2": "BW N Battle", + "battle_rival_3": "BW Final N Battle", + "battle_trainer": "BW Trainer Battle", + "battle_wild": "BW Wild Battle", + "battle_wild_strong": "BW Strong Wild Battle", + "end_summit": "PMD RTDX Sky Tower Summit", + "battle_rocket_grunt": "HGSS Team Rocket Battle", + "battle_aqua_magma_grunt": "ORAS Team Aqua & Magma Battle", + "battle_galactic_grunt": "BDSP Team Galactic Battle", + "battle_plasma_grunt": "BW Team Plasma Battle", + "battle_flare_grunt": "XY Team Flare Battle", + "battle_rocket_boss": "USUM Giovanni Battle", + "battle_aqua_magma_boss": "ORAS Archie & Maxie Battle", + "battle_galactic_boss": "BDSP Cyrus Battle", + "battle_plasma_boss": "B2W2 Ghetsis Battle", + "battle_flare_boss": "XY Lysandre Battle", + + // Biome Music + "abyss": "PMD EoS Dark Crater", + "badlands": "PMD EoS Barren Valley", + "beach": "PMD EoS Drenched Bluff", + "cave": "PMD EoS Sky Peak Cave", + "construction_site": "PMD EoS Boulder Quarry", + "desert": "PMD EoS Northern Desert", + "dojo": "PMD EoS Marowak Dojo", + "end": "PMD RTDX Sky Tower", + "factory": "PMD EoS Concealed Ruins", + "fairy_cave": "PMD EoS Star Cave", + "forest": "PMD EoS Dusk Forest", + "grass": "PMD EoS Apple Woods", + "graveyard": "PMD EoS Mystifying Forest", + "ice_cave": "PMD EoS Vast Ice Mountain", + "island": "PMD EoS Craggy Coast", + "jungle": "Lmz - Jungle", // The composer thinks about a more creative name + "laboratory": "Firel - Laboratory", // The composer thinks about a more creative name + "lake": "PMD EoS Crystal Cave", + "meadow": "PMD EoS Sky Peak Forest", + "metropolis": "Firel - Metropolis", // The composer thinks about a more creative name + "mountain": "PMD EoS Mt. Horn", + "plains": "PMD EoS Sky Peak Prairie", + "power_plant": "PMD EoS Far Amp Plains", + "ruins": "PMD EoS Deep Sealed Ruin", + "sea": "Andr06 - Marine Mystique", // Name defined by the composer + "seabed": "Firel - Seabed", // The composer thinks about a more creative name + "slum": "Andr06 - Sneaky Snom", // Name defined by the composer + "snowy_forest": "PMD EoS Sky Peak Snowfield", + "space": "Firel - Aether", + "swamp": "PMD EoS Surrounded Sea", + "tall_grass": "PMD EoS Foggy Forest", + "temple": "PMD EoS Aegis Cave", + "town": "PMD EoS Random Dungeon Theme 3", + "volcano": "PMD EoS Steam Cave", + "wasteland": "PMD EoS Hidden Highland", + + // Encounter + "encounter_ace_trainer": "BW Trainers' Eyes Meet (Ace Trainer)", + "encounter_backpacker": "BW Trainers' Eyes Meet (Backpacker)", + "encounter_clerk": "BW Trainers' Eyes Meet (Clerk)", + "encounter_cyclist": "BW Trainers' Eyes Meet (Cyclist)", + "encounter_lass": "BW Trainers' Eyes Meet (Lass)", + "encounter_parasol_lady": "BW Trainers' Eyes Meet (Parasol Lady)", + "encounter_pokefan": "BW Trainers' Eyes Meet (Poke Fan)", + "encounter_psychic": "BW Trainers' Eyes Meet (Psychic)", + "encounter_rich": "BW Trainers' Eyes Meet (Gentleman)", + "encounter_rival": "BW Cheren", + "encounter_roughneck": "BW Trainers' Eyes Meet (Roughneck)", + "encounter_scientist": "BW Trainers' Eyes Meet (Scientist)", + "encounter_twins": "BW Trainers' Eyes Meet (Twins)", + "encounter_youngster": "BW Trainers' Eyes Meet (Youngster)", + + // Other + "heal": "BW Pokémon Heal", + "menu": "PMD EoS Welcome to the World of Pokémon!", + "title": "PMD EoS Top Menu Theme", +} as const; diff --git a/src/locales/ja/biome.ts b/src/locales/ja/biome.ts new file mode 100644 index 00000000000..4551e779c74 --- /dev/null +++ b/src/locales/ja/biome.ts @@ -0,0 +1,40 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const biome: SimpleTranslationEntries = { + "unknownLocation": "覚えていない場所", + "TOWN": "タウン", + "PLAINS": "平原", + "GRASS": "草原", + "TALL_GRASS": "長い草むら", + "METROPOLIS": "シティ", + "FOREST": "森", + "SEA": "海", + "SWAMP": "沼", + "BEACH": "海辺", + "LAKE": "湖", + "SEABED": "海底", + "MOUNTAIN": "山", + "BADLANDS": "悪地", + "CAVE": "洞窟", + "DESERT": "砂漠", + "ICE_CAVE": "氷の洞窟", + "MEADOW": "花畑", + "POWER_PLANT": "発電所", + "VOLCANO": "火山", + "GRAVEYARD": "墓場", + "DOJO": "道場", + "FACTORY": "工場", + "RUINS": "古の遺跡", + "WASTELAND": "荒地", + "ABYSS": "深淵", + "SPACE": "宇宙", + "CONSTRUCTION_SITE": "工事現場", + "JUNGLE": "ジャングル", + "FAIRY_CAVE": "フェアリーの洞窟", + "TEMPLE": "神殿", + "SLUM": "スラム", + "SNOWY_FOREST": "雪の森", + "ISLAND": "島", + "LABORATORY": "ラボ", + "END": "???", +} as const; diff --git a/src/locales/ja/challenges.ts b/src/locales/ja/challenges.ts new file mode 100644 index 00000000000..1383ea021b1 --- /dev/null +++ b/src/locales/ja/challenges.ts @@ -0,0 +1,32 @@ +import { TranslationEntries } from "#app/interfaces/locales"; + +export const challenges: TranslationEntries = { + "title": "チャレンジ じょうけん せってい", + "illegalEvolution": "{{pokemon}} changed into an ineligble pokémon\nfor this challenge!", + "singleGeneration": { + "name": "Mono Gen", + "desc": "You can only use Pokémon from Generation {{gen}}.", + "desc_default": "You can only use Pokémon from the chosen generation.", + "gen_1": "one", + "gen_2": "two", + "gen_3": "three", + "gen_4": "four", + "gen_5": "five", + "gen_6": "six", + "gen_7": "seven", + "gen_8": "eight", + "gen_9": "nine", + }, + "singleType": { + "name": "Mono Type", + "desc": "You can only use Pokémon with the {{type}} type.", + "desc_default": "You can only use Pokémon of the chosen type." + //types in pokemon-info + }, + "freshStart": { + "name": "Fresh Start", + "desc": "You can only use the original starters, and only as if you had just started pokerogue.", + "value.0": "Off", + "value.1": "On", + }, +} as const; diff --git a/src/locales/ja/command-ui-handler.ts b/src/locales/ja/command-ui-handler.ts new file mode 100644 index 00000000000..e120efe223c --- /dev/null +++ b/src/locales/ja/command-ui-handler.ts @@ -0,0 +1,9 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const commandUiHandler: SimpleTranslationEntries = { + "fight": "たたかう", + "ball": "ボール", + "pokemon": "ポケモン", + "run": "にげる", + "actionMessage": "{{pokemonName}}は どうする?", +} as const; diff --git a/src/locales/ja/common.ts b/src/locales/ja/common.ts new file mode 100644 index 00000000000..750322e1f09 --- /dev/null +++ b/src/locales/ja/common.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const common: SimpleTranslationEntries = { + "start": "Start", + "luckIndicator": "Luck:", + "shinyOnHover": "Shiny", + "commonShiny": "Common", + "rareShiny": "Rare", + "epicShiny": "Epic", +} as const; diff --git a/src/locales/ja/config.ts b/src/locales/ja/config.ts new file mode 100644 index 00000000000..6af79547a04 --- /dev/null +++ b/src/locales/ja/config.ts @@ -0,0 +1,117 @@ +import { ability } from "./ability"; +import { abilityTriggers } from "./ability-trigger"; +import { arenaFlyout } from "./arena-flyout"; +import { arenaTag } from "./arena-tag"; +import { PGFachv, PGMachv } from "./achv"; +import { battle } from "./battle"; +import { battleInfo } from "./battle-info"; +import { battleMessageUiHandler } from "./battle-message-ui-handler"; +import { battlerTags } from "./battler-tags"; +import { berry } from "./berry"; +import { bgmName } from "./bgm-name"; +import { biome } from "./biome"; +import { challenges } from "./challenges"; +import { commandUiHandler } from "./command-ui-handler"; +import { + PGFbattleSpecDialogue, + PGFdialogue, + PGFdoubleBattleDialogue, + PGFmiscDialogue, + PGMbattleSpecDialogue, + PGMdialogue, + PGMdoubleBattleDialogue, + PGMmiscDialogue +} from "./dialogue"; + +import { egg } from "./egg"; +import { fightUiHandler } from "./fight-ui-handler"; +import { filterBar } from "./filter-bar"; +import { gameMode } from "./game-mode"; +import { gameStatsUiHandler } from "./game-stats-ui-handler"; +import { growth } from "./growth"; +import { menu } from "./menu"; +import { menuUiHandler } from "./menu-ui-handler"; +import { modifier } from "./modifier"; +import { modifierType } from "./modifier-type"; +import { move } from "./move"; +import { nature } from "./nature"; +import { pokeball } from "./pokeball"; +import { pokemon } from "./pokemon"; +import { pokemonForm, battlePokemonForm } from "./pokemon-form"; +import { pokemonInfo } from "./pokemon-info"; +import { pokemonInfoContainer } from "./pokemon-info-container"; +import { pokemonSummary } from "./pokemon-summary"; +import { saveSlotSelectUiHandler } from "./save-slot-select-ui-handler"; +import { splashMessages } from "./splash-messages"; +import { starterSelectUiHandler } from "./starter-select-ui-handler"; +import { statusEffect } from "./status-effect"; +import { titles, trainerClasses, trainerNames } from "./trainers"; +import { tutorial } from "./tutorial"; +import { voucher } from "./voucher"; +import { terrain, weather } from "./weather"; +import { partyUiHandler } from "./party-ui-handler"; +import { settings } from "./settings.js"; +import { common } from "./common.js"; +import { modifierSelectUiHandler } from "./modifier-select-ui-handler"; +import { moveTriggers } from "./move-trigger"; + +export const jaConfig = { + ability: ability, + abilityTriggers: abilityTriggers, + arenaFlyout: arenaFlyout, + arenaTag: arenaTag, + battle: battle, + battleInfo: battleInfo, + battleMessageUiHandler: battleMessageUiHandler, + battlePokemonForm: battlePokemonForm, + battlerTags: battlerTags, + berry: berry, + bgmName: bgmName, + biome: biome, + challenges: challenges, + commandUiHandler: commandUiHandler, + common: common, + PGMachv: PGMachv, + PGFachv: PGFachv, + PGMdialogue: PGMdialogue, + PGFdialogue: PGFdialogue, + PGMbattleSpecDialogue: PGMbattleSpecDialogue, + PGFbattleSpecDialogue: PGFbattleSpecDialogue, + PGMmiscDialogue: PGMmiscDialogue, + PGFmiscDialogue: PGFmiscDialogue, + PGMdoubleBattleDialogue: PGMdoubleBattleDialogue, + PGFdoubleBattleDialogue: PGFdoubleBattleDialogue, + egg: egg, + fightUiHandler: fightUiHandler, + filterBar: filterBar, + gameMode: gameMode, + gameStatsUiHandler: gameStatsUiHandler, + growth: growth, + menu: menu, + menuUiHandler: menuUiHandler, + modifier: modifier, + modifierType: modifierType, + move: move, + nature: nature, + pokeball: pokeball, + pokemon: pokemon, + pokemonForm: pokemonForm, + pokemonInfo: pokemonInfo, + pokemonInfoContainer: pokemonInfoContainer, + pokemonSummary: pokemonSummary, + saveSlotSelectUiHandler: saveSlotSelectUiHandler, + settings: settings, + splashMessages: splashMessages, + starterSelectUiHandler: starterSelectUiHandler, + statusEffect: statusEffect, + terrain: terrain, + titles: titles, + trainerClasses: trainerClasses, + trainerNames: trainerNames, + tutorial: tutorial, + voucher: voucher, + weather: weather, + partyUiHandler: partyUiHandler, + modifierSelectUiHandler: modifierSelectUiHandler, + moveTriggers: moveTriggers +}; diff --git a/src/locales/ja/dialogue.ts b/src/locales/ja/dialogue.ts new file mode 100644 index 00000000000..e783ea14006 --- /dev/null +++ b/src/locales/ja/dialogue.ts @@ -0,0 +1,2920 @@ +import { DialogueTranslationEntries, SimpleTranslationEntries } from "#app/interfaces/locales"; + +// Dialogue of the NPCs in the game when the player character is male (or unset) +export const PGMdialogue: DialogueTranslationEntries = { + "youngster": { + "encounter": { + 1: "Hey, wanna battle?", + 2: "Are you a new trainer too?", + 3: "Hey, I haven't seen you before. Let's battle!", + 4: "I just lost, so I'm trying to find more Pokémon.\nWait! You look weak! Come on, let's battle!", + 5: "Have we met or not? I don't really remember. Well, I guess it's nice to meet you anyway!", + 6: "All right! Let's go!", + 7: "All right! Here I come! I'll show you my power!", + 8: "Haw haw haw... I'll show you how hawesome my Pokémon are!", + 9: "No need to waste time saying hello. Bring it on whenever you're ready!", + 10: "Don't let your guard down, or you may be crying when a kid beats you.", + 11: "I've raised my Pokémon with great care. You're not allowed to hurt them!", + 12: "Glad you made it! It won't be an easy job from here.", + 13: "The battles continue forever! Welcome to the world with no end!" + }, + "victory": { + 1: "Wow! You're strong!", + 2: "I didn't stand a chance, huh?", + 3: "I'll find you again when I'm older and beat you!", + 4: "Ugh. I don't have any more Pokémon.", + 5: "No way… NO WAY! How could I lose again…", + 6: "No! I lost!", + 7: "Whoa! You are incredible! I'm amazed and surprised!", + 8: "Could it be… How… My Pokémon and I are the strongest, though…", + 9: "I won't lose next time! Let's battle again sometime!", + 10: "Sheesh! Can't you see that I'm just a kid! It wasn't fair of you to go all out like that!", + 11: "Your Pokémon are more amazing! Trade with me!", + 12: "I got a little carried away earlier, but what job was I talking about?", + 13: "Ahaha! There it is! That's right! You're already right at home in this world!" + } + }, + "lass": { + "encounter": { + 1: "Let's have a battle, shall we?", + 2: "You look like a new trainer. Let's have a battle!", + 3: "I don't recognize you. How about a battle?", + 4: "Let's have a fun Pokémon battle!", + 5: "I'll show you the ropes of how to really use Pokémon!", + 6: "A serious battle starts from a serious beginning! Are you sure you're ready?", + 7: "You're only young once. And you only get one shot at a given battle. Soon, you'll be nothing but a memory.", + 8: "You'd better go easy on me, OK? Though I'll be seriously fighting!", + 9: "School is boring. I've got nothing to do. Yawn. I'm only battling to kill the time." + }, + "victory": { + 1: "That was impressive! I've got a lot to learn.", + 2: "I didn't think you'd beat me that bad…", + 3: "I hope we get to have a rematch some day.", + 4: "That was pretty amazingly fun! You've totally exhausted me…", + 5: "You actually taught me a lesson! You're pretty amazing!", + 6: "Seriously, I lost. That is, like, seriously depressing, but you were seriously cool.", + 7: "I don't need memories like this. Deleting memory…", + 8: "Hey! I told you to go easy on me! Still, you're pretty cool when you're serious.", + 9: "I'm actually getting tired of battling… There's gotta be something new to do…" + } + }, + "breeder": { + "encounter": { + 1: "Obedient Pokémon, selfish Pokémon… Pokémon have unique characteristics.", + 2: "Even though my upbringing and behavior are poor, I've raised my Pokémon well.", + 3: "Hmm, do you discipline your Pokémon? Pampering them too much is no good.", + }, + "victory": { + 1: "It is important to nurture and train each Pokémon's characteristics.", + 2: "Unlike my diabolical self, these are some good Pokémon.", + 3: "Too much praise can spoil both Pokémon and people.", + }, + "defeat": { + 1: "You should not get angry at your Pokémon, even if you lose a battle.", + 2: "Right? Pretty good Pokémon, huh? I'm suited to raising things.", + 3: "No matter how much you love your Pokémon, you still have to discipline them when they misbehave." + } + }, + "breeder_female": { + "encounter": { + 1: "Pokémon never betray you. They return all the love you give them.", + 2: "Shall I give you a tip for training good Pokémon?", + 3: "I have raised these very special Pokémon using a special method." + }, + "victory": { + 1: "Ugh… It wasn't supposed to be like this. Did I administer the wrong blend?", + 2: "How could that happen to my Pokémon… What are you feeding your Pokémon?", + 3: "If I lose, that tells you I was just killing time. It doesn't damage my ego at all." + }, + "defeat": { + 1: "This proves my Pokémon have accepted my love.", + 2: "The real trick behind training good Pokémon is catching good Pokémon.", + 3: "Pokémon will be strong or weak depending on how you raise them." + } + }, + "fisherman": { + "encounter": { + 1: "Aack! You made me lose a bite!\nWhat are you going to do about it?", + 2: "Go away! You're scaring the Pokémon!", + 3: "Let's see if you can reel in a victory!", + }, + "victory": { + 1: "Just forget about it.", + 2: "Next time, I'll be reelin' in the triumph!", + 3: "Guess I underestimated the currents this time.", + }, + }, + "fisherman_female": { + "encounter": { + 1: "Woah! I've hooked a big one!", + 2: "Line's in, ready to reel in success!", + 3: "Ready to make waves!" + }, + "victory": { + 1: "I'll be back with a stronger hook.", + 2: "I'll reel in victory next time.", + 3: "I'm just sharpening my hooks for the comeback!" + }, + }, + "swimmer": { + "encounter": { + 1: "Time to dive in!", + 2: "Let's ride the waves of victory!", + 3: "Ready to make a splash!", + }, + "victory": { + 1: "Drenched in defeat!", + 2: "A wave of defeat!", + 3: "Back to shore, I guess.", + }, + }, + "backpacker": { + "encounter": { + 1: "Pack up, game on!", + 2: "Let's see if you can keep pace!", + 3: "Gear up, challenger!", + 4: "I've spent 20 years trying to find myself… But where am I?" + }, + "victory": { + 1: "Tripped up this time!", + 2: "Oh, I think I'm lost.", + 3: "Dead end!", + 4: "Wait up a second! Hey! Don't you know who I am?" + }, + }, + "ace_trainer": { + "encounter": { + 1: "You seem quite confident.", + 2: "Your Pokémon… Show them to me…", + 3: "Because I'm an Ace Trainer, people think I'm strong.", + 4: "Are you aware of what it takes to be an Ace Trainer?" + }, + "victory": { + 1: "Yes… You have good Pokémon…", + 2: "What?! But I'm a battling genius!", + 3: "Of course, you are the main character!", + 4: "OK! OK! You could be an Ace Trainer!" + }, + "defeat": { + 1: "I am devoting my body and soul to Pokémon battles!", + 2: "All within my expectations… Nothing to be surprised about…", + 3: "I thought I'd grow up to be a frail person who looked like they would break if you squeezed them too hard.", + 4: "Of course I'm strong and don't lose. It's important that I win gracefully." + } + }, + "parasol_lady": { + "encounter": { + 1: "Time to grace the battlefield with elegance and poise!", + }, + "victory": { + 1: "My elegance remains unbroken!", + } + }, + "twins": { + "encounter": { + 1: "Get ready, because when we team up, it's double the trouble!", + 2: "Two hearts, one strategy – let's see if you can keep up with our twin power!", + 3: "Hope you're ready for double trouble, because we're about to bring the heat!" + }, + "victory": { + 1: "We may have lost this round, but our bond remains unbreakable!", + 2: "Our twin spirit won't be dimmed for long.", + 3: "We'll come back stronger as a dynamic duo!" + }, + "defeat": { + 1: "Twin power reigns supreme!", + 2: "Two hearts, one triumph!", + 3: "Double the smiles, double the victory dance!" + } + }, + "cyclist": { + "encounter": { + 1: "Get ready to eat my dust!", + 2: "Gear up, challenger! I'm about to leave you in the dust!", + 3: "Pedal to the metal, let's see if you can keep pace!" + }, + "victory": { + 1: "Spokes may be still, but determination pedals on.", + 2: "Outpaced!", + 3: "The road to victory has many twists and turns yet to explore." + }, + }, + "black_belt": { + "encounter": { + 1: "I praise your courage in challenging me! For I am the one with the strongest kick!", + 2: "Oh, I see. Would you like to be cut to pieces? Or do you prefer the role of punching bag?" + }, + "victory": { + 1: "Oh. The Pokémon did the fighting. My strong kick didn't help a bit.", + 2: "Hmmm… If I was going to lose anyway, I was hoping to get totally messed up in the process." + }, + }, + "battle_girl": { + "encounter": { + 1: "You don't have to try to impress me. You can lose against me.", + }, + "victory": { + 1: "It's hard to say good-bye, but we are running out of time…", + }, + }, + "hiker": { + "encounter": { + 1: "My middle-age spread has given me as much gravitas as the mountains I hike!", + 2: "I inherited this big-boned body from my parents… I'm like a living mountain range…", + }, + "victory": { + 1: "At least I cannot lose when it comes to BMI!", + 2: "It's not enough… It's never enough. My bad cholesterol isn't high enough…" + }, + }, + "ranger": { + "encounter": { + 1: "When I am surrounded by nature, most other things cease to matter.", + 2: "When I'm living without nature in my life, sometimes I'll suddenly feel an anxiety attack coming on." + }, + "victory": { + 1: "It doesn't matter to the vastness of nature whether I win or lose…", + 2: "Something like this is pretty trivial compared to the stifling feelings of city life." + }, + "defeat": { + 1: "I won the battle. But victory is nothing compared to the vastness of nature…", + 2: "I'm sure how you feel is not so bad if you compare it to my anxiety attacks…" + } + }, + "scientist": { + "encounter": { + 1: "My research will lead this world to peace and joy.", + }, + "victory": { + 1: "I am a genius… I am not supposed to lose against someone like you…", + }, + }, + "school_kid": { + "encounter": { + 1: "…Heehee. I'm confident in my calculations and analysis.", + 2: "I'm gaining as much experience as I can because I want to be a Gym Leader someday." + }, + "victory": { + 1: "Ohhhh… Calculation and analysis are perhaps no match for chance…", + 2: "Even difficult, trying experiences have their purpose, I suppose." + } + }, + "artist": { + "encounter": { + 1: "I used to be popular, but now I am all washed up.", + }, + "victory": { + 1: "As times change, values also change. I realized that too late.", + }, + }, + "guitarist": { + "encounter": { + 1: "Get ready to feel the rhythm of defeat as I strum my way to victory!", + }, + "victory": { + 1: "Silenced for now, but my melody of resilience will play on.", + }, + }, + "worker": { + "encounter": { + 1: "It bothers me that people always misunderstand me. I'm a lot more pure than everyone thinks.", + }, + "victory": { + 1: "I really don't want my skin to burn, so I want to stay in the shade while I work.", + }, + }, + "worker_female": { + "encounter": { + 1: `It bothers me that people always misunderstand me. + $I'm a lot more pure than everyone thinks.` + }, + "victory": { + 1: "I really don't want my skin to burn, so I want to stay in the shade while I work." + }, + "defeat": { + 1: "My body and mind aren't necessarily always in sync." + } + }, + "worker_double": { + "encounter": { + 1: "I'll show you we can break you. We've been training in the field!", + }, + "victory": { + 1: "How strange… How could this be… I shouldn't have been outmuscled.", + }, + }, + "hex_maniac": { + "encounter": { + 1: "I normally only ever listen to classical music, but if I lose, I think I shall try a bit of new age!", + 2: "I grow stronger with each tear I cry." + }, + "victory": { + 1: "Is this the dawning of the age of Aquarius?", + 2: "Now I can get even stronger. I grow with every grudge." + }, + "defeat": { + 1: "New age simply refers to twentieth century classical composers, right?", + 2: "Don't get hung up on sadness or frustration. You can use your grudges to motivate yourself." + } + }, + "psychic": { + "encounter": { + 1: "Hi! Focus!", + }, + "victory": { + 1: "Eeeeek!", + }, + }, + "officer": { + "encounter": { + 1: "Brace yourself, because justice is about to be served!", + 2: "Ready to uphold the law and serve justice on the battlefield!" + }, + "victory": { + 1: "The weight of justice feels heavier than ever…", + 2: "The shadows of defeat linger in the precinct." + } + }, + "beauty": { + "encounter": { + 1: "My last ever battle… That's the way I'd like us to view this match…", + }, + "victory": { + 1: "It's been fun… Let's have another last battle again someday…", + }, + }, + "baker": { + "encounter": { + 1: "Hope you're ready to taste defeat!" + }, + "victory": { + 1: "I'll bake a comeback." + }, + }, + "biker": { + "encounter": { + 1: "Time to rev up and leave you in the dust!" + }, + "victory": { + 1: "I'll tune up for the next race." + }, + }, + "firebreather": { + "encounter": { + 1: "My flames shall devour you!", + 2: "My soul is on fire. I'll show you how hot it burns!", + 3: "Step right up and take a look!" + }, + "victory": { + 1: "I burned down to ashes...", + 2: "Yow! That's hot!", + 3: "Ow! I scorched the tip of my nose!" + }, + }, + "sailor": { + "encounter": { + 1: "Matey, you're walking the plank if you lose!", + 2: "Come on then! My sailor's pride is at stake!", + 3: "Ahoy there! Are you seasick?" + }, + "victory": { + 1: "Argh! Beaten by a kid!", + 2: "Your spirit sank me!", + 3: "I think it's me that's seasick..." + }, + }, + + "archer": { + "encounter": { + 1: "Before you go any further, let's see how you far against us, Team Rocket!", + 2: "I have received reports that your skills are not insignificant. Let's see if they are true.", + 3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." + }, + "victory": { + 1: "What a blunder!", + 2: "With my current skills, I was not up to the task after all.", + 3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." + }, + }, + "ariana": { + "encounter": { + 1: `Hold it right there! We can't someone on the loose." + $It's harmful to Team Rocket's pride, you see.`, + 2: `I don't know or care if what I'm doing is right or wrong... + $I just put my faith in Giovanni and do as I am told`, + 3: "Your trip ends here. I'm going to take you down!" + }, + "victory": { + 1: `Tch, you really are strong. It's too bad. + $If you were to join Team Rocket, you could become an Executive.`, + 2: "I... I'm shattered...", + 3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" + }, + }, + "proton": { + "encounter": { + 1: "What do you want? If you interrupt our work, don't expect any mercy!", + 2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket… + $I strongly urge you not to interfere with our business!`, + 3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" + }, + "victory": { + 1: "The fortress came down!", + 2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", + 3: "I am defeated… But I will not forget this!" + }, + }, + + "petrel": { + "encounter": { + 1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. + $The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni? + $I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, + 2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + 3: "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + 1: "OK, OK. I'll tell you where he is.", + 2: "I… I couldn't do a thing… Giovanni, please forgive me…", + 3: "No, I can't let this affect me. I have to inform the others…" + }, + }, + "tabitha": { + "encounter": { + 1: "Hehehe! So you've come all the way here! But you're too late!", + 2: `Hehehe... Got here already, did you? We underestimated you! But this is it! + $I'm a cut above the Grunts you've seen so far. I'm not stalling for time. + $I'm going to pulverize you!`, + 3: "I'm going to give you a little taste of pain! Resign yourself to it!" + }, + "victory": { + 1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! + $If you get lost now, you won't have to face a sound whipping!`, + 2: "Hehehe... So, I lost, too...", + 3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." + }, + }, + "courtney": { + "encounter": { + 1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", + 2: "... Well then...Deleting...", + 3: "...Ha. ...Analyzing... ...Hah♪" + }, + "victory": { + 1: "... ...Change...the world.", + 2: `As anticipated. Unanticipated. You. Target lock...completed. + $Commencing...experiment. You. Forever. Aha... ♪`, + 3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" + }, + }, + "shelly": { + "encounter": { + 1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? + $You're either absolutely fearless, simply ignorant, or both! + $You're so cute, you're disgusting! I'll put you down`, + 2: "What's this? Who's this spoiled brat?", + 3: "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + 1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. + $We'll have to pull out. But this isn't the last you'll see of Team Aqua! + $We have other plans! Don't you forget it!`, + 2: "Ahhh?! Did I go too easy on you?!", + 3: `Uh. Are you telling me you've upped your game even more during the fight? + $You're a brat with a bright future… My Pokémon and I don't have any strength left to fight… + $Go on… Go and be destroyed by Archie.` + }, + }, + "matt": { + "encounter": { + 1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", + 2: "Oho! You! You're that funny kid!", + 3: "What are you doing here? Did you follow us?" + }, + "victory": { + 1: "All right then, until the Boss has time for you, I'll be your opponent!", + 2: `I can feel it! I can feel it, all right! The strength coming offa you! + $More! I still want more! But looks like we're outta time...`, + 3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" + }, + }, + "mars": { + "encounter": { + 1: "I'm Mars, one of Team Galactic's top Commanders.", + 2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + 3: "Feeling nervous? You should be!" + }, + "victory": { + 1: "This can't be happening! How did I lose?!", + 2: "You have some skill, I'll give you that.", + 3: "Defeated... This was a costly mistake." + } + }, + "jupiter": { + "encounter": { + 1: "Jupiter, Commander of Team Galactic, at your service.", + 2: "Resistance is futile. Team Galactic will prevail!", + 3: "You're trembling... scared already?" + }, + "victory": { + 1: "No way... I lost?!", + 2: "Impressive, you've got guts!", + 3: "Losing like this... How embarrassing." + } + }, + "saturn": { + "encounter": { + 1: "I am Saturn, Commander of Team Galactic.", + 2: "Our mission is absolute. Any hindrance will be obliterated!", + 3: "Is that fear I see in your eyes?" + }, + "victory": { + 1: "Impossible... Defeated by you?!", + 2: "You have proven yourself a worthy adversary.", + 3: "Bestowed in defeat... This is unacceptable." + }}, + "zinzolin": { + "encounter": { + 1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", + 2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", + 3: "You're an impressive Trainer to have made it this far. But it ends here." + }, + "victory": { + 1: "Ghetsis... I have failed you...", + 2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", + 3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } + }, + "rood": { + "encounter": { + 1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + 2: "Oh, this icy wind... I never thought I'd have to fight here!", + 3: "You are a remarkable Trainer to have made it this far. But this is where it ends." + }, + "victory": { + 1: "Ghetsis... I have failed my mission...", + 2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", + 3: "Hm. You are a talented Trainer, but unfortunately not talented enough." + } + }, + "xerosic": { + "encounter": { + 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", + 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", + 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" + }, + "victory": { + 1: "Ah, you're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You did it! To the victor go the spoils!", + 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + 1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + 2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + 3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + 1: "You're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You've done well. Victory is yours.", + 3: "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, + "rocket_grunt": { + "encounter": { + 1: "Se prepara pra encrenca!", + 2: "We're pulling a big job here! Get lost, kid!", + 3: "Hand over your Pokémon, or face the wrath of Team Rocket!", + 4: "You're about to experience the true terror of Team Rocket!", + 5: "Hey, kid! Me am a Team Rocket member kind of guy!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "Equipe Rocket decolando de novo!", + 2: "Oh no! I dropped the Lift Key!", + 3: "I blew it!", + 4: "My associates won't stand for this!", + 5: "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" //Use of wrong grammar is deliberate. + }, + }, + "magma_grunt": { + "encounter": { + 1: "Se você se meter com a Equipe Magma, não teremos piedade!", + 2: "You'd better not interfere with our plans! We're making the world a better place!", + 3: "You're in the way! Team Magma has no time for kids like you!", + 4: "I hope you brought marshmallows because things are about to heat up!", + 5: "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" + }, + "victory": { + 1: "Ahn? Eu perdi?!", + 2: "I can't believe I lost! I even skipped lunch for this", + 3: "No way! You're just a kid!", + 4: "Urrrgh... I should've ducked into our hideout right away...", + 5: "You beat me... Do you think the boss will dock my pay for this?" + }, + }, + "aqua_grunt": { + "encounter": { + 1: "Não pegamos leve com quem se mete com a Equipe Aqua, nem mesmo crianças!", + 2: "Grrr... You've got some nerve meddling with Team Aqua!", + 3: "You're about to get soaked! And not just from my water Pokémon!", + 4: "We, Team Aqua, exist for the good of all!", + 5: "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" + }, + "victory": { + 1: "Tá de brincadeira!", + 2: "Arrgh, I didn't count on being meddled with by some meddling kid!", + 3: "I lost?! Guess I'll have to swim back to the hideout now...", + 4: "Oh, man, what a disaster... The boss is going to be furious...", + 5: "You beat me... Do you think the boss will make me walk the plank for this?" + }, + }, + "galactic_grunt": { + "encounter": { + 1: "Não mexe com a Equipe Galáctica!", + 2: "Witness the power of our technology and the future we envision!", + 3: "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + 4: "Get ready to lose!", + 5: "Hope you're ready for a cosmic beatdown!" + }, + "victory": { + 1: "Fui amassado...", + 2: "This setback means nothing in the grand scheme.", + 3: "Our plans are bigger than this defeat.", + 4: "How?!", + 5: "Note to self: practice Pokémon battling, ASAP." + }, + }, + "plasma_grunt": { + "encounter": { + 1: "Não toleramos pessoas que pensam diferente de nós!", + 2: "If I win against you, release your Pokémon!", + 3: "If you get in the way of Team Plasma, I'll take care of you!", + 4: "Team Plasma will liberate Pokémon from selfish humans like you!", + 5: "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." + }, + "victory": { + 1: "Plasmaaaaaaaaa!", + 2: "How could I lose...", + 3: "...What a weak Pokémon, I'll just have to go steal some better ones!", + 4: "Great plans are always interrupted.", + 5: "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" + }, + }, + "flare_grunt": { + "encounter": { + 1: "Your Pokémon are no match for the elegance of Team Flare.", + 2: "Hope you brought your sunglasses, because things are about to get bright!", + 3: "Team Flare will cleanse the world of imperfection!", + 4: "Prepare to face the brilliance of Team Flare!", + 5: "Fashion is most important to us!" + }, + "victory": { + 1: "The future doesn't look bright for me.", + 2: "Perhaps there's more to battling than I thought. Back to the drawing board.", + 3: "Gahh?! I lost?!", + 4: "Even in defeat, Team Flare's elegance shines through.", + 5: "You may have beaten me, but when I lose, I go out in style!" + }, + }, + "rocket_boss_giovanni_1": { + "encounter": { + 1: "So! I must say, I am impressed you got here!" + }, + "victory": { + 1: "WHAT! This cannot be!" + }, + "defeat": { + 1: "Mark my words. Not being able to measure your own strength shows that you are still a child." + } + }, + "rocket_boss_giovanni_2": { + "encounter": { + 1: "My old associates need me... Are you going to get in my way?" + }, + "victory": { + 1: "How is this possible...? The precious dream of Team Rocket has become little more than an illusion..." + }, + "defeat": { + 1: "Team Rocket will be reborn again, and I will rule the world!" + } + }, + "magma_boss_maxie_1": { + "encounter": { + 1: "I will bury you by my own hand. I hope you appreciate this honor!" + }, + "victory": { + 1: "Ugh! You are... quite capable...\nI fell behind, but only by an inch..." + }, + "defeat": { + 1: "Team Magma will prevail!" + } + }, + "magma_boss_maxie_2": { + "encounter": { + 1: `You are the final obstacle remaining between me and my goals. + $Brace yourself for my ultimate attack! Fuhahaha!` + }, + "victory": { + 1: "This... This is not.. Ngh..." + }, + "defeat": { + 1: "And now... I will transform this planet to a land ideal for humanity." + } + }, + "aqua_boss_archie_1": { + "encounter": { + 1: "I'm the leader of Team Aqua, so I'm afraid it's the rope's end for you." + }, + "victory": { + 1: "Let's meet again somewhere. I'll be sure to remember that face." + }, + "defeat": { + 1: "Brilliant! My team won't hold back now!" + } + }, + "aqua_boss_archie_2": { + "encounter": { + 1: "I've been waiting so long for this day to come.\nThis is the true power of my team!" + }, + "victory": { + 1: "Like I figured..." + }, + "defeat": { + 1: "I'll return everything in this world to its original, pure state!!" + } + }, + "galactic_boss_cyrus_1": { + "encounter": { + 1: `You were compelled to come here by such vacuous sentimentality. + $I will make you regret paying heed to your heart!` + }, + "victory": { + 1: "Interesting. And quite curious." + }, + "defeat": { + 1: "I will create my new world..." + } + }, + "galactic_boss_cyrus_2": { + "encounter": { + 1: `So we meet again. It seems our fates have become intertwined. + $But here and now, I will finally break that bond!` + }, + "victory": { + 1: "How? How? HOW?!" + }, + "defeat": { + 1: "Farewell." + } + }, + "plasma_boss_ghetsis_1": { + "encounter": { + 1: "I won't allow anyone to stop me! No matter who does what!" + }, + "victory": { + 1: "How can this be? I'm the creator of Team Plasma! I'm perfect!" + }, + "defeat": { + 1: "I am the perfect ruler of a perfect new world! Mwa ha ha!" + } + }, + "plasma_boss_ghetsis_2": { + "encounter": { + 1: "Come now! I want to see your face at the moment you lose all hope!" + }, + "victory": { + 1: "My calculations... No! My careful schemes! The world should be mine!" + }, + "defeat": { + 1: "Kyurem! Use Absofusion!" + } + }, + "flare_boss_lysandre_1": { + "encounter": { + 1: "Do you want to stop me? Show me in battle." + }, + "victory": { + 1: "You are here to stop me. But I ask you to wait. " + }, + "defeat": { + 1: "Pokemon...Shall no longer exist." + } + }, + "flare_boss_lysandre_2": { + "encounter": { + 1: "The future you want, or the future I want... Let us see which one is more deserving, shall we?" + }, + "victory": { + 1: "Whaugh!" + }, + "defeat": { + 1: "Fools with no vision will continue to befoul this beautiful world." + } + }, + "brock": { + "encounter": { + 1: "My expertise on Rock-type Pokémon will take you down! Come on!", + 2: "My rock-hard willpower will overwhelm you!", + 3: "Allow me to show you the true strength of my Pokémon!" + }, + "victory": { + 1: "Your Pokémon's strength have overcome my rock-hard defenses!", + 2: "The world is huge! I'm glad to have had a chance to battle you.", + 3: "Perhaps I should go back to pursuing my dream as a Pokémon Breeder…" + }, + "defeat": { + 1: "The best offense is a good defense!\nThat's my way of doing things!", + 2: "Come study rocks with me next time to better learn how to fight them!", + 3: "Hah, all my traveling around the regions is paying off!" + } + }, + "misty": { + "encounter": { + 1: "My policy is an all out offensive with Water-type Pokémon!", + 2: "Hiya, I'll show you the strength of my aquatic Pokémon!", + 3: "My dream was to go on a journey and battle powerful trainers…\nWill you be a sufficient challenge?" + }, + "victory": { + 1: "You really are strong… I'll admit that you are skilled…", + 2: "Grrr… You know you just got lucky, right?!", + 3: "Wow, you're too much! I can't believe you beat me!" + }, + "defeat": { + 1: "Was the mighty Misty too much for you?", + 2: "I hope you saw my Pokémon's elegant swimming techniques!", + 3: "Your Pokémon were no match for my pride and joys!" + } + }, + "lt_surge": { + "encounter": { + 1: "My Electric Pokémon saved me during the war! I'll show you how!", + 2: "Ten-hut! I'll shock you into surrender!", + 3: "I'll zap you just like I do to all my enemies in battle!" + }, + "victory": { + 1: "Whoa! Your team's the real deal, kid!", + 2: "Aaargh, you're strong! Even my electric tricks lost against you.", + 3: "That was an absolutely shocking loss!" + }, + "defeat": { + 1: "Oh yeah! When it comes to Electric-type Pokémon, I'm number one in the world!", + 2: "Hahaha! That was an electrifying battle, kid!", + 3: "A Pokémon battle is war, and I have showed you first-hand combat!" + } + }, + "erika": { + "encounter": { + 1: "Ah, the weather is lovely here…\nOh, a battle? Very well then.", + 2: "My Pokémon battling skills rival that of my flower arranging skills.", + 3: "Oh, I hope the pleasant aroma of my Pokémon doesn't put me to sleep again…", + 4: "Seeing flowers in a garden is so soothing." + }, + "victory": { + 1: "Oh! I concede defeat.", + 2: "That match was most delightful.", + 3: "Ah, it appears it is my loss…", + 4: "Oh, my goodness." + }, + "defeat": { + 1: "I was afraid I would doze off…", + 2: "Oh my, it seems my Grass Pokémon overwhelmed you.", + 3: "That battle was such a soothing experience.", + 4: "Oh… Is that all?" + } + }, + "janine": { + "encounter": { + 1: "I am mastering the art of poisonous attacks.\nI shall spar with you today!", + 2: "Father trusts that I can hold my own.\nI will prove him right!", + 3: "My ninja techniques are only second to my Father's!\nCan you keep up?" + }, + "victory": { + 1: "Even now, I still need training… I understand.", + 2: "Your battle technique has outmatched mine.", + 3: "I'm going to really apply myself and improve my skills." + }, + "defeat": { + 1: "Fufufu… the poison has sapped all your strength to battle.", + 2: "Ha! You didn't stand a chance against my superior ninja skills!", + 3: "Father's faith in me has proven to not be misplaced." + } + }, + "sabrina": { + "encounter": { + 1: "Through my psychic ability, I had a vision of your arrival!", + 2: "I dislike fighting, but if you wish, I will show you my powers!", + 3: "I can sense great ambition in you. I shall see if it not unfounded." + }, + "victory": { + 1: "Your power… It far exceeds what I foresaw…", + 2: "I failed to accurately predict your power.", + 3: "Even with my immense psychic powers, I cannot sense another as strong as you." + }, + "defeat": { + 1: "This victory… It is exactly as I foresaw in my visions!", + 2: "Perhaps it was another I sensed a great desire in…", + 3: "Hone your abilities before recklessly charging into battle.\nYou never know what the future may hold if you do…" + } + }, + "blaine": { + "encounter": { + 1: "Hah! Hope you brought a Burn Heal!", + 2: "My fiery Pokémon will incinerate all challengers!", + 3: "Get ready to play with fire!" + }, + "victory": { + 1: "I have burned down to nothing! Not even ashes remain!", + 2: "Didn't I stoke the flames high enough?", + 3: "I'm all burned out… But this makes my motivation to improve burn even hotter!" + }, + "defeat": { + 1: "My raging inferno cannot be quelled!", + 2: "My Pokémon have been powered up with the heat from this victory!", + 3: "Hah! My passion burns brighter than yours!" + } + }, + "giovanni": { + "encounter": { + 1: "I, the leader of Team Rocket, will make you feel a world of pain!", + 2: "My training here will be vital before I am to face my old associates again.", + 3: "I do not think you are prepared for the level of failure you are about to experience!" + }, + "victory": { + 1: "WHAT! Me, lose?! There is nothing I wish to say to you!", + 2: "Hmph… You could never understand what I hope to achieve.", + 3: "This defeat is merely delaying the inevitable.\nI will rise Team Rocket from the ashes in due time." + }, + "defeat": { + 1: "Not being able to measure your own strength shows that you are still but a child.", + 2: "Do not try to interfere with me again.", + 3: "I hope you understand how foolish challenging me was." + } + }, + "roxanne": { + "encounter": { + 1: "Would you kindly demonstrate how you battle?", + 2: "You can learn many things by battling many trainers.", + 3: "Oh, you caught me strategizing.\nWould you like to battle?" + }, + "victory": { + 1: "Oh, I appear to have lost.\nI understand.", + 2: "It seems that I still have so much more to learn when it comes to battle.", + 3: "I'll take what I learned here today to heart." + }, + "defeat": { + 1: "I have learned many things from our battle.\nI hope you have too.", + 2: "I look forward to battling you again.\nI hope you'll use what you've learned here.", + 3: "I won due to everything I have learned." + } + }, + "brawly": { + "encounter": { + 1: "Oh man, a challenger!\nLet's see what you can do!", + 2: "You seem like a big splash.\nLet's battle!", + 3: "Time to create a storm!\nLet's go!" + }, + "victory": { + 1: "Oh woah, you've washed me out!", + 2: "You surfed my wave and crashed me down!", + 3: "I feel like I'm lost in Granite Cave!" + }, + "defeat": { + 1: "Haha, I surfed the big wave!\nChallenge me again sometime.", + 2: "Surf with me again some time!", + 3: "Just like the tides come in and out, I hope you return to challenge me again." + } + }, + "wattson": { + "encounter": { + 1: "Time to get shocked!\nWahahahaha!", + 2: "I'll make sparks fly!\nWahahahaha!", + 3: "I hope you brought Paralyz Heal!\nWahahahaha!" + }, + "victory": { + 1: "Seems like I'm out of charge!\nWahahahaha!", + 2: "You've completely grounded me!\nWahahahaha!", + 3: "Thanks for the thrill!\nWahahahaha!" + }, + "defeat": { + 1: "Recharge your batteries and challenge me again sometime!\nWahahahaha!", + 2: "I hope you found our battle electrifying!\nWahahahaha!", + 3: "Aren't you shocked I won?\nWahahahaha!" + } + }, + "flannery": { + "encounter": { + 1: "Nice to meet you! Wait, no…\nI will crush you!", + 2: "I've only been a leader for a little while, but I'll smoke you!", + 3: "It's time to demonstrate the moves my grandfather has taught me! Let's battle!" + }, + "victory": { + 1: "You remind me of my grandfather…\nNo wonder I lost.", + 2: "Am I trying too hard?\nI should relax, can't get too heated.", + 3: "Losing isn't going to smother me out.\nTime to reignite training!" + }, + "defeat": { + 1: "I hope I've made my grandfather proud…\nLet's battle again some time.", + 2: "I…I can't believe I won!\nDoing things my way worked!", + 3: "Let's exchange burning hot moves again soon!" + } + }, + "norman": { + "encounter": { + 1: "I'm surprised you managed to get here.\nLet's battle.", + 2: "I'll do everything in my power as a Gym Leader to win.\nLet's go!", + 3: "You better give this your all.\nIt's time to battle!" + }, + "victory": { + 1: "I lost to you…?\nRules are rules, though.", + 2: "Was moving from Olivine a mistake…?", + 3: "I can't believe it.\nThat was a great match." + }, + "defeat": { + 1: "We both tried our best.\nI hope we can battle again soon.", + 2: "You should try challenging my kid instead.\nYou might learn something!", + 3: "Thank you for the excellent battle.\nBetter luck next time." + } + }, + "winona": { + "encounter": { + 1: "I've been soaring the skies looking for prey…\nAnd you're my target!", + 2: "No matter how our battle is, my Flying Pokémon and I will triumph with grace. Let's battle!", + 3: "I hope you aren't scared of heights.\nLet's ascend!" + }, + "victory": { + 1: "You're the first Trainer I've seen with more grace than I.\nExcellently played.", + 2: "Oh, my Flying Pokémon have plummeted!\nVery well.", + 3: "Though I may have fallen, my Pokémon will continue to fly!" + }, + "defeat": { + 1: "My Flying Pokémon and I will forever dance elegantly!", + 2: "I hope you enjoyed our show.\nOur graceful dance is finished.", + 3: "Won't you come see our elegant choreography again?" + } + }, + "tate": { + "encounter": { + 1: "Hehehe…\nWere you surprised to see me without my sister?", + 2: "I can see what you're thinking…\nYou want to battle!", + 3: "How can you defeat someone…\nWho knows your every move?" + }, + "victory": { + 1: "It can't be helped…\nI miss Liza…", + 2: "Your bond with your Pokémon was stronger than mine.", + 3: "If I were with Liza, we would have won.\nWe can finish each other's thoughts!" + }, + "defeat": { + 1: "My Pokémon and I are superior!", + 2: "If you can't even defeat me, you'll never be able to defeat Liza either.", + 3: "It's all thanks to my strict training with Liza.\nI can make myself one with Pokémon." + } + }, + "liza": { + "encounter": { + 1: "Fufufu…\nWere you surprised to see me without my brother?", + 2: "I can determine what you desire…\nYou want to battle, don't you?", + 3: "How can you defeat someone…\nWho's one with their Pokémon?" + }, + "victory": { + 1: "It can't be helped…\nI miss Tate…", + 2: "Your bond with your Pokémon…\nIt's stronger than mine.", + 3: "If I were with Tate, we would have won.\nWe can finish each other's sentences!" + }, + "defeat": { + 1: "My Pokémon and I are victorious.", + 2: "If you can't even defeat me, you'll never be able to defeat Tate either.", + 3: "It's all thanks to my strict training with Tate.\nI can synchronize myself with my Pokémon." + } + }, + "juan": { + "encounter": { + 1: "Now's not the time to act coy.\nLet's battle!", + 2: "Ahahaha, You'll be witness to my artistry with Water Pokémon!", + 3: "A typhoon approaches!\nWill you be able to test me?", + 4: "Please, you shall bear witness to our artistry.\nA grand illusion of water sculpted by my Pokémon and myself!" + }, + "victory": { + 1: "You may be a genius who can take on Wallace!", + 2: "I focused on elegance while you trained.\nIt's only natural that you defeated me.", + 3: "Ahahaha!\nVery well, You have won this time.", + 4: "From you, I sense the brilliant shine of skill that will overcome all." + }, + "defeat": { + 1: "My Pokémon and I have sculpted an illusion of Water and come out victorious.", + 2: "Ahahaha, I have won, and you have lost.", + 3: "Shall I loan you my outfit? It may help you battle!\nAhahaha, I jest!", + 4: "I'm the winner! Which is to say, you lost." + } + }, + "crasher_wake": { + "encounter": { + 1: "Crash! Crash! Watch out!\nCrasher Wake…is…heeere!", + 2: "Crash! Crash! Crasher Wake!", + 3: "I'm the tidal wave of power to wash you away!" + }, + "victory": { + 1: "That puts a grin on my face!\nGuhahaha! That was a blast!", + 2: "Hunwah! It's gone and ended!\nHow will I say this…\nI want more! I wanted to battle a lot more!", + 3: "WHAAAAT!?" + }, + "defeat": { + 1: "Yeeeeah! That's right!", + 2: "I won, but I want more! I wanted to battle a lot more!", + 3: "So long!" + } + }, + "falkner": { + "encounter": { + 1: "I'll show you the real power of the magnificent bird Pokémon!", + 2: "Winds, stay with me!", + 3: "Dad! I hope you're watching me battle from above!" + }, + "victory": { + 1: "I understand… I'll bow out gracefully.", + 2: "A defeat is a defeat. You are strong indeed.", + 3: "…Shoot! Yeah, I lost." + }, + "defeat": { + 1: "Dad! I won with your cherished bird Pokémon…", + 2: "Bird Pokémon are the best after all!", + 3: "Feels like I'm catching up to my dad!" + } + }, + "nessa": { + "encounter": { + 1: "No matter what kind of plan your refined mind may be plotting, my partner and I will be sure to sink it.", + 2: "I'm not here to chat. I'm here to win!", + 3: "This is a little gift from my Pokémon… I hope you can take it!" + }, + "victory": { + 1: "You and your Pokémon are just too much…", + 2: "How…? How can this be?!", + 3: "I was totally washed away!" + }, + "defeat": { + 1: "The raging wave crashes again!", + 2: "Time to ride the wave of victory!", + 3: "Ehehe!" + } + }, + "melony": { + "encounter": { + 1: "I'm not going to hold back!", + 2: "All righty, I suppose we should get started.", + 3: "I'll freeze you solid!" + }, + "victory": { + 1: "You… You're pretty good, huh?", + 2: "If you find Gordie around, be sure to give him a right trashing, would you?", + 3: "I think you took breaking the ice a little too literally…" + }, + "defeat": { + 1: "Now do you see how severe battles can be?", + 2: "Hee! Looks like I went and won again!", + 3: "Are you holding back?" + } + }, + "marlon": { + "encounter": { + 1: "You look strong! Shoots! Let's start!", + 2: "I'm strong like the ocean's wide. You're gonna get swept away, fo' sho'.", + 3: "Oh ho, so I'm facing you! That's off the wall." + }, + "victory": { + 1: "You totally rocked that! You're raising some wicked Pokémon. You got this Trainer thing down!", + 2: "You don't just look strong, you're strong fo' reals! Eh, I was swept away, too!", + 3: "You're strong as a gnarly wave!" + }, + "defeat": { + 1: "You're tough, but it's not enough to sway the sea, 'K!", + 2: "Hee! Looks like I went and won again!", + 3: "Sweet, sweet victory!" + } + }, + "shauntal": { + "encounter": { + 1: "Excuse me. You're a challenger, right?\nI'm the Elite Four's Ghost-type Pokémon user, Shauntal, and I shall be your opponent.", + 2: "I absolutely love writing about Trainers who come here and the Pokémon they train.\nCould I use you and your Pokémon as a subject?", + 3: "Every person who works with Pokémon has a story to tell.\nWhat story is about to be told?" + }, + "victory": { + 1: "Wow. I'm dumbstruck!", + 2: "S-sorry! First, I must apologize to my Pokémon…\n\nI'm really sorry you had a bad experience because of me!", + 3: "Even in light of that, I'm still one of the Elite Four!" + }, + "defeat": { + 1: "Eheh.", + 2: "That gave me excellent material for my next novel!", + 3: "And so, another tale ends…" + } + }, + "marshal": { + "encounter": { + 1: "My mentor, Alder, sees your potential as a Trainer and is taking an interest in you.\nIt is my intention to test you--to take you to the limits of your strength. Kiai!", + 2: "Victory, decisive victory, is my intention! Challenger, here I come!", + 3: "In myself, I seek to develop the strength of a fighter and shatter any weakness in myself!\nPrevailing with the force of my convictions!" + }, + "victory": { + 1: "Whew! Well done!", + 2: "As your battles continue, aim for even greater heights!", + 3: "The strength shown by you and your Pokémon has deeply impressed me…" + }, + "defeat": { + 1: "Hmm.", + 2: "That was good battle.", + 3: "Haaah! Haaah! Haiyaaaah!" + } + }, + "cheren": { + "encounter": { + 1: "You remind me of an old friend. That makes me excited about this Pokémon battle!", + 2: `Pokémon battles have no meaning if you don't think why you battle. + $Or better said, it makes battling together with Pokémon meaningless.`, + 3: "My name's Cheren! I'm a Gym Leader and a teacher! Pleasure to meet you." + }, + "victory": { + 1: "Thank you! I saw what was missing in me.", + 2: "Thank you! I feel like I saw a little of the way toward my ideals.", + 3: "Hmm… This is problematic." + }, + "defeat": { + 1: "As a Gym Leader, I aim to be a wall for you to overcome.", + 2: "All right!", + 3: "I made it where I am because Pokémon were by my side.\nPerhaps we need to think about why Pokémon help us not in terms of Pokémon and Trainers but as a relationship between living beings." + } + }, + "chili": { + "encounter": { + 1: "Yeeeeooow! Time to play with FIRE!! I'm the strongest of us brothers!", + 2: "Ta-da! The Fire-type scorcher Chili--that's me--will be your opponent!", + 3: "I'm going to show you what me and my blazing Fire types can do!" + }, + "victory": { + 1: "You got me. I am… burned… out…", + 2: "Whoa ho! You're on fire!", + 3: "Augh! You got me!" + }, + "defeat": { + 1: "I'm on fire! Play with me, and you'll get burned!", + 2: "When you play with fire, you get burned!", + 3: "I mean, c'mon, your opponent was me! You didn't have a chance!" + } + }, + "cilan": { + "encounter": { + 1: `Nothing personal... No hard feelings... Me and my Grass-type Pokémon will... + $Um... We're gonna battle come what may.`, + 2: "So, um, if you're OK with me, I'll, um, put everything I've got into being, er, you know, your opponent.", + 3: "OK… So, um, I'm Cilan, I like Grass-type Pokémon." + }, + "victory": { + 1: "Er… Is it over now?", + 2: `…What a surprise. You are very strong, aren't you? + $I guess my brothers wouldn't have been able to defeat you either…`, + 3: "…Huh. Looks like my timing was, um, off?" + }, + "defeat": { + 1: "Huh? Did I win?", + 2: `I guess… + $I suppose I won, because I've been competing with my brothers Chili and Cress, and we all were able to get tougher.`, + 3: "It…it was quite a thrilling experience…" + } + }, + "roark": { + "encounter": { + 1: "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!", + 2: "Here goes! These are my rocking Pokémon, my pride and joy!", + 3: "Rock-type Pokémon are simply the best!", + 4: "I need to see your potential as a Trainer. And, I'll need to see the toughness of the Pokémon that battle with you!" + }, + "victory": { + 1: "W-what? That can't be! My buffed-up Pokémon!", + 2: "…We lost control there. Next time I'd like to challenge you to a Fossil-digging race underground.", + 3: "With skill like yours, it's natural for you to win.", + 4: "Wh-what?! It can't be! Even that wasn't enough?", + 5: "I blew it." + }, + "defeat": { + 1: "See? I'm proud of my rocking battle style!", + 2: "Thanks! The battle gave me confidence that I may be able to beat my dad!", + 3: "I feel like I just smashed through a really stubborn boulder!" + } + }, + "morty": { + "encounter": { + 1: `With a little more, I could see a future in which I meet the legendary Pokémon. + $You're going to help me reach that level!`, + 2: `It's said that a rainbow-hued Pokémon will come down to appear before a truly powerful Trainer. + $I believed that tale, so I have secretly trained here all my life. As a result, I can now see what others cannot. + $I see a shadow of the person who will make the Pokémon appear. + $I believe that person is me! You're going to help me reach that level!`, + 3: "Whether you choose to believe or not, mystic power does exist.", + 4: "You can bear witness to the fruits of my training.", + 5: "You must make your soul one with that of Pokémon. Can you do this?", + 6: "Say, do you want to be part of my training?" + }, + "victory": { + 1: "I'm not good enough yet…", + 2: `I see… Your journey has taken you to far-away places and you have witnessed much more than I. + $I envy you for that…`, + 3: "How is this possible…", + 4: `I don't think our potentials are so different. + $But you seem to have something more than that… So be it.`, + 5: "Guess I need more training.", + 6: "That's a shame." + }, + "defeat": { + 1: "I moved… one step ahead again.", + 2: "Fufufu…", + 3: "Wh-what?! It can't be! Even that wasn't enough?", + 4: "I feel like I just smashed through a really stubborn boulder!", + 5: "Ahahahah!", + 6: "I knew I would win!" + } + }, + "crispin": { + "encounter": { + 1: "I wanna win, so that's exactly what I'll do!", + 2: "I battle because I wanna battle! And you know what? That's how it should be!" + }, + "victory": { + 1: "I wanted to win…but I lost!", + 2: "I lost…'cause I couldn't win!" + }, + "defeat": { + 1: "Hey, wait a sec. Did I just win? I think I just won! Talk about satisfying!", + 2: "Wooo! That was amazing!" + } + }, + "amarys": { + "encounter": { + 1: `I want to be the one to help a certain person. That being the case, I cannot afford to lose. + $… Our battle starts now.`, + }, + "victory": { + 1: "I am… not enough, I see." + }, + "defeat": { + 1: "Victory belongs to me. Well fought." + } + }, + "lacey": { + "encounter": { + 1: "I'll be facing you with my usual party as a member of the Elite Four." + }, + "victory": { + 1: "That was a great battle!" + }, + "defeat": { + 1: "Let's give your Pokémon a nice round of applause for their efforts!" + } + }, + "drayton": { + "encounter": { + 1: `Man, I love chairs. Don't you love chairs? What lifesavers. + $I don't get why everyone doesn't just sit all the time. Standing up's tiring work!`, + }, + "victory": { + 1: "Guess I should've expected that!" + }, + "defeat": { + 1: "Heh heh! Don't mind me, just scooping up a W over here. I get it if you're upset, but don't go full Kieran on me, OK?" + } + }, + "ramos": { + "encounter": { + 1: `Did yeh enjoy the garden playground I made with all these sturdy plants o' mine? + $Their strength is a sign o' my strength as a gardener and a Gym Leader! Yeh sure yer up to facing all that?`, + }, + "victory": { + 1: "Yeh believe in yer Pokémon… And they believe in yeh, too… It was a fine battle, sprout." + }, + "defeat": { + 1: "Hohoho… Indeed. Frail little blades o' grass'll break through even concrete." + } + }, + "viola": { + "encounter": { + 1: `Whether it's the tears of frustration that follow a loss or the blossoming of joy that comes with victory… + $They're both great subjects for my camera! Fantastic! This'll be just fantastic! + $Now come at me!`, + 2: "My lens is always focused on victory--I won't let anything ruin this shot!" + }, + "victory": { + 1: "You and your Pokémon have shown me a whole new depth of field! Fantastic! Just fantastic!", + 2: `The world you see through a lens, and the world you see with a Pokémon by your side… + $The same world can look entirely different depending on your view.` + }, + "defeat": { + 1: "The photo from the moment of my victory will be a real winner, all right!", + 2: "Yes! I took some great photos!" + } + }, + "candice": { + "encounter": { + 1: `You want to challenge Candice? Sure thing! I was waiting for someone tough! + $But I should tell you, I'm tough because I know how to focus.`, + 2: `Pokémon, fashion, romance… It's all about focus! + $I'll show you just what I mean. Get ready to lose!` + }, + "victory": { + 1: "I must say, I'm warmed up to you! I might even admire you a little.", + 2: `Wow! You're great! You've earned my respect! + $I think your focus and will bowled us over totally. ` + }, + "defeat": { + 1: "I sensed your will to win, but I don't lose!", + 2: "See? Candice's focus! My Pokémon's focus is great, too!" + } + }, + "gardenia": { + "encounter": { + 1: "You have a winning aura about you. So, anyway, this will be fun. Let's have our battle!" + }, + "victory": { + 1: "Amazing! You're very good, aren't you?" + }, + "defeat": { + 1: "Yes! My Pokémon and I are perfectly good!" + } + }, + "aaron": { + "encounter": { + 1: "Ok! Let me take you on!" + }, + "victory": { + 1: "Battling is a deep and complex affair…" + }, + "defeat": { + 1: "Victory over an Elite Four member doesn't come easily." + } + }, + "cress": { + "encounter": { + 1: "That is correct! It shall be I and my esteemed Water types that you must face in battle!" + }, + "victory": { + 1: "Lose? Me? I don't believe this." + }, + "defeat": { + 1: "This is the appropriate result when I'm your opponent." + } + }, + "allister": { + "encounter": { + 1: "'M Allister.\nH-here… I go…" + }, + "victory": { + 1: `I nearly lost my mask from the shock… That was… + $Wow. I can see your skill for what it is.`, + }, + "defeat": { + 1: "Th-that was ace!" + } + }, + "clay": { + "encounter": { + 1: "Harrumph! Kept me waitin', didn't ya, kid? All right, time to see what ya can do!" + }, + "victory": { + 1: "Man oh man… It feels good to go all out and still be defeated!" + }, + "defeat": { + 1: `What's important is how ya react to losin'. + $That's why folks who use losin' as fuel to get better are tough.`, + } + }, + "kofu": { + "encounter": { + 1: "I'mma serve you a full course o' Water-type Pokémon! Don't try to eat 'em, though!" + }, + "victory": { + 1: "Vaultin' Veluza! Yer a lively one, aren't ya! A little TOO lively, if I do say so myself!" + }, + "defeat": { + 1: "You come back to see me again now, ya hear?" + } + }, + "tulip": { + "encounter": { + 1: "Allow me to put my skills to use to make your cute little Pokémon even more beautiful!" + }, + "victory": { + 1: "Your strength has a magic to it that cannot be washed away." + }, + "defeat": { + 1: "You know, in my line of work, people who lack talent in one area or the other often fade away quickly—never to be heard of again." + } + }, + "sidney": { + "encounter": { + 1: `I like that look you're giving me. I guess you'll give me a good match. + $That's good! Looking real good! All right! + $You and me, let's enjoy a battle that can only be staged here!`, + }, + "victory": { + 1: "Well, how do you like that? I lost! Eh, it was fun, so it doesn't matter." + }, + "defeat": { + 1: "No hard feelings, alright?" + } + }, + "phoebe": { + "encounter": { + 1: `While I trained, I gained the ability to commune with Ghost-type Pokémon. + $Yes, the bond I developed with Pokémon is extremely tight. + $So, come on, just try and see if you can even inflict damage on my Pokémon!`, + }, + "victory": { + 1: "Oh, darn. I've gone and lost." + }, + "defeat": { + 1: "I look forward to battling you again sometime!" + } + }, + "glacia": { + "encounter": { + 1: `All I have seen are challenges by weak Trainers and their Pokémon. + $What about you? It would please me to no end if I could go all out against you!`, + }, + "victory": { + 1: `You and your Pokémon… How hot your spirits burn! + $The all-consuming heat overwhelms. + $It's no surprise that my icy skills failed to harm you.`, + }, + "defeat": { + 1: "A fiercely passionate battle, indeed." + } + }, + "drake": { + "encounter": { + 1: `For us to battle with Pokémon as partners, do you know what it takes? Do you know what is needed? + $If you don't, then you will never prevail over me!`, + }, + "victory": { + 1: "Superb, it should be said." + }, + "defeat": { + 1: "I gave my all for that battle!" + } + }, + "wallace": { + "encounter": { + 1: `There's something about you… A difference in your demeanor. + $I think I sense that in you. Now, show me. Show me the power you wield with your Pokémon. + $And I, in turn, shall present you with a performance of illusions in water by me and my Pokémon!`, + }, + "victory": { + 1: `Bravo. I realize now your authenticity and magnificence as a Pokémon Trainer. + $I find much joy in having met you and your Pokémon. You have proven yourself worthy.`, + }, + "defeat": { + 1: "A grand illusion!" + } + }, + "lorelei": { + "encounter": { + 1: `No one can best me when it comes to icy Pokémon! Freezing moves are powerful! + $Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?`, + }, + "victory": { + 1: "How dare you!" + }, + "defeat": { + 1: "There's nothing you can do once you're frozen." + } + }, + "will": { + "encounter": { + 1: `I have trained all around the world, making my psychic Pokémon powerful. + $I can only keep getting better! Losing is not an option!`, + }, + "victory": { + 1: "I… I can't… believe it…" + }, + "defeat": { + 1: "That was close. I wonder what it is that you lack." + } + }, + "malva": { + "encounter": { + 1: `I feel like my heart might just burst into flames. + $I'm burning up with my hatred for you, runt!`, + }, + "victory": { + 1: "What news… So a new challenger has defeated Malva!" + }, + "defeat": { + 1: "I am delighted! Yes, delighted that I could squash you beneath my heel." + } + }, + "hala": { + "encounter": { + 1: "Old Hala is here to make you holler!" + }, + "victory": { + 1: "I could feel the power you gained on your journey." + }, + "defeat": { + 1: "Haha! What a delightful battle!" + } + }, + "molayne": { + "encounter": { + 1: `I gave the captain position to my cousin Sophocles, but I'm confident in my ability. + $My strength is like that of a supernova!`, + }, + "victory": { + 1: "I certainly found an interesting Trainer to face!" + }, + "defeat": { + 1: "Ahaha. What an interesting battle." + } + }, + "rika": { + "encounter": { + 1: "I'd say I'll go easy on you, but… I'd be lying! Think fast!" + }, + "victory": { + 1: "Not bad, kiddo." + }, + "defeat": { + 1: "Nahahaha! You really are something else, kiddo!" + } + }, + "bruno": { + "encounter": { + 1: "We will grind you down with our superior power! Hoo hah!" + }, + "victory": { + 1: "Why? How could I lose?" + }, + "defeat": { + 1: "You can challenge me all you like, but the results will never change!" + } + }, + "bugsy": { + "encounter": { + 1: "I'm Bugsy! I never lose when it comes to bug Pokémon!" + }, + "victory": { + 1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win." + }, + "defeat": { + 1: "Thanks! Thanks to our battle, I was also able to make progress in my research!" + } + }, + "koga": { + "encounter": { + 1: "Fwahahahaha! Pokémon are not merely about brute force--you shall see soon enough!" + }, + "victory": { + 1: "Ah! You've proven your worth!" + }, + "defeat": { + 1: "Have you learned to fear the techniques of the ninja?" + } + }, + "bertha": { + "encounter": { + 1: "Well, would you show this old lady how much you've learned?" + }, + "victory": { + 1: `Well! Dear child, I must say, that was most impressive. + $Your Pokémon believed in you and did their best to earn you the win. + $Even though I've lost, I find myself with this silly grin!`, + }, + "defeat": { + 1: "Hahahahah! Looks like this old lady won!" + } + }, + "lenora": { + "encounter": { + 1: "Well then, challenger, I'm going to research how you battle with the Pokémon you've so lovingly raised!" + }, + "victory": { + 1: "My theory about you was correct. You're more than just talented… You're motivated! I salute you!" + }, + "defeat": { + 1: "Ah ha ha! If you lose, make sure to analyze why, and use that knowledge in your next battle!" + } + }, + "siebold": { + "encounter": { + 1: "As long as I am alive, I shall strive onward to seek the ultimate cuisine... and the strongest opponents in battle!" + }, + "victory": { + 1: "I shall store my memory of you and your Pokémon forever away within my heart." + }, + "defeat": { + 1: `Our Pokémon battle was like food for my soul. It shall keep me going. + $That is how I will pay my respects to you for giving your all in battle!`, + } + }, + "roxie": { + "encounter": { + 1: "Get ready! I'm gonna knock some sense outta ya!" + }, + "victory": { + 1: "Wild! Your reason's already more toxic than mine!" + }, + "defeat": { + 1: "Hey, c'mon! Get serious! You gotta put more out there!" + } + }, + "olivia": { + "encounter": { + 1: "No introduction needed here. Time to battle me, Olivia!" + }, + "victory": { + 1: "Really lovely… Both you and your Pokémon…" + }, + "defeat": { + 1: "Mmm-hmm." + } + }, + "poppy": { + "encounter": { + 1: "Oooh! Do you wanna have a Pokémon battle with me?" + }, + "victory": { + 1: "Uagh?! Mmmuuuggghhh…" + }, + "defeat": { + 1: `Yaaay! I did it! I de-feet-ed you! You can come for… For… An avenge match? + $Come for an avenge match anytime you want!`, + } + }, + "agatha": { + "encounter": { + 1: "Pokémon are for battling! I'll show you how a real Trainer battles!" + }, + "victory": { + 1: "Oh my! You're something special, child!" + }, + "defeat": { + 1: "Bahaha. That's how a proper battle's done!" + } + }, + "flint": { + "encounter": { + 1: "Hope you're warmed up, cause here comes the Big Bang!" + }, + "victory": { + 1: "Incredible! Your moves are so hot, they make mine look lukewarm!" + }, + "defeat": { + 1: "Huh? Is that it? I think you need a bit more passion." + } + }, + "grimsley": { + "encounter": { + 1: "The winner takes everything, and there's nothing left for the loser." + }, + "victory": { + 1: "When one loses, they lose everything… The next thing I'll look for will be victory, too!" + }, + "defeat": { + 1: "If somebody wins, the person who fought against that person will lose." + } + }, + "caitlin": { + "encounter": { + 1: `It's me who appeared when the flower opened up. You who have been waiting… + $You look like a Pokémon Trainer with refined strength and deepened kindness. + $What I look for in my opponent is superb strength… + $Please unleash your power to the fullest!`, + }, + "victory": { + 1: "My Pokémon and I learned so much! I offer you my thanks." + }, + "defeat": { + 1: "I aspire to claim victory with elegance and grace." + } + }, + "diantha": { + "encounter": { + 1: `Battling against you and your Pokémon, all of you brimming with hope for the future… + $Honestly, it just fills me up with energy I need to keep facing each new day! It does!`, + }, + "victory": { + 1: "Witnessing the noble spirits of you and your Pokémon in battle has really touched my heart…" + }, + "defeat": { + 1: "Oh, fantastic! What did you think? My team was pretty cool, right?" + } + }, + "wikstrom": { + "encounter": { + 1: `Well met, young challenger! Verily am I the famed blade of hardened steel, Duke Wikstrom! + $Let the battle begin! En garde!`, + }, + "victory": { + 1: "Glorious! The trust that you share with your honorable Pokémon surpasses even mine!" + }, + "defeat": { + 1: `What manner of magic is this? My heart, it doth hammer ceaselessly in my breast! + $Winning against such a worthy opponent doth give my soul wings--thus do I soar!`, + } + }, + "acerola": { + "encounter": { + 1: "Battling is just plain fun! Come on, I can take you!" + }, + "victory": { + 1: "I'm… I'm speechless! How did you do it?!" + }, + "defeat": { + 1: "Ehaha! What an amazing victory!" + } + }, + "larry_elite": { + "encounter": { + 1: `Hello there… It's me, Larry. + $I serve as a member of the Elite Four too, yes… Unfortunately for me.`, + }, + "victory": { + 1: "Well, that took the wind from under our wings…" + }, + "defeat": { + 1: "It's time for a meeting with the boss." + } + }, + "lance": { + "encounter": { + 1: "I've been waiting for you. Allow me to test your skill.", + 2: "I thought that you would be able to get this far. Let's get this started." + }, + "victory": { + 1: "You got me. You are magnificent!", + 2: "I never expected another trainer to beat me… I'm surprised." + }, + "defeat": { + 1: "That was close. Want to try again?", + 2: "It's not that you are weak. Don't let it bother you." + } + }, + "karen": { + "encounter": { + 1: "I am Karen. Would you care for a showdown with my Dark-type Pokémon?", + 2: "I am unlike those you've already met.", + 3: "You've assembled a charming team. Our battle should be a good one." + }, + "victory": { + 1: "No! I can't win. How did you become so strong?", + 2: "I will not stray from my chosen path.", + 3: "The Champion is looking forward to meeting you." + }, + "defeat": { + 1: "That's about what I expected.", + 2: "Well, that was relatively entertaining.", + 3: "Come visit me anytime." + } + }, + "milo": { + "encounter": { + 1: `Sure seems like you understand Pokémon real well. + $This is gonna be a doozy of a battle! + $I'll have to Dynamax my Pokémon if I want to win!`, + }, + "victory": { + 1: "The power of Grass has wilted… What an incredible Challenger!" + }, + "defeat": { + 1: "This'll really leave you in shock and awe." + } + }, + "lucian": { + "encounter": { + 1: `Just a moment, please. The book I'm reading has nearly reached its thrilling climax… + $The hero has obtained a mystic sword and is about to face their final trial… Ah, never mind. + $Since you've made it this far, I'll put that aside and battle you. + $Let me see if you'll achieve as much glory as the hero of my book!` + }, + "victory": { + 1: "I see… It appears you've put me in checkmate." + }, + "defeat": { + 1: "I have a reputation to uphold." + } + }, + "drasna": { + "encounter": { + 1: `You must be a strong Trainer. Yes, quite strong indeed… + $That's just wonderful news! Facing opponents like you and your team will make my Pokémon grow like weeds!` + }, + "victory": { + 1: "Oh, dear me. That sure was a quick battle… I do hope you'll come back again sometime!" + }, + "defeat": { + 1: "How can this be?" + } + }, + "kahili": { + "encounter": { + 1: "So, here you are… Why don't we see who the winds favor today, you… Or me?" + }, + "victory": { + 1: "It's frustrating to me as a member of the Elite Four, but it seems your strength is the real deal." + }, + "defeat": { + 1: "That was an ace!" + } + }, + "hassel": { + "encounter": { + 1: "Prepare to learn firsthand how the fiery breath of ferocious battle feels!" + }, + "victory": { + 1: `Fortune smiled on me this time, but… + $Judging from how the match went, who knows if I will be so lucky next time.`, + }, + "defeat": { + 1: "That was an ace!" + } + }, + "blue": { + "encounter": { + 1: "You must be pretty good to get this far." + }, + "victory": { + 1: "I've only lost to him and now to you… Him? Hee, hee…" + }, + "defeat": { + 1: "See? My power is what got me here." + } + }, + "piers": { + "encounter": { + 1: "Get ready for a mosh pit with me and my party! Spikemuth, it's time to rock!" + }, + "victory": { + 1: "Me an' my team gave it our best. Let's meet up again for a battle some time…" + }, + "defeat": { + 1: "My throat's ragged from shoutin'… But 'at was an excitin' battle!" + } + }, + "red": { + "encounter": { + 1: "…!" + }, + "victory": { + 1: "…?" + }, + "defeat": { + 1: "…!" + } + }, + "jasmine": { + "encounter": { + 1: "Oh… Your Pokémon are impressive. I think I will enjoy this." + }, + "victory": { + 1: "You are truly strong. I'll have to try much harder, too." + }, + "defeat": { + 1: "I never expected to win." + } + }, + "lance_champion": { + "encounter": { + 1: "I am still the Champion. I won't hold anything back." + }, + "victory": { + 1: "This is the emergence of a new Champion." + }, + "defeat": { + 1: "I successfully defended my Championship." + } + }, + "steven": { + "encounter": { + 1: `Tell me… What have you seen on your journey with your Pokémon? + $What have you felt, meeting so many other Trainers out there? + $Traveling this rich land… Has it awoken something inside you? + $I want you to come at me with all that you've learned. + $My Pokémon and I will respond in turn with all that we know!`, + }, + "victory": { + 1: "So I, the Champion, fall in defeat…" + }, + "defeat": { + 1: "That was time well spent! Thank you!" + } + }, + "cynthia": { + "encounter": { + 1: "I, Cynthia, accept your challenge! There won't be any letup from me!" + }, + "victory": { + 1: "No matter how fun the battle is, it will always end sometime…" + }, + "defeat": { + 1: "Even if you lose, never lose your love of Pokémon." + } + }, + "iris": { + "encounter": { + 1: `Know what? I really look forward to having serious battles with strong Trainers! + $I mean, come on! The Trainers who make it here are Trainers who desire victory with every fiber of their being! + $And they are battling alongside Pokémon that have been through countless difficult battles! + $If I battle with people like that, not only will I get stronger, my Pokémon will, too! + $And we'll get to know each other even better! OK! Brace yourself! + $I'm Iris, the Pokémon League Champion, and I'm going to defeat you!`, + }, + "victory": { + 1: "Aghhhh… I did my best, but we lost…" + }, + "defeat": { + 1: "Yay! We won!" + } + }, + "hau": { + "encounter": { + 1: `I wonder if a Trainer battles differently depending on whether they're from a warm region or a cold region. + $Let's test it out!`, + }, + "victory": { + 1: "That was awesome! I think I kinda understand your vibe a little better now!" + }, + "defeat": { + 1: "Ma-an, that was some kinda battle!" + } + }, + "geeta": { + "encounter": { + 1: `I decided to throw my hat in the ring once more. + $Come now… Show me the fruits of your training.`, + }, + "victory": { + 1: "I eagerly await news of all your achievements!" + }, + "defeat": { + 1: "What's the matter? This isn't all, is it?" + } + }, + "nemona": { + "encounter": { + 1: "Yesss! I'm so psyched! Time for us to let loose!" + }, + "victory": { + 1: "Well, that stinks, but I still had fun! I'll getcha next time!" + }, + "defeat": { + 1: "Well, that was a great battle! Fruitful for sure." + } + }, + "leon": { + "encounter": { + 1: "We're gonna have an absolutely champion time!" + }, + "victory": { + 1: `My time as Champion is over… + $But what a champion time it's been! + $Thank you for the greatest battle I've ever had!`, + }, + "defeat": { + 1: "An absolute champion time, that was!" + } + }, + "whitney": { + "encounter": { + 1: "Hey! Don't you think Pokémon are, like, super cute?" + }, + "victory": { + 1: "Waaah! Waaah! You're so mean!" + }, + "defeat": { + 1: "And that's that!" + } + }, + "chuck": { + "encounter": { + 1: "Hah! You want to challenge me? Are you brave or just ignorant?" + }, + "victory": { + 1: "You're strong! Would you please make me your apprentice?" + }, + "defeat": { + 1: "There. Do you realize how much more powerful I am than you?" + } + }, + "katy": { + "encounter": { + 1: "Don't let your guard down unless you would like to find yourself knocked off your feet!" + }, + "victory": { + 1: "All of my sweet little Pokémon dropped like flies!" + }, + "defeat": { + 1: "Eat up, my cute little Vivillon!" + } + }, + "pryce": { + "encounter": { + 1: "Youth alone does not ensure victory! Experience is what counts." + }, + "victory": { + 1: "Outstanding! That was perfect. Try not to forget what you feel now." + }, + "defeat": { + 1: "Just as I envisioned." + } + }, + "clair": { + "encounter": { + 1: "Do you know who I am? And you still dare to challenge me?" + }, + "victory": { + 1: "I wonder how far you can get with your skill level. This should be fascinating." + }, + "defeat": { + 1: "That's that." + } + }, + "maylene": { + "encounter": { + 1: `I've come to challenge you now, and I won't hold anything back. + $Please prepare yourself for battle!`, + }, + "victory": { + 1: "I admit defeat…" + }, + "defeat": { + 1: "That was awesome." + } + }, + "fantina": { + "encounter": { + 1: `You shall challenge me, yes? But I shall win. + $That is what the Gym Leader of Hearthome does, non?`, + }, + "victory": { + 1: "You are so fantastically strong. I know why I have lost." + }, + "defeat": { + 1: "I am so, so, very happy!" + } + }, + "byron": { + "encounter": { + 1: `Trainer! You're young, just like my son, Roark. + $With more young Trainers taking charge, the future of Pokémon is bright! + $So, as a wall for young people, I'll take your challenge!`, + }, + "victory": { + 1: "Hmm! My sturdy Pokémon--defeated!" + }, + "defeat": { + 1: "Gwahahaha! How were my sturdy Pokémon?!" + } + }, + "olympia": { + "encounter": { + 1: "An ancient custom deciding one's destiny. The battle begins!" + }, + "victory": { + 1: "Create your own path. Let nothing get in your way. Your fate, your future." + }, + "defeat": { + 1: "Our path is clear now." + } + }, + "volkner": { + "encounter": { + 1: `Since you've come this far, you must be quite strong… + $I hope you're the Trainer who'll make me remember how fun it is to battle!`, + }, + "victory": { + 1: `You've got me beat… + $Your desire and the noble way your Pokémon battled for you… + $I even felt thrilled during our match. That was a very good battle.`, + }, + "defeat": { + 1: `It was not shocking at all… + $That is not what I wanted!`, + } + }, + "burgh": { + "encounter": { + 1: `M'hm… If I win this battle, I feel like I can draw a picture unlike any before it. + $OK! I can hear my battle muse loud and clear. Let's get straight to it!`, + 2: `Of course, I'm really proud of all of my Pokémon! + $Well now… Let's get right to it!` + }, + "victory": { + 1: "Is it over? Has my muse abandoned me?", + 2: "Hmm… It's over! You're incredible!" + }, + "defeat": { + 1: "Wow… It's beautiful somehow, isn't it…", + 2: `Sometimes I hear people say something was an ugly win. + $I think if you're trying your best, any win is beautiful.` + } + }, + "elesa": { + "encounter": { + 1: `C'est fini! When I'm certain of that, I feel an electric jolt run through my body! + $I want to feel the sensation, so now my beloved Pokémon are going to make your head spin!`, + }, + "victory": { + 1: "I meant to make your head spin, but you shocked me instead." + }, + "defeat": { + 1: "That was unsatisfying somehow… Will you give it your all next time?" + } + }, + "skyla": { + "encounter": { + 1: `It's finally time for a showdown! That means the Pokémon battle that decides who's at the top, right? + $I love being on the summit! 'Cause you can see forever and ever from high places! + $So, how about you and I have some fun?`, + }, + "victory": { + 1: "Being your opponent in battle is a new source of strength to me. Thank you!" + }, + "defeat": { + 1: "Win or lose, you always gain something from a battle, right?" + } + }, + "brycen": { + "encounter": { + 1: `There is also strength in being with other people and Pokémon. + $Receiving their support makes you stronger. I'll show you this power!`, + }, + "victory": { + 1: "The wonderful combination of you and your Pokémon! What a beautiful friendship!" + }, + "defeat": { + 1: "Extreme conditions really test you and train you!" + } + }, + "drayden": { + "encounter": { + 1: `What I want to find is a young Trainer who can show me a bright future. + $Let's battle with everything we have: your skill, my experience, and the love we've raised our Pokémon with!`, + }, + "victory": { + 1: "This intense feeling that floods me after a defeat… I don't know how to describe it." + }, + "defeat": { + 1: "Harrumph! I know your ability is greater than that!" + } + }, + "grant": { + "encounter": { + 1: `There is only one thing I wish for. + $That by surpassing one another, we find a way to even greater heights.`, + }, + "victory": { + 1: "You are a wall that I am unable to surmount!" + }, + "defeat": { + 1: `Do not give up. + $That is all there really is to it. + $The most important lessons in life are simple.`, + } + }, + "korrina": { + "encounter": { + 1: "Time for Lady Korrina's big appearance!" + }, + "victory": { + 1: "It's your very being that allows your Pokémon to evolve!" + }, + "defeat": { + 1: "What an explosive battle!" + } + }, + "clemont": { + "encounter": { + 1: "Oh! I'm glad that we got to meet!" + }, + "victory": { + 1: "Your passion for battle inspires me!" + }, + "defeat": { + 1: "Looks like my Trainer-Grow-Stronger Machine, Mach 2 is really working!" + } + }, + "valerie": { + "encounter": { + 1: `Oh, if it isn't a young Trainer… It is lovely to get to meet you like this. + $Then I suppose you have earned yourself the right to a battle, as a reward for your efforts. + $The elusive Fairy may appear frail as the breeze and delicate as a bloom, but it is strong.`, + }, + "victory": { + 1: "I hope that you will find things worth smiling about tomorrow…" + }, + "defeat": { + 1: "Oh goodness, what a pity…" + } + }, + "wulfric": { + "encounter": { + 1: `You know what? We all talk big about what you learn from battling and bonds and all that… + $But really, I just do it 'cause it's fun. + $Who cares about the grandstanding? Let's get to battling!`, + }, + "victory": { + 1: "Outstanding! I'm tough as an iceberg, but you smashed me through and through!" + }, + "defeat": { + 1: "Tussle with me and this is what happens!" + } + }, + "kabu": { + "encounter": { + 1: `Every Trainer and Pokémon trains hard in pursuit of victory. + $But that means your opponent is also working hard to win. + $In the end, the match is decided by which side is able to unleash their true potential.`, + }, + "victory": { + 1: "I'm glad I could battle you today!" + }, + "defeat": { + 1: "That's a great way for me to feel my own growth!" + } + }, + "bea": { + "encounter": { + 1: `Do you have an unshakable spirit that won't be moved, no matter how you are attacked? + $I think I'll just test that out, shall I?`, + }, + "victory": { + 1: "I felt the fighting spirit of your Pokémon as you led them in battle." + }, + "defeat": { + 1: "That was the best sort of match anyone could ever hope for." + } + }, + "opal": { + "encounter": { + 1: "Let me have a look at how you and your partner Pokémon behave!" + }, + "victory": { + 1: "Your pink is still lacking, but you're an excellent Trainer with excellent Pokémon." + }, + "defeat": { + 1: "Too bad for you, I guess." + } + }, + "bede": { + "encounter": { + 1: "I suppose I should prove beyond doubt just how pathetic you are and how strong I am." + }, + "victory": { + 1: "I see… Well, that's fine. I wasn't really trying all that hard anyway." + }, + "defeat": { + 1: "Not a bad job, I suppose." + } + }, + "gordie": { + "encounter": { + 1: "So, let's get this over with." + }, + "victory": { + 1: "I just want to climb into a hole… Well, I guess it'd be more like falling from here." + }, + "defeat": { + 1: "Battle like you always do, victory will follow!" + } + }, + "marnie": { + "encounter": { + 1: `The truth is, when all's said and done… I really just wanna become Champion for myself! + $So don't take it personal when I kick your butt!`, + }, + "victory": { + 1: "OK, so I lost… But I got to see a lot of the good points of you and your Pokémon!" + }, + "defeat": { + 1: "Hope you enjoyed our battle tactics." + } + }, + "raihan": { + "encounter": { + 1: "I'm going to defeat the Champion, win the whole tournament, and prove to the world just how strong the great Raihan really is!" + }, + "victory": { + 1: `I look this good even when I lose. + $It's a real curse. + $Guess it's time for another selfie!`, + }, + "defeat": { + 1: "Let's take a selfie to remember this." + } + }, + "brassius": { + "encounter": { + 1: "I assume you are ready? Let our collaborative work of art begin!" + }, + "victory": { + 1: "Ahhh…vant-garde!" + }, + "defeat": { + 1: "I will begin on a new piece at once!" + } + }, + "iono": { + "encounter": { + 1: `How're ya feelin' about this battle? + $... + $Let's get this show on the road! How strong is our challenger? + $I 'unno! Let's find out together!`, + }, + "victory": { + 1: "You're as flashy and bright as a 10,000,000-volt Thunderbolt, friendo!" + }, + "defeat": { + 1: "Your eyeballs are MINE!" + } + }, + "larry": { + "encounter": { + 1: "When all's said and done, simplicity is strongest." + }, + "victory": { + 1: "A serving of defeat, huh?" + }, + "defeat": { + 1: "I'll call it a day." + } + }, + "ryme": { + "encounter": { + 1: "Come on, baby! Rattle me down to the bone!" + }, + "victory": { + 1: "You're cool, my friend—you move my SOUL!" + }, + "defeat": { + 1: "Later, baby!" + } + }, + "grusha": { + "encounter": { + 1: "All I need to do is make sure the power of my Pokémon chills you to the bone!" + }, + "victory": { + 1: "Your burning passion… I kinda like it, to be honest." + }, + "defeat": { + 1: "Things didn't heat up for you." + } + }, + "marnie_elite": { + "encounter": { + 1: "You've made it this far, huh? Let's see if you can handle my Pokémon!", + 2: "I'll give it my best shot, but don't think I'll go easy on you!" + }, + "victory": { + 1: "I can't believe I lost... But you deserved that win. Well done!", + 2: "Looks like I've still got a lot to learn. Great battle, though!" + }, + "defeat": { + 1: "You put up a good fight, but I've got the edge! Better luck next time!", + 2: "Seems like my training's paid off. Thanks for the battle!" + } + }, + "nessa_elite": { + "encounter": { + 1: "The tides are turning in my favor. Ready to get swept away?", + 2: "Let's make some waves with this battle! I hope you're prepared!" + }, + "victory": { + 1: "You navigated those waters perfectly... Well done!", + 2: "Looks like my currents were no match for you. Great job!" + }, + "defeat": { + 1: "Water always finds a way. That was a refreshing battle!", + 2: "You fought well, but the ocean's power is unstoppable!" + } + }, + "bea_elite": { + "encounter": { + 1: "Prepare yourself! My fighting spirit burns bright!", + 2: "Let's see if you can keep up with my relentless pace!" + }, + "victory": { + 1: "Your strength... It's impressive. You truly deserve this win.", + 2: "I've never felt this intensity before. Amazing job!" + }, + "defeat": { + 1: "Another victory for my intense training regimen! Well done!", + 2: "You've got strength, but I trained harder. Great battle!" + } + }, + "allister_elite": { + "encounter": { + 1: "Shadows fall... Are you ready to face your fears?", + 2: "Let's see if you can handle the darkness that I command." + }, + "victory": { + 1: "You've dispelled the shadows... For now. Well done.", + 2: "Your light pierced through my darkness. Great job." + }, + "defeat": { + 1: "The shadows have spoken... Your strength isn't enough.", + 2: "Darkness triumphs... Maybe next time you'll see the light." + } + }, + "raihan_elite": { + "encounter": { + 1: "Storm's brewing! Let's see if you can weather this fight!", + 2: "Get ready to face the eye of the storm!" + }, + "victory": { + 1: "You've bested the storm... Incredible job!", + 2: "You rode the winds perfectly... Great battle!" + }, + "defeat": { + 1: "Another storm weathered, another victory claimed! Well fought!", + 2: "You got caught in my storm! Better luck next time!" + } + }, + "alder": { + "encounter": { + 1: "Prepare yourself for a match against the strongest Trainer in Unova!" + }, + "victory": { + 1: "Well done! You certainly are an unmatched talent." + }, + "defeat": { + 1: `A fresh wind blows through my heart... + $What an extraordinary effort!` + } + }, + "kieran": { + "encounter": { + 1: `Through hard work, I become stronger and stronger! + $I don't lose.` + }, + "victory": { + 1: `I don't believe it... + $What a fun and heart-pounding battle!` + }, + "defeat": { + 1: `Wowzers, what a battle! + $Time for you to train even harder.` + } + }, + "rival": { + "encounter": { + 1: `@c{smile}Hey, I was looking for you! I knew you were eager to get going but I expected at least a goodbye… + $@c{smile_eclosed}So you're really pursuing your dream after all?\n I almost can't believe it. + $@c{serious_smile_fists}Since we're here, how about a battle?\nAfter all, I want to make sure you're ready. + $@c{serious_mopen_fists}Don't hold back, I want you to give me everything you've got!` + }, + "victory": { + 1: `@c{shock}Wow… You cleaned me out.\nAre you actually a beginner? + $@c{smile}Maybe it was a bit of luck but…\nWho knows you might just be able to go all the way. + $By the way, the professor asked me to give you these items. They look pretty cool. + $@c{serious_smile_fists}Good luck out there!` + }, + }, + "rival_female": { + "encounter": { + 1: `@c{smile_wave}There you are! I've been looking everywhere for you!\n@c{angry_mopen}Did you forget to say goodbye to your best friend? + $@c{smile_ehalf}You're going after your dream, huh?\nThat day is really today isn't it… + $@c{smile}Anyway, I'll forgive you for forgetting me, but on one condition. @c{smile_wave_wink}You have to battle me! + $@c{angry_mopen}Give it your all! Wouldn't want your adventure to be over before it started, right?` + }, + "victory": { + 1: `@c{shock}You just started and you're already this strong?!@d{96}\n@c{angry}You totally cheated, didn't you? + $@c{smile_wave_wink}Just kidding!@d{64} @c{smile_eclosed}I lost fair and square… I have a feeling you're going to do really well out there. + $@c{smile}By the way, the professor wanted me to give you some items. Hopefully they're helpful! + $@c{smile_wave}Do your best like always! I believe in you!` + }, + }, + "rival_2": { + "encounter": { + 1: `@c{smile}Hey, you're here too?\n@c{smile_eclosed}Still a perfect record, huh…? + $@c{serious_mopen_fists}I know it kind of looks like I followed you here, but that's mostly not true. + $@c{serious_smile_fists}Honestly though, I've been itching for a rematch since you beat me back at home. + $I've been doing a lot of my own training so I'll definitely put up a fight this time. + $@c{serious_mopen_fists}Don't hold back, just like before!\nLet's go!` + }, + "victory": { + 1: `@c{neutral_eclosed}Oh. I guess I was overconfident. + $@c{smile}That's alright, though. I figured this might happen.\n@c{serious_mopen_fists}It just means I need to try harder for next time!\n + $@c{smile}Oh, not that you really need the help, but I had an extra one of these lying around and figured you might want it.\n + $@c{serious_smile_fists}Don't expect another one after this, though!\nI can't keep giving my opponent an advantage after all. + $@c{smile}Anyway, take care!` + }, + }, + "rival_2_female": { + "encounter": { + 1: `@c{smile_wave}Oh, fancy meeting you here. Looks like you're still undefeated. @c{angry_mopen}Huh… Not bad! + $@c{angry_mopen}I know what you're thinking, and no, I wasn't creeping on you. @c{smile_eclosed}I just happened to be in the area. + $@c{smile_ehalf}I'm happy for you but I just want to let you know that it's OK to lose sometimes. + $@c{smile}We learn from our mistakes, often more than we would if we kept succeeding. + $@c{angry_mopen}In any case, I've been training hard for our rematch, so you'd better give it your all!` + }, + "victory": { + 1: `@c{neutral}I… wasn't supposed to lose that time… + $@c{smile}Aw well. That just means I'll have to train even harder for next time! + $@c{smile_wave}I also got you another one of these!\n@c{smile_wave_wink}No need to thank me~. + $@c{angry_mopen}This is the last one, though! You won't be getting anymore freebies from me after this! + $@c{smile_wave}Keep at it!` + }, + "defeat": { + 1: "It's OK to lose sometimes…" + } + }, + "rival_3": { + "encounter": { + 1: `@c{smile}Hey, look who it is! It's been a while.\n@c{neutral}You're… still undefeated? Huh. + $@c{neutral_eclosed}Things have been kind of… strange.\nIt's not the same back home without you. + $@c{serious}I know it's selfish, but I need to get this off my chest.\n@c{neutral_eclosed}I think you're in over your head here. + $@c{serious}Never losing once is just unrealistic.\nWe need to lose sometimes in order to grow. + $@c{neutral_eclosed}You've had a great run but there's still so much ahead, and it only gets harder. @c{neutral}Are you prepared for that? + $@c{serious_mopen_fists}If so, prove it to me.` + }, + "victory": { + 1: "@c{angry_mhalf}This is ridiculous… I've hardly stopped training…\nHow are we still so far apart?" + }, + }, + "rival_3_female": { + "encounter": { + 1: `@c{smile_wave}Long time no see! Still haven't lost, huh.\n@c{angry}You're starting to get on my nerves. @c{smile_wave_wink}Just kidding! + $@c{smile_ehalf}But really, don't you miss home by now? Or… me?\nI… I mean, we've really missed you. + $@c{smile_eclosed}I support you in your dream and everything, but the reality is you're going to lose sooner or later. + $@c{smile}And when you do, I'll be there for you like always.\n@c{angry_mopen}Now, let me show you how strong I've become!` + }, + "victory": { + 1: "@c{shock}After all that… it wasn't enough…?\nYou'll never come back at this rate…" + + }, + "defeat": { + 1: "You gave it your best, now let's go home." + } + }, + "rival_4": { + "encounter": { + 1: `@c{neutral}Hey. + $I won't mince words or pleasantries with you.\n@c{neutral_eclosed}I'm here to win, plain and simple. + $@c{serious_mhalf_fists}I've learned to maximize my potential by putting all my time into training. + $@c{smile}You get a lot of extra time when you cut out the unnecessary sleep and social interaction. + $@c{serious_mopen_fists}None of that matters anymore, not until I win. + $@c{neutral_eclosed}I've even reached the point where I don't lose anymore.\n@c{smile_eclosed}I suppose your philosophy wasn't so wrong after all. + $@c{angry_mhalf}Losing is for the weak, and I'm not weak anymore. + $@c{serious_mopen_fists}Prepare yourself.` + }, + "victory": { + 1: "@c{neutral}What…@d{64} What are you?" + }, + }, + "rival_4_female": { + "encounter": { + 1: `@c{neutral}It's me! You didn't forget about me again… did you? + $@c{smile}You should be proud of how far you made it. Congrats!\nBut it looks like it's the end of your journey. + $@c{smile_eclosed}You've awoken something in me I never knew was there.\nIt seems like all I do now is train. + $@c{smile_ehalf}I hardly even eat or sleep now, I just train my Pokémon all day, getting stronger every time. + $@c{neutral}In fact, I… hardly recognize myself. + $And now, I've finally reached peak performance.\nI don't think anyone could beat me now. + $And you know what? It's all because of you.\n@c{smile_ehalf}I don't know whether to thank you or hate you. + $@c{angry_mopen}Prepare yourself.` + }, + "victory": { + 1: "@c{neutral}What…@d{64} What are you?" + + }, + "defeat": { + 1: "$@c{smile}You should be proud of how far you made it." + } + }, + "rival_5": { + "encounter": { + 1: "@c{neutral}…" + }, + "victory": { + 1: "@c{neutral}…" + }, + }, + "rival_5_female": { + "encounter": { + 1: "@c{neutral}…" + }, + "victory": { + 1: "@c{neutral}…" + + }, + "defeat": { + 1: "$@c{smile_ehalf}…" + } + }, + "rival_6": { + "encounter": { + 1: `@c{smile_eclosed}We meet again. + $@c{neutral}I've had some time to reflect on all this.\nThere's a reason this all seems so strange. + $@c{neutral_eclosed}Your dream, my drive to beat you…\nIt's all a part of something greater. + $@c{serious}This isn't about me, or about you… This is about the world, @c{serious_mhalf_fists}and it's my purpose to push you to your limits. + $@c{neutral_eclosed}Whether I've fulfilled that purpose I can't say, but I've done everything in my power. + $@c{neutral}This place we ended up in is terrifying… Yet somehow I feel unphased, like I've been here before. + $@c{serious_mhalf_fists}You feel the same, don't you? + $@c{serious}…and it's like something here is speaking to me.\nThis is all the world's known for a long time now. + $Those times we cherished together that seem so recent are nothing but a distant memory. + $@c{neutral_eclosed}Who can say whether they were ever even real in the first place. + $@c{serious_mopen_fists}You need to keep pushing, because if you don't, it will never end. You're the only one who can do this. + $@c{serious_smile_fists}I hardly know what any of this means, I just know that it's true. + $@c{serious_mopen_fists}If you can't defeat me here and now, you won't stand a chance.` + }, + "victory": { + 1: `@c{smile_eclosed}It looks like my work is done here. + $I want you to promise me one thing.\n@c{smile}After you heal the world, please come home.` + }, + }, + "rival_6_female": { + "encounter": { + 1: `@c{smile_ehalf}So it's just us again. + $@c{smile_eclosed}You know, I keep going around and around in my head… + $@c{smile_ehalf}There's something to all this, why everything seems so strange now… + $@c{smile}You have your dream, and I have this ambition in me… + $I just can't help but feel there's a greater purpose to all this, to what we're doing, you and I. + $@c{smile_eclosed}I think I'm supposed to push you… to your limits. + $@c{smile_ehalf}I'm not sure if I've been doing a good job at that, but I've tried my best up to now. + $It's something about this strange and dreadful place… Everything seems so clear… + $This… is all the world's known for a long time now. + $@c{smile_eclosed}It's like I can barely remember the memories we cherished together. + $@c{smile_ehalf}Were they even real? They seem so far away now… + $@c{angry_mopen}You need to keep pushing, because if you don't, it will never end. You're the only one who can do this. + $@c{smile_ehalf}I… don't know what all this means… but I feel it's true. + $@c{neutral}If you can't defeat me here and now, you won't stand a chance.` + }, + "victory": { + 1: `@c{smile_ehalf}I… I think I fulfilled my purpose… + $@c{smile_eclosed}Promise me… After you heal the world… Please… come home safe. + $@c{smile_ehalf}…Thank you.` + + }, + }, +}; + + +// Dialogue of the NPCs in the game when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMdialogue. +export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; + +// Dialogue of the endboss of the game when the player character is male (Or unset) +export const PGMbattleSpecDialogue: SimpleTranslationEntries = { + "encounter": `It appears the time has finally come once again.\nYou know why you have come here, do you not? + $You were drawn here, because you have been here before.\nCountless times. + $Though, perhaps it can be counted.\nTo be precise, this is in fact your 5,643,853rd cycle. + $Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain. + $Until now you have yet to succeed, but I sense a different presence in you this time.\n + $You are the only one here, though it is as if there is… another. + $Will you finally prove a formidable challenge to me?\nThe challenge I have longed for for millennia? + $We begin.`, + "firstStageWin": `I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back. + $Do not disappoint me.`, + "secondStageWin": "…Magnificent." +}; + +// Dialogue of the endboss of the game when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMbattleSpecDialogue. +export const PGFbattleSpecDialogue: SimpleTranslationEntries = PGMbattleSpecDialogue; + +// Dialogue that does not fit into any other category (e.g. tutorial messages, or the end of the game). For when the player character is male +export const PGMmiscDialogue: SimpleTranslationEntries = { + "ending": + `@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now. + $@c{smile}It's over.@d{64} You ended the loop. + $@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once. + $@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it? + $@c{serious_smile_fists}Your legend will always live on in our hearts. + $@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home. + $@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.`, + "ending_female": + `@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you. + $@c{smile_eclosed}Of course… I always had that feeling.\n@c{smile}It's over now, right? You ended the loop. + $@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once. + $I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget! + $@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts. + $@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place. + $Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?`, + "ending_endless": "Congratulations on reaching the current end!\nMore content is coming soon.", + "ending_name": "Devs" +}; +// Dialogue that does not fit into any other category (e.g. tutorial messages, or the end of the game). For when the player character is female. For languages that do not have gendered pronouns, this can be set to PGMmiscDialogue. +export const PGFmiscDialogue: SimpleTranslationEntries = PGMmiscDialogue; + + +// Dialogue of the named double battles in the game. For when the player is male (or unset). +export const PGMdoubleBattleDialogue: DialogueTranslationEntries = { + "blue_red_double": { + "encounter": { + 1: `Blue: Hey Red, let's show them what we're made of! + $Red: ... + $Blue: This is Pallet Town Power!`, + }, + "victory": { + 1: `Blue: That was a great battle! + $Red: ...`, + }, + }, + "red_blue_double": { + "encounter": { + 1: `Red: ...! + $Blue: He never talks much. + $Blue: But dont let that fool you! He is a champ after all!`, + }, + "victory": { + 1: `Red: ...! + $Blue: Next time we will beat you!`, + }, + }, + "tate_liza_double": { + "encounter": { + 1: `Tate: Are you surprised? + $Liza: We are two gym leaders at once! + $Tate: We are twins! + $Liza: We dont need to talk to understand each other! + $Tate: Twice the power... + $Liza: Can you handle it?`, + }, + "victory": { + 1: `Tate: What? Our combination was perfect! + $Liza: Looks like we need to train more...`, + }, + }, + "liza_tate_double": { + "encounter": { + 1: `Liza: Hihihi... Are you surprised? + $Tate: Yes, we are really two gym leaders at once! + $Liza: This is my twin brother Tate! + $Tate: And this is my twin sister Liza! + $Liza: Don't you think we are a perfect combination?` + }, + "victory": { + 1: `Liza: Are we... + $Tate: ...not as strong as we thought?`, + }, + }, + "wallace_steven_double": { + "encounter": { + 1: `Steven: Wallace, let's show them the power of the champions! + $Wallace: We will show you the power of Hoenn! + $Steven: Let's go!`, + }, + "victory": { + 1: `Steven: That was a great battle! + $Wallace: We will win next time!`, + }, + }, + "steven_wallace_double": { + "encounter": { + 1: `Steven: Do you have any rare Pokémon? + $Wallace: Steven... We are here for a battle, not to show off our Pokémon. + $Steven: Oh... I see... Let's go then!`, + }, + "victory": { + 1: `Steven: Now that we are done with the battle, let's show off our Pokémon! + $Wallace: Steven...`, + }, + }, + "alder_iris_double": { + "encounter": { + 1: `Alder: We are the strongest trainers in Unova! + $Iris: Fights against strong trainers are the best!`, + }, + "victory": { + 1: `Alder: Wow! You are super strong! + $Iris: We will win next time!`, + }, + }, + "iris_alder_double": { + "encounter": { + 1: `Iris: Welcome Challenger! I am THE Unova Champion! + $Alder: Iris, aren't you a bit too excited?`, + }, + "victory": { + 1: `Iris: A loss like this is not easy to take... + $Alder: But we will only get stronger with every loss!`, + }, + }, + "piers_marnie_double": { + "encounter": { + 1: `Marnie: Brother, let's show them the power of Spikemuth! + $Piers: We bring darkness!`, + }, + "victory": { + 1: `Marnie: You brought light to our darkness! + $Piers: Its too bright...`, + }, + }, + "marnie_piers_double": { + "encounter": { + 1: `Piers: Ready for a concert? + $Marnie: Brother... They are here to fight, not to sing...`, + }, + "victory": { + 1: `Piers: Now that was a great concert! + $Marnie: Brother...`, + }, + }, +}; + +// Dialogue of the named double battles in the game. For when the player is female. For languages that do not have gendered pronouns, this can be set to PGMdoubleBattleDialogue. +export const PGFdoubleBattleDialogue: DialogueTranslationEntries = PGMdoubleBattleDialogue; diff --git a/src/locales/ja/egg.ts b/src/locales/ja/egg.ts new file mode 100644 index 00000000000..962abfb133a --- /dev/null +++ b/src/locales/ja/egg.ts @@ -0,0 +1,28 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const egg: SimpleTranslationEntries = { + "egg": "タマゴ", + "greatTier": "レア", + "ultraTier": "超レア", + "masterTier": "伝説", + "defaultTier": "ふつう", + "hatchWavesMessageSoon": "なかから おとが きこえてくる! もうすぐ うまれそう!", + "hatchWavesMessageClose": "ときどき うごいている みたい。 うまれるまで もう ちょっとかな?", + "hatchWavesMessageNotClose": "なにが うまれてくるのかな? うまれるまで まだまだ じかんが かかりそう。", + "hatchWavesMessageLongTime": "この タマゴは うまれるまで かなり じかんが かかりそう。", + "gachaTypeLegendary": "伝説確率アップ", + "gachaTypeMove": "レアなタマゴわざ確率アップ", + "gachaTypeShiny": "色違い確率アップ", + "selectMachine": "ガチャマシンを選択", + "notEnoughVouchers": "タマゴクーポンが足りません!", + "tooManyEggs": "タマゴが一杯です!", + "pull": "回引く", + "pulls": "回引く", + "sameSpeciesEgg": "{{species}}は このタマゴから うまれる!", + "hatchFromTheEgg": "{{pokemonName}}は タマゴから うまれた!", + "eggMoveUnlock": "タマゴわざ {{moveName}}を おぼえた!", + "rareEggMoveUnlock": "レアなタマゴわざ {{moveName}}を おぼえた!!", + "moveUPGacha": "わざ UP!", + "shinyUPGacha": "色違い UP!", + "legendaryUPGacha": "UP!", +} as const; diff --git a/src/locales/ja/fight-ui-handler.ts b/src/locales/ja/fight-ui-handler.ts new file mode 100644 index 00000000000..f1f6bdb9575 --- /dev/null +++ b/src/locales/ja/fight-ui-handler.ts @@ -0,0 +1,9 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const fightUiHandler: SimpleTranslationEntries = { + "pp": "PP", + "power": "いりょく", + "accuracy": "めいちゅう", + "abilityFlyInText": " {{pokemonName}}の {{passive}}{{abilityName}}", + "passive": "Passive ", // The space at the end is important +} as const; diff --git a/src/locales/ja/filter-bar.ts b/src/locales/ja/filter-bar.ts new file mode 100644 index 00000000000..60c6ffb1bbc --- /dev/null +++ b/src/locales/ja/filter-bar.ts @@ -0,0 +1,21 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const filterBar: SimpleTranslationEntries = { + "genFilter": "Gen", + "typeFilter": "Type", + "unlocksFilter": "Unlocks", + "winFilter": "Win", + "sortFilter": "Sort", + "all": "All", + "normal": "Normal", + "uncaught": "Uncaught", + "passiveUnlocked": "Passive Unlocked", + "passiveLocked": "Passive Locked", + "hasWon": "Yes", + "hasNotWon": "No", + "sortByNumber": "No.", + "sortByCost": "Cost", + "sortByCandies": "Candy Count", + "sortByIVs": "IVs", + "sortByName": "Name", +}; diff --git a/src/locales/ja/game-mode.ts b/src/locales/ja/game-mode.ts new file mode 100644 index 00000000000..4d1e861ced7 --- /dev/null +++ b/src/locales/ja/game-mode.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const gameMode: SimpleTranslationEntries = { + "classic": "クラシック", + "endless": "エンドレス", + "endlessSpliced": "エンドレス (Spliced)", + "dailyRun": "デイリーラン", + "unknown": "Unknown", + "challenge": "チャレンジ", +} as const; diff --git a/src/locales/ja/game-stats-ui-handler.ts b/src/locales/ja/game-stats-ui-handler.ts new file mode 100644 index 00000000000..a29eaf5d1b6 --- /dev/null +++ b/src/locales/ja/game-stats-ui-handler.ts @@ -0,0 +1,44 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const gameStatsUiHandler: SimpleTranslationEntries = { + "stats": "Stats", + "playTime": "Play Time", + "totalBattles": "Total Battles", + "starters": "Starters", + "shinyStarters": "Shiny Starters", + "speciesSeen": "Species Seen", + "speciesCaught": "Species Caught", + "ribbonsOwned": "Ribbons Owned", + "classicRuns": "Classic Runs", + "classicWins": "Classic Wins", + "dailyRunAttempts": "Daily Run Attempts", + "dailyRunWins": "Daily Run Wins", + "endlessRuns": "Endless Runs", + "highestWaveEndless": "Highest Wave (Endless)", + "highestMoney": "Highest Money", + "highestDamage": "Highest Damage", + "highestHPHealed": "Highest HP Healed", + "pokemonEncountered": "Pokémon Encountered", + "pokemonDefeated": "Pokémon Defeated", + "pokemonCaught": "Pokémon Caught", + "eggsHatched": "Eggs Hatched", + "subLegendsSeen": "Sub-Legends Seen", + "subLegendsCaught": "Sub-Legends Caught", + "subLegendsHatched": "Sub-Legends Hatched", + "legendsSeen": "Legends Seen", + "legendsCaught": "Legends Caught", + "legendsHatched": "Legends Hatched", + "mythicalsSeen": "Mythicals Seen", + "mythicalsCaught": "Mythicals Caught", + "mythicalsHatched": "Mythicals Hatched", + "shiniesSeen": "Shinies Seen", + "shiniesCaught": "Shinies Caught", + "shiniesHatched": "Shinies Hatched", + "pokemonFused": "Pokémon Fused", + "trainersDefeated": "Trainers Defeated", + "eggsPulled": "Eggs Pulled", + "rareEggsPulled": "Rare Eggs Pulled", + "epicEggsPulled": "Epic Eggs Pulled", + "legendaryEggsPulled": "Legendary Eggs Pulled", + "manaphyEggsPulled": "Manaphy Eggs Pulled", +} as const; diff --git a/src/locales/ja/growth.ts b/src/locales/ja/growth.ts new file mode 100644 index 00000000000..fa79e093ef0 --- /dev/null +++ b/src/locales/ja/growth.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const growth: SimpleTranslationEntries = { + "Erratic": "60まんタイプ", + "Fast": "80まんタイプ", + "Medium_Fast": "100まんタイプ", + "Medium_Slow": "105まんタイプ", + "Slow": "125まんタイプ", + "Fluctuating": "164まんタイプ" +} as const; diff --git a/src/locales/ja/menu-ui-handler.ts b/src/locales/ja/menu-ui-handler.ts new file mode 100644 index 00000000000..b3dae131f7b --- /dev/null +++ b/src/locales/ja/menu-ui-handler.ts @@ -0,0 +1,28 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const menuUiHandler: SimpleTranslationEntries = { + "GAME_SETTINGS": "せってい", + "ACHIEVEMENTS": "じっせき", + "STATS": "とうけい", + "VOUCHERS": "クーポン", + "EGG_LIST": "タマゴリスト", + "EGG_GACHA": "タマゴガチャ", + "MANAGE_DATA": "データかんり", + "COMMUNITY": "コミュニティ", + "SAVE_AND_QUIT": "Save and Quit", + "LOG_OUT": "ログアウト", + "slot": "スロット {{slotNumber}}", + "importSession": "セッションのインポート", + "importSlotSelect": "Select a slot to import to.", + "exportSession": "セッションのエクスポート", + "exportSlotSelect": "Select a slot to export from.", + "importData": "データのインポート", + "exportData": "データのエクスポート", + "linkDiscord": "Link Discord", + "unlinkDiscord": "Unlink Discord", + "linkGoogle": "Link Google", + "unlinkGoogle": "Unlink Google", + "cancel": "キャンセル", + "losingProgressionWarning": "You will lose any progress since the beginning of the battle. Proceed?", + "noEggs": "You are not hatching\nany eggs at the moment!", +} as const; diff --git a/src/locales/ja/menu.ts b/src/locales/ja/menu.ts new file mode 100644 index 00000000000..a3ede68be79 --- /dev/null +++ b/src/locales/ja/menu.ts @@ -0,0 +1,62 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +/** + * The menu namespace holds most miscellaneous text that isn't directly part of the game's + * contents or directly related to Pokemon data. This includes menu navigation, settings, + * account interactions, descriptive text, etc. + */ +export const menu: SimpleTranslationEntries = { + "cancel": "キャンセル", + "continue": "つづきから", + "dailyRun": "Daily Run (Beta)", + "loadGame": "ロードセーブ", + "newGame": "はじめから", + "settings": "Settings", + "selectGameMode": "Select a game mode.", + "logInOrCreateAccount": "Log in or create an account to start. No email required!", + "username": "ユーザーめい", + "password": "パスワード", + "login": "ログイン", + "orUse": "Or use", + "register": "かいいん とうろく", + "emptyUsername": "ユーザー名は空にできません", + "invalidLoginUsername": "入力したユーザー名は無効です", + "invalidRegisterUsername": "ユーザー名には英文字、数字、アンダースコアのみを含める必要があります", + "invalidLoginPassword": "入力したパスワードは無効です", + "invalidRegisterPassword": "パスワードは6文字以上でなければなりません", + "usernameAlreadyUsed": "ユーザー名は既に使用されています", + "accountNonExistent": "ユーザーは存在しません", + "unmatchingPassword": "パスワードが一致しません", + "passwordNotMatchingConfirmPassword": "パスワードは確認パスワードと一致する必要があります", + "confirmPassword": "パスワード確認", + "registrationAgeWarning": "登録することで、あなたが13歳以上であることを確認します。", + "backToLogin": "ログインへ", + "failedToLoadSaveData": "保存データの読み込みに失敗しました。ページを再読み込みしてください。\nこれが続く場合は、管理者に連絡してください。", + "sessionSuccess": "セッションが正常に読み込まれました。", + "failedToLoadSession": "セッションデータを読み込むことができませんでした。\nデータが破損している可能性があります。", + "boyOrGirl": "おとこのこ?\nそれとも おんなのこ?", + "evolving": "…おや!?\n{{pokemonName}}のようすが…!", + "stoppedEvolving": "{{pokemonName}}のへんかがとまった", + "pauseEvolutionsQuestion": "Would you like to pause evolutions for {{pokemonName}}?\nEvolutions can be re-enabled from the party screen.", + "evolutionsPaused": "Evolutions have been paused for {{pokemonName}}.", + "evolutionDone": "おめでとう!\n{{pokemonName}}は{{evolvedPokemonName}}にしんかした!", + "dailyRankings": "ほんじつのランキング", + "weeklyRankings": "しゅうのランキング", + "noRankings": "ランキングなし", + "positionIcon": "#", + "usernameScoreboard": "Username", + "score": "Score", + "wave": "Wave", + "loading": "よみこみちゅう…", + "loadingAsset": "Loading asset: {{assetName}}", + "playersOnline": "オンラインのプレイヤー", + "yes":"はい", + "no":"いいえ", + "disclaimer": "DISCLAIMER", + "disclaimerDescription": "This game is an unfinished product; it might have playability issues (including the potential loss of save data),\n change without notice, and may or may not be updated further or completed.", + "choosePokemon": "Choose a Pokémon.", + "renamePokemon": "Rename Pokémon", + "rename": "Rename", + "nickname": "Nickname", + "errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.", +} as const; diff --git a/src/locales/ja/modifier-select-ui-handler.ts b/src/locales/ja/modifier-select-ui-handler.ts new file mode 100644 index 00000000000..75299a08ba7 --- /dev/null +++ b/src/locales/ja/modifier-select-ui-handler.ts @@ -0,0 +1,14 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const modifierSelectUiHandler: SimpleTranslationEntries = { + "transfer": "Transfer", + "reroll": "Reroll", + "lockRarities": "Lock Rarities", + "checkTeam": "Check Team", + "transferDesc": "Transfer a held item from one Pokémon to another.", + "rerollDesc": "Spend money to reroll your item options.", + "lockRaritiesDesc": "Lock item rarities on reroll (affects reroll cost).", + "checkTeamDesc": "Check your team or use a form changing item.", + "rerollCost": "₽{{formattedMoney}}", + "itemCost": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/ja/modifier-type.ts b/src/locales/ja/modifier-type.ts new file mode 100644 index 00000000000..e9a28a63af8 --- /dev/null +++ b/src/locales/ja/modifier-type.ts @@ -0,0 +1,456 @@ +import { ModifierTypeTranslationEntries } from "#app/interfaces/locales"; + +export const modifierType: ModifierTypeTranslationEntries = { + ModifierType: { + "AddPokeballModifierType": { + name: "{{modifierCount}}x {{pokeballName}}", + description: "{{pokeballName}} x{{modifierCount}}こ てにいれる (インベントリ: {{pokeballAmount}}) \nほそくりつ: {{catchRate}}", + }, + "AddVoucherModifierType": { + name: "{{modifierCount}}x {{voucherTypeName}}", + description: "{{voucherTypeName}} x{{modifierCount}}こ てにいれる", + }, + "PokemonHeldItemModifierType": { + extra: { + "inoperable": "{{pokemonName}} はこのアイテムを\nもつことができません!", + "tooMany": "{{pokemonName}} はこのアイテムを\nもちすぎています!", + } + }, + "PokemonHpRestoreModifierType": { + description: "ポケモンの HPを {{restorePoints}} または {{restorePercent}}%のどちらか たかいほうを かいふくする", + extra: { + "fully": "ポケモンのHPをすべてかいふくする", + "fullyWithStatus": "ポケモンの HPと じょうたいいじょうを かいふくする", + } + }, + "PokemonReviveModifierType": { + description: "ひんしになってしまったポケモンの HP {{restorePercent}}%を かいふくする", + }, + "PokemonStatusHealModifierType": { + description: "すべてのじょうたいいじょうを なおす", + }, + "PokemonPpRestoreModifierType": { + description: "ポケモンが おぼえている わざの PPを {{restorePoints}}ずつ かいふくする", + extra: { + "fully": "ポケモンが おぼえている わざの PPを すべて かいふくする", + } + }, + "PokemonAllMovePpRestoreModifierType": { + description: "ポケモンが おぼえている 4つの わざの PPを {{restorePoints}}ずつ かいふくする", + extra: { + "fully": "ポケモンが おぼえている 4つの わざの PPを すべて かいふくする", + } + }, + "PokemonPpUpModifierType": { + description: "ポケモンのわざのさいだいPPを さいだいPP 5ごとに {{upPoints}} ポイントずつ ふやします(さいだい3)", + }, + "PokemonNatureChangeModifierType": { + name: "{{natureName}} Mint", + description: "ポケモンのせいかくを {{natureName}}にかえて スターターのせいかくをえいきゅうにかいじょする", + }, + "DoubleBattleChanceBoosterModifierType": { + description: "バトル{{battleCount}}かいのあいだ ダブルバトルになるかくりつを2ばいにする", + }, + "TempBattleStatBoosterModifierType": { + description: "すべてのパーティメンバーの {{tempBattleStatName}}を5かいのバトルのあいだ 1だんかいあげる", + }, + "AttackTypeBoosterModifierType": { + description: "ポケモンの {{moveType}}タイプのわざのいりょくを20パーセントあげる", + }, + "PokemonLevelIncrementModifierType": { + description: "ポケモンのレベルを1あげる", + }, + "AllPokemonLevelIncrementModifierType": { + description: "すべてのパーティメンバーのレベルを1あげる", + }, + "PokemonBaseStatBoosterModifierType": { + description: "ポケモンの{{statName}}のきほんステータスを10パーセントあげる。こたいちがたかいほどスタックのげんかいもたかくなる。", + }, + "AllPokemonFullHpRestoreModifierType": { + description: "すべてのポケモンのHPを100パーセントかいふくする", + }, + "AllPokemonFullReviveModifierType": { + description: "ひんしになったすべてのポケモンをふっかつさせ HPをぜんかいふくする", + }, + "MoneyRewardModifierType": { + description: "{{moneyMultiplier}}ぶんのきんがくをあたえる (₽{{moneyAmount}})", + extra: { + "small": "すくない", + "moderate": "ふつう", + "large": "おおい", + }, + }, + "ExpBoosterModifierType": { + description: "もらえるけいけんちを {{boostPercent}}パーセントふやす", + }, + "PokemonExpBoosterModifierType": { + description: "もっているポケモンのけいけんちを {{boostPercent}}パーセントふやす", + }, + "PokemonFriendshipBoosterModifierType": { + description: "しょうりごとに 50%パーセント なかよく なりやすくなる", + }, + "PokemonMoveAccuracyBoosterModifierType": { + description: "わざのめいちゅうりつを{{accuracyAmount}}ふやす (さいだい100)", + }, + "PokemonMultiHitModifierType": { + description: "こうげきがもういちどあたる。そのたびにいりょくがそれぞれ60/75/82.5%へる", + }, + "TmModifierType": { + name: "TM{{moveId}} - {{moveName}}", + description: "ポケモンに {{moveName}} をおしえる", + }, + "TmModifierTypeWithInfo": { + name: "TM{{moveId}} - {{moveName}}", + description: "ポケモンに {{moveName}} をおしえる\n(Hold C or Shift for more info)", + }, + "EvolutionItemModifierType": { + description: "とくていのポケモンをしんかさせる", + }, + "FormChangeItemModifierType": { + description: "とくていのポケモンをフォームチェンジさせる", + }, + "FusePokemonModifierType": { + description: "2匹のポケモンをけつごうする (とくせいをいどうし、きほんステータスとタイプをわけ、わざプールをきょうゆうする)", + }, + "TerastallizeModifierType": { + name: "{{teraType}} Tera Shard", + description: "ポケモンを{{teraType}}タイプにテラスタル(10かいのバトルまで)", + }, + "ContactHeldItemTransferChanceModifierType": { + description: "こうげきするとき あいてがもっているアイテムを {{chancePercent}}パーセントのかくりつでぬすむ", + }, + "TurnHeldItemTransferModifierType": { + description: "まいターン あいてからひとつのもちものをてにいれる", + }, + "EnemyAttackStatusEffectChanceModifierType": { + description: "こうげきわざに {{chancePercent}}パーセントのかくりつで {{statusEffect}}をあたえる", + }, + "EnemyEndureChanceModifierType": { + description: "こうげきをこらえるかくりつを{{chancePercent}}パーセントふやす", + }, + + "RARE_CANDY": { name: "ふしぎなアメ" }, + "RARER_CANDY": { name: "もっとふしぎなアメ" }, + + "MEGA_BRACELET": { name: "メガバングル", description: "メガストーンがつかえるようになる" }, + "DYNAMAX_BAND": { name: "ダイマックスバンド", description: "ダイスープがつかえるようになる" }, + "TERA_ORB": { name: "テラスタルオーブ", description: "テラピースがつかえるようになる" }, + + "MAP": { name: "ちず", description: "わかれみちでいきさきをえらべるようになる" }, + + "POTION": { name: "キズぐすり" }, + "SUPER_POTION": { name: "いいキズぐすり" }, + "HYPER_POTION": { name: "すごいキズぐすり" }, + "MAX_POTION": { name: "まんたんのくすり" }, + "FULL_RESTORE": { name: "かいふくのくすり" }, + + "REVIVE": { name: "げんきのかけら" }, + "MAX_REVIVE": { name: "げんきのかたまり" }, + + "FULL_HEAL": { name: "なんでもなおし" }, + + "SACRED_ASH": { name: "せいなるはい" }, + + "REVIVER_SEED": { name: "ふっかつのタネ", description: "ひんしになったときもっているポケモンをHPはんぶんでふっかつさせる" }, + + "WHITE_HERB": { name: "White Herb", description: "An item to be held by a Pokémon. It will restore any lowered stat in battle." }, + + "ETHER": { name: "ピーピーエイド" }, + "MAX_ETHER": { name: "ピーピーリカバー" }, + + "ELIXIR": { name: "ピーピーエイダー" }, + "MAX_ELIXIR": { name: "ピーピーマックス" }, + + "PP_UP": { name: "ポイントアップ" }, + "PP_MAX": { name: "ポイントマックス" }, + + "LURE": { name: "ダブルバトルコロン" }, + "SUPER_LURE": { name: "シルバーコロン" }, + "MAX_LURE": { name: "ゴールドコロン" }, + + "MEMORY_MUSHROOM": { name: "きおくキノコ", description: "ポケモンのわすれたわざをおぼえさせる" }, + + "EXP_SHARE": { name: "がくしゅうそうち", description: "バトルにさんかしていないポケモンが けいけんちの20パーセントをもらう" }, + "EXP_BALANCE": { name: "バランスそうち", description: "レベルがひくいパーティメンバーがもらうけいけんちがふえる" }, + + "OVAL_CHARM": { name: "まるいおまもり", description: "バトルにふくすうのポケモンがさんかするとけいけんちが10パーセントふえる" }, + + "EXP_CHARM": { name: "けいけんちおまもり" }, + "SUPER_EXP_CHARM": { name: "いいけいけんちおまもり" }, + "GOLDEN_EXP_CHARM": { name: "ゴールドけいけんちおまもり" }, + + "LUCKY_EGG": { name: "しあわせタマゴ" }, + "GOLDEN_EGG": { name: "おうごんタマゴ" }, + + "SOOTHE_BELL": { name: "やすらぎのすず" }, + + "SCOPE_LENS": { name: "ピントレンズ", description: "弱点が 見える レンズ。持たせた ポケモンの技が 急所に 当たりやすくなる。"}, + "LEEK": { name: "ながねぎ", description: "とても長くて 硬いクキ。カモネギに 持たせると 技が 急所に 当たりやすくなる。"}, + + "EVIOLITE": { name: "しんかのきせき", description: "進化の不思議な かたまり。持たせると 進化前ポケモンの 防御と 特防が あがる。" }, + + "SOUL_DEW": { name: "こころのしずく", description: "ポケモンのせいかくがステータスにあたえるえいきょうを10%ふやす(合算)" }, + + "NUGGET": { name: "きんのたま" }, + "BIG_NUGGET": { name: "でかいきんのたま" }, + "RELIC_GOLD": { name: "こだいのきんか" }, + + "AMULET_COIN": { name: "おまもりこばん", description: "もらえる おかねが 20パーセント ふえる" }, + "GOLDEN_PUNCH": { name: "ゴールドパンチ", description: "あたえたちょくせつダメージの50パーセントをおかねとしてもらえる" }, + "COIN_CASE": { name: "コインケース", description: "10かいのバトルごとにもちきんの10パーセントをりしとしてうけとる" }, + + "LOCK_CAPSULE": { name: "ロックカプセル", description: "リロールするときにアイテムのレアリティをロックできる" }, + + "GRIP_CLAW": { name: "ねばりのかぎづめ" }, + "WIDE_LENS": { name: "こうかくレンズ" }, + + "MULTI_LENS": { name: "マルチレンズ" }, + + "HEALING_CHARM": { name: "ヒーリングチャーム", description: "HPをかいふくするわざとアイテムのこうかを10パーセントあげる (ふっかつはのぞく)" }, + "CANDY_JAR": { name: "アメボトル", description: "ふしぎなアメのアイテムでふえるレベルが1ふえる" }, + + "BERRY_POUCH": { name: "きのみぶくろ", description: "つかったきのみがつかわれないかくりつを30パーセントふやす" }, + + "FOCUS_BAND": { name: "きあいのハチマキ", description: "ひんしになるダメージをうけてもHP1でたえるかくりつを10パーセントふやす" }, + + "QUICK_CLAW": { name: "せんせいのツメ", description: "すばやさにかかわらず さきにこうどうするかくりつを10パーセントふやす (ゆうせんどのあと)" }, + + "KINGS_ROCK": { name: "おうじゃのしるし", description: "こうげきわざがあいてをひるませるかくりつを10パーセントふやす" }, + + "LEFTOVERS": { name: "たべのこし", description: "ポケモンのさいだいHPの1/16をまいターンかいふくする" }, + "SHELL_BELL": { name: "かいがらのすず", description: "ポケモンがあたえたダメージの1/8をかいふくする" }, + + "TOXIC_ORB": { name: "どくどくだま", description: "ターンの終わりに すでに じょうたいじょうしょうが なければ もうどくの じょうたいに なる" }, + "FLAME_ORB": { name: "かえんだま", description: "ターンの終わりに すでに じょうたいじょうしょうが なければ やけどの じょうたいに なる" }, + + "BATON": { name: "バトン", description: "ポケモンをこうたいするときにこうかをひきつぎ わなをかいひすることもできる" }, + + "SHINY_CHARM": { name: "ひかるおまもり", description: "やせいのポケモンがいろちがいポケモンであるかくりつをおおきくふやす" }, + "ABILITY_CHARM": { name: "とくせいおまもり", description: "やせいのポケモンがかくれとくせいをもつかくりつをおおきくふやす" }, + + "IV_SCANNER": { name: "こたいち たんちき", description: "やせいのポケモンのこたいちをスキャンできる。スタックごとに2つのこたいちがあきらかになる。もっともたかいこたいちがさいしょにひょうじされる" }, + + "DNA_SPLICERS": { name: "いでんしのくさび" }, + + "MINI_BLACK_HOLE": { name: "ミニブラックホール" }, + + "GOLDEN_POKEBALL": { name: "ゴールドモンスターボール", description: "バトルごとに1つのアイテムオプションをふやす" }, + + "ENEMY_DAMAGE_BOOSTER": { name: "ダメージトークン", description: "ダメージを5%ふやす" }, + "ENEMY_DAMAGE_REDUCTION": { name: "プロテクショントークン", description: "うけるダメージを2.5%へらす" }, + "ENEMY_HEAL": { name: "かいふくトークン", description: "まいターンさいだいHPの2%をかいふくする" }, + "ENEMY_ATTACK_POISON_CHANCE": { name: "どくトークン" }, + "ENEMY_ATTACK_PARALYZE_CHANCE": { name: "まひトークン" }, + "ENEMY_ATTACK_BURN_CHANCE": { name: "やけどトークン" }, + "ENEMY_STATUS_EFFECT_HEAL_CHANCE": { name: "なおしトークン", description: "まいターン2.5%のかくりつでじょうたいじょうしょうをかいふくする" }, + "ENEMY_ENDURE_CHANCE": { name: "こらえるトークン" }, + "ENEMY_FUSED_CHANCE": { name: "フュージョントークン", description: "やせいのポケモンがフュージョンするかくりつを1%ふやす" }, + }, + SpeciesBoosterItem: { + "LIGHT_BALL": { name: "でんきだま", description: "ピカチュウに 持たせると 攻撃と 特攻が あがる 不思議な玉。" }, + "THICK_CLUB": { name: "ふといホネ", description: "なにかの 硬いホネ。カラカラ または ガラガラに 持たせると 攻撃が あがる。" }, + "METAL_POWDER": { name: "メタルパウダー", description: "メタモンに 持たせると 防御が あがる 不思議な粉。とても こまかくて 硬い。" }, + "QUICK_POWDER": { name: "スピードパウダー", description: "メタモンに 持たせると 素早さが あがる 不思議 粉。とても こまかくて 硬い。" } + }, + TempBattleStatBoosterItem: { + "x_attack": "プラスパワー", + "x_defense": "ディフェンダー", + "x_sp_atk": "スペシャルアップ", + "x_sp_def": "スペシャルガード", + "x_speed": "スピーダー", + "x_accuracy": "ヨクアタール", + "dire_hit": "クリティカット", + }, + + TempBattleStatBoosterStatName: { + "ATK": "こうげき", + "DEF": "ぼうぎょ", + "SPATK": "とくこう", + "SPDEF": "とくぼう", + "SPD": "すばやさ", + "ACC": "めいちゅう", + "CRIT": "きゅうしょりつ", + "EVA": "かいひ", + "DEFAULT": "???", + }, + + AttackTypeBoosterItem: { + "silk_scarf": "シルクのスカーフ", + "black_belt": "くろおび", + "sharp_beak": "するどいくちばし", + "poison_barb": "どくバリ", + "soft_sand": "やわらかいすな", + "hard_stone": "かたいいし", + "silver_powder": "ぎんのこな", + "spell_tag": "のろいのおふだ", + "metal_coat": "メタルコート", + "charcoal": "もくたん", + "mystic_water": "しんぴのしずく", + "miracle_seed": "きせきのタネ", + "magnet": "じしゃく", + "twisted_spoon": "まがったスプーン", + "never_melt_ice": "とけないこおり", + "dragon_fang": "りゅうのキバ", + "black_glasses": "くろいメガネ", + "fairy_feather": "ようせいのハネ", + }, + BaseStatBoosterItem: { + "hp_up": "マックスアップ", + "protein": "タウリン", + "iron": "ブロムヘキシン", + "calcium": "リゾチウム", + "zinc": "キトサン", + "carbos": "インドメタシン", + }, + EvolutionItem: { + "NONE": "None", + + "LINKING_CORD": "つながりのヒモ", + "SUN_STONE": "たいようのいし", + "MOON_STONE": "つきのいし", + "LEAF_STONE": "リーフのいし", + "FIRE_STONE": "ほのおのいし", + "WATER_STONE": "みずのいし", + "THUNDER_STONE": "かみなりのいし", + "ICE_STONE": "こおりのいし", + "DUSK_STONE": "やみのいし", + "DAWN_STONE": "めざめいし", + "SHINY_STONE": "ひかりのいし", + "CRACKED_POT": "われたポット", + "SWEET_APPLE": "あまーいりんご", + "TART_APPLE": "すっぱいりんご", + "STRAWBERRY_SWEET": "いちごアメざいく", + "UNREMARKABLE_TEACUP": "ボンサクのちゃわん", + + "CHIPPED_POT": "かけたポット", + "BLACK_AUGURITE": "くろのきせき", + "GALARICA_CUFF": "ガラナツブレス", + "GALARICA_WREATH": "ガラナツリース", + "PEAT_BLOCK": "ピートブロック", + "AUSPICIOUS_ARMOR": "イワイノヨロイ", + "MALICIOUS_ARMOR": "ノロイノヨロイ", + "MASTERPIECE_TEACUP": "ケッサクのちゃわん", + "METAL_ALLOY": "ふくごうきんぞく", + "SCROLL_OF_DARKNESS": "あくのかけじく", + "SCROLL_OF_WATERS": "みずのかけじく", + "SYRUPY_APPLE": "みついりりんご", + }, + FormChangeItem: { + "NONE": "None", + + "ABOMASITE": "ユキノオナイト", + "ABSOLITE": "アブソルナイト", + "AERODACTYLITE": "プテラナイト", + "AGGRONITE": "ボスゴドラナイト", + "ALAKAZITE": "フーディナイト", + "ALTARIANITE": "チルタリスナイト", + "AMPHAROSITE": "デンリュウナイト", + "AUDINITE": "タブンネナイト", + "BANETTITE": "ジュペッタナイト", + "BEEDRILLITE": "スピアナイト", + "BLASTOISINITE": "カメックスナイト", + "BLAZIKENITE": "バシャーモナイト", + "CAMERUPTITE": "バクーダナイト", + "CHARIZARDITE_X": "リザードナイトX", + "CHARIZARDITE_Y": "リザードナイトY", + "DIANCITE": "ディアンシナイト", + "GALLADITE": "エルレイドナイト", + "GARCHOMPITE": "ガブリアスナイト", + "GARDEVOIRITE": "サーナイトナイト", + "GENGARITE": "ゲンガナイト", + "GLALITITE": "オニゴーリナイト", + "GYARADOSITE": "ギャラドスナイト", + "HERACRONITE": "ヘラクロスナイト", + "HOUNDOOMINITE": "ヘルガナイト", + "KANGASKHANITE": "ガルーラナイト", + "LATIASITE": "ラティアスナイト", + "LATIOSITE": "ラティオスナイト", + "LOPUNNITE": "ミミロップナイト", + "LUCARIONITE": "ルカリオナイト", + "MANECTITE": "ライボルトナイト", + "MAWILITE": "クチートナイト", + "MEDICHAMITE": "チャーレムナイト", + "METAGROSSITE": "メタグロスナイト", + "MEWTWONITE_X": "ミュウツナイトX", + "MEWTWONITE_Y": "ミュウツナイトY", + "PIDGEOTITE": "ピジョットナイト", + "PINSIRITE": "カイロスナイト", + "RAYQUAZITE": "レックウザナイト", + "SABLENITE": "ヤミラミナイト", + "SALAMENCITE": "ボーマンダナイト", + "SCEPTILITE": "ジュカインナイト", + "SCIZORITE": "ハッサムナイト", + "SHARPEDONITE": "サメハダナイト", + "SLOWBRONITE": "ヤドランナイト", + "STEELIXITE": "ハガネールナイト", + "SWAMPERTITE": "ラグラージナイト", + "TYRANITARITE": "バンギラスナイト", + "VENUSAURITE": "フシギバナイト", + + "BLUE_ORB": "あいいろのたま", + "RED_ORB": "べにいろのたま", + "SHARP_METEORITE": "シャープなうんせき", + "HARD_METEORITE": "かたいうんせき", + "SMOOTH_METEORITE": "やわらかいうんせき", + "ADAMANT_CRYSTAL": "だいこんごうだま", + "LUSTROUS_GLOBE": "だいしらたま", + "GRISEOUS_CORE": "だいはっきんだま", + "REVEAL_GLASS": "うつしかがみ", + "GRACIDEA": "グラシデアのはな", + "MAX_MUSHROOMS": "ダイキノコ", + "DARK_STONE": "ダークストーン", + "LIGHT_STONE": "ライトストーン", + "PRISON_BOTTLE": "いましめのツボ", + "N_LUNARIZER": "ネクロプラスルナ", + "N_SOLARIZER": "ネクロプラスソル", + "RUSTED_SWORD": "くちたけん", + "RUSTED_SHIELD": "くちたたて", + "ICY_REINS_OF_UNITY": "つめたいキズナのタヅナ", + "SHADOW_REINS_OF_UNITY": "くろいキズナのタヅナ", + "WELLSPRING_MASK": "いどのめん", + "HEARTHFLAME_MASK": "かまどのめん", + "CORNERSTONE_MASK": "いしずえのめん", + "SHOCK_DRIVE": "イナズマカセット", + "BURN_DRIVE": "ブレイズカセット", + "CHILL_DRIVE": "フリーズカセット", + "DOUSE_DRIVE": "アクアカセット", + + "FIST_PLATE": "Fist Plate", + "SKY_PLATE": "Sky Plate", + "TOXIC_PLATE": "Toxic Plate", + "EARTH_PLATE": "Earth Plate", + "STONE_PLATE": "Stone Plate", + "INSECT_PLATE": "Insect Plate", + "SPOOKY_PLATE": "Spooky Plate", + "IRON_PLATE": "Iron Plate", + "FLAME_PLATE": "Flame Plate", + "SPLASH_PLATE": "Splash Plate", + "MEADOW_PLATE": "Meadow Plate", + "ZAP_PLATE": "Zap Plate", + "MIND_PLATE": "Mind Plate", + "ICICLE_PLATE": "Icicle Plate", + "DRACO_PLATE": "Draco Plate", + "DREAD_PLATE": "Dread Plate", + "PIXIE_PLATE": "Pixie Plate", + "BLANK_PLATE": "Blank Plate", + "LEGEND_PLATE": "Legend Plate", + "FIGHTING_MEMORY": "Fighting Memory", + "FLYING_MEMORY": "Flying Memory", + "POISON_MEMORY": "Poison Memory", + "GROUND_MEMORY": "Ground Memory", + "ROCK_MEMORY": "Rock Memory", + "BUG_MEMORY": "Bug Memory", + "GHOST_MEMORY": "Ghost Memory", + "STEEL_MEMORY": "Steel Memory", + "FIRE_MEMORY": "Fire Memory", + "WATER_MEMORY": "Water Memory", + "GRASS_MEMORY": "Grass Memory", + "ELECTRIC_MEMORY": "Electric Memory", + "PSYCHIC_MEMORY": "Psychic Memory", + "ICE_MEMORY": "Ice Memory", + "DRAGON_MEMORY": "Dragon Memory", + "DARK_MEMORY": "Dark Memory", + "FAIRY_MEMORY": "Fairy Memory", + "BLANK_MEMORY": "Blank Memory", + }, +} as const; diff --git a/src/locales/ja/modifier.ts b/src/locales/ja/modifier.ts new file mode 100644 index 00000000000..26a6a9c18ae --- /dev/null +++ b/src/locales/ja/modifier.ts @@ -0,0 +1,14 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const modifier: SimpleTranslationEntries = { + "surviveDamageApply": "{{pokemonNameWithAffix}} hung on\nusing its {{typeName}}!", + "turnHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", + "hitHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", + "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} was revived\nby its {{typeName}}!", + "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}'s lowered stats were restored\nby its {{typeName}}!", + "moneyInterestApply": "You received interest of ₽{{moneyAmount}}\nfrom the {{typeName}}!", + "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was absorbed\nby {{pokemonName}}'s {{typeName}}!", + "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was snatched\nby {{pokemonName}}'s {{typeName}}!", + "enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestored some HP!", + "bypassSpeedChanceApply": "{{pokemonName}} can act faster than normal, thanks to its {{itemName}}!", +} as const; diff --git a/src/locales/ja/move-trigger.ts b/src/locales/ja/move-trigger.ts new file mode 100644 index 00000000000..7c794379f55 --- /dev/null +++ b/src/locales/ja/move-trigger.ts @@ -0,0 +1,64 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const moveTriggers: SimpleTranslationEntries = { + "hitWithRecoil" : "{{pokemonName}}は\nはんどうによる ダメージを うけた!", + "cutHpPowerUpMove": "{{pokemonName}}は\nたいりょくを けずって パワーぜんかい!", + "absorbedElectricity": "{{pokemonName}}は\n でんきを きゅうしゅうした!", + "switchedStatChanges": "{{pokemonName}}は あいてと じぶんのn\のうりょくへんかを いれかえた!", + "goingAllOutForAttack": "{{pokemonName}}は\nほんきを だした!", + "regainedHealth": "{{pokemonName}}は\nたいりょくを かいふくした!", + "keptGoingAndCrashed": "いきおいあまって {{pokemonName}}は\nじめんに ぶつかった!", + "fled": "{{pokemonName}}は にげだした!", + "cannotBeSwitchedOut": "{{pokemonName}}を\nもどすことが できない!", + "swappedAbilitiesWithTarget": "{{pokemonName}}は\nおたがいの とくせいを いれかえた!", + "coinsScatteredEverywhere": "こばんが あたりに ちらばった!", + "attackedByItem": "{{pokemonName}}に\n{{itemName}}が おそいかかる!", + "whippedUpAWhirlwind": "{{pokemonName}}の まわりで\nくうきが うずをまく!", + "flewUpHigh": "{{pokemonName}}は\nそらたかく とびあがった!", + "tookInSunlight": "{{pokemonName}}は\nひかりを きゅうしゅうした!", + "dugAHole": "{{pokemonName}}は\nじめんに もぐった!", + "loweredItsHead": "{{pokemonName}}は\nくびを ひっこめた!", + "isGlowing": "{{pokemonName}}を\nはげしいひかりが つつむ!", + "bellChimed": "すずのおとが ひびきわたった!", + "foresawAnAttack": "{{pokemonName}}は\nみらいに こうげきを よちした!", + "isTighteningFocus": "{{pokemonName}} is\ntightening its focus!", + "hidUnderwater": "{{pokemonName}}は\nすいちゅうに みをひそめた!", + "soothingAromaWaftedThroughArea": "ここちよい かおりが ひろがった!", + "sprangUp": "{{pokemonName}}は\nたかく とびはねた!", + "choseDoomDesireAsDestiny": "{{pokemonName}}は\nはめつのねがいを みらいに たくした!", + "vanishedInstantly": "{{pokemonName}}の すがたが\nいっしゅんにして きえた!", + "tookTargetIntoSky": "{{pokemonName}}は {{targetName}}を\nじょうくうに つれさった!", + "becameCloakedInFreezingLight": "{{pokemonName}}は\nつめたいひかりに つつまれた!", + "becameCloakedInFreezingAir": "{{pokemonName}}は\nこごえるくうきに つつまれた!", + "isChargingPower": "{{pokemonName}}は\nパワーを ためこんでいる!", + "burnedItselfOut": "{{pokemonName}}の ほのうは\nもえつきた!", + "startedHeatingUpBeak": "{{pokemonName}}は\nクチバシを かねつしはじめた!", + "isOverflowingWithSpacePower": "{{pokemonName}}に\nうちゅうの ちからが あふれだす!", + "usedUpAllElectricity": "{{pokemonName}}は\nでんきを つかいきった!", + "stoleItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を ぬすんだ!", + "incineratedItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を もやした!", + "knockedOffItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を はたきおとした!", + "tookMoveAttack": "{{pokemonName}}は\n{{moveName}}の こうげきを うけた!", + "cutOwnHpAndMaximizedStat": "{{pokemonName}}は\nたいりょくを けずって {{statName}}ぜんかい!", + "copiedStatChanges": "{{pokemonName}}は {{targetName}}の\nのうりょくへんかを コピーした!", + "magnitudeMessage": "マグニチュード{{magnitude}}!", + "tookAimAtTarget": "{{pokemonName}}は {{targetName}}に\nねらいを さだめた!", + "transformedIntoType": "{{pokemonName}}は\n{{typeName}}タイプに なった!", + "copiedMove": "{{pokemonName}}は\n{{moveName}}を コピーした!", + "sketchedMove": "{{pokemonName}}は\n{{moveName}}を スケッチした!", + "acquiredAbility": "{{pokemonName}}の とくせいが\n{{abilityName}}に なった!", + "copiedTargetAbility": "{{pokemonName}}は\n{{targetName}}の {{abilityName}}を コピーした!", + "transformedIntoTarget": "{{pokemonName}}は\n{{targetName}}に へんしんした!", + "tryingToTakeFoeDown": "{{pokemonName}}は あいてを\nみちづれに しようとしている!", + "addType": "{{pokemonName}}に\n{{typeName}}タイプが ついかされた!", + "cannotUseMove": "{{pokemonName}}は\n{{moveName}}を つかえなかった!", + "healHp": "{{pokemonName}}の\nたいりょくが かいふくした!", + "sacrificialFullRestore": "{{pokemonName}}の\nねがいごとが かなった!", + "invertStats": "{{pokemonName}}の\nのうりょくへんかが ぎゃくてんした!", + "resetStats": "{{pokemonName}}の\nのうりょくへんかが もとにもどった!", + "faintCountdown": "{{pokemonName}}は\n{{turnCount}}ターンごに ほろびてしまう!", + "copyType": "{{pokemonName}}は {{targetPokemonName}}と\n同じタイプに なった!", + "suppressAbilities": "{{pokemonName}}の とくせいが きかなくなった!", + "swapArenaTags": "{{pokemonName}}は\nおたがいの ばのこうかを いれかえた!", + "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!", +} as const; diff --git a/src/locales/ja/move.ts b/src/locales/ja/move.ts new file mode 100644 index 00000000000..1b46e10be3a --- /dev/null +++ b/src/locales/ja/move.ts @@ -0,0 +1,3812 @@ +import { MoveTranslationEntries } from "#app/interfaces/locales"; + +export const move: MoveTranslationEntries = { + pound: { + name: "はたく", + effect: "長い しっぽや 手などを 使って 相手を はたいて 攻撃する。" + }, + karateChop: { + name: "からてチョップ", + effect: "鋭い チョップで 相手を たたいて 攻撃する。 急所に 当たりやすい。" + }, + doubleSlap: { + name: "おうふくビンタ", + effect: "おうふく ビンタで 相手を たたいて 攻撃する。 2ー5回の 間 連続で だす。" + }, + cometPunch: { + name: "れんぞくパンチ", + effect: "どとうの パンチで 相手を なぐりつけて 攻撃する。 2ー5回の 間 連続で だす。" + }, + megaPunch: { + name: "メガトンパンチ", + effect: "力を こめた パンチで 相手を 攻撃する。" + }, + payDay: { + name: "ネコにこばん", + effect: "相手の 体に 小判を 投げつけて 攻撃する。 戦闘の あとで お金が もらえる。" + }, + firePunch: { + name: "ほのおのパンチ", + effect: "炎を こめた パンチで 相手を 攻撃する。 やけど状態に することが ある。" + }, + icePunch: { + name: "れいとうパンチ", + effect: "冷気を こめた パンチで 相手を 攻撃する。 こおり状態に することが ある。" + }, + thunderPunch: { + name: "かみなりパンチ", + effect: "電撃を こめた パンチで 相手を 攻撃する。 まひ状態に することが ある。" + }, + scratch: { + name: "ひっかく", + effect: "硬く とがった 鋭い ツメで 相手を ひっかいて 攻撃する。" + }, + viseGrip: { + name: "はさむ", + effect: "相手を 両側から はさんで ダメージを あたえる。" + }, + guillotine: { + name: "ハサミギロチン", + effect: "大きな ハサミで 相手を 切り裂いて 攻撃する。 当たれば 一撃で ひんしに する。" + }, + razorWind: { + name: "かまいたち", + effect: "風の 刃を つくり 2ターン目に 相手を 攻撃する。 急所に 当たりやすい。" + }, + swordsDance: { + name: "つるぎのまい", + effect: "戦いの舞を 激しく おどって 気合を 高める。 自分の 攻撃を ぐーんと あげる。" + }, + cut: { + name: "いあいぎり", + effect: "カマや ツメなどで 相手を 切りつけて 攻撃する。" + }, + gust: { + name: "かぜおこし", + effect: "翼で おこした 激しい 風を 相手に ぶつけて 攻撃する。" + }, + wingAttack: { + name: "つばさでうつ", + effect: "大きく ひろげた りっぱな 翼を 相手に ぶつけて 攻撃する。" + }, + whirlwind: { + name: "ふきとばし", + effect: "相手を 吹きとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" + }, + fly: { + name: "そらをとぶ", + effect: "1ターン目で 空へ 飛び 2ターン目に 相手を 攻撃する。" + }, + bind: { + name: "しめつける", + effect: "長い 体や つるなどを 使い 4ー5ターンの 間 相手を 締めつけて 攻撃する。" + }, + slam: { + name: "たたきつける", + effect: "長い しっぽや つるなどを 使い 相手を たたきつけて 攻撃する。" + }, + vineWhip: { + name: "つるのムチ", + effect: "ムチのように しなる 細長い つるで 相手を たたきつけて 攻撃する。" + }, + stomp: { + name: "ふみつけ", + effect: "大きな 足で 相手を 踏みつけて 攻撃する。 相手を ひるませることが ある。" + }, + doubleKick: { + name: "にどげり", + effect: "2本の 足で 相手を けとばして 攻撃する。 2回連続で ダメージを 与える。" + }, + megaKick: { + name: "メガトンキック", + effect: "ものすごい 力を こめた キックで 相手を けとばして 攻撃する。" + }, + jumpKick: { + name: "とびげり", + effect: "高い ジャンプからの キックで 相手を 攻撃する。 はずすと 自分が ダメージを 受ける。" + }, + rollingKick: { + name: "まわしげり", + effect: "体を 素早く 回転させながら けとばして 攻撃する。 相手を ひるませる ことが ある。" + }, + sandAttack: { + name: "すなかけ", + effect: "相手の 顔に 砂を かけて 命中率を さげる。" + }, + headbutt: { + name: "ずつき", + effect: "頭を 突きだして まっすぐ つっこんで 攻撃する。 相手を ひるませることが ある。" + }, + hornAttack: { + name: "つのでつく", + effect: "鋭く とがった つので 相手を 攻撃する。" + }, + furyAttack: { + name: "みだれづき", + effect: "つのや くちばしで 相手を つついて 攻撃する。 2ー5回の 間 連続で だす。" + }, + hornDrill: { + name: "つのドリル", + effect: "回転する つのを 相手に 突き刺して 攻撃する。 当たれば 一撃で ひんしに する。" + }, + tackle: { + name: "たいあたり", + effect: "相手に むかって 全身で ぶつかっていき 攻撃する。" + }, + bodySlam: { + name: "のしかかり", + effect: "全身で 相手に のしかかり 攻撃する。 まひ状態に することが ある。" + }, + wrap: { + name: "まきつく", + effect: "長い 体や つるなどを 使って 4ー5ターンの 間 相手に まきついて 攻撃する。" + }, + takeDown: { + name: "とっしん", + effect: "すごい 勢いで 相手に ぶつかって 攻撃する。 自分も 少し ダメージを 受ける。" + }, + thrash: { + name: "あばれる", + effect: "2ー3ターンの 間 暴れまくって 相手を 攻撃する。 暴れたあとは 混乱する。" + }, + doubleEdge: { + name: "すてみタックル", + effect: "命を 懸けて 相手に 突進して 攻撃する。 自分も かなり ダメージを 受ける。" + }, + tailWhip: { + name: "しっぽをふる", + effect: "しっぽを 左右に かわいく ふって 油断を 誘う。 相手の 防御を さげる。" + }, + poisonSting: { + name: "どくばり", + effect: "毒の ある ハリを 相手に 突き刺して 攻撃する。 毒状態に することが ある。" + }, + twineedle: { + name: "ダブルニードル", + effect: "2本の ハリを 相手に 突き刺し 2回連続で ダメージ。 毒状態に することが ある。" + }, + pinMissile: { + name: "ミサイルばり", + effect: "鋭い ハリを 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + }, + leer: { + name: "にらみつける", + effect: "鋭い 目つきで おびえさせて 相手の 防御を さげる。" + }, + bite: { + name: "かみつく", + effect: "鋭く とがった 歯で かみついて 攻撃する。 相手を ひるませることが ある。" + }, + growl: { + name: "なきごえ", + effect: "かわいい なきごえを 聞かせて 気を ひき 油断を させて 相手の 攻撃を さげる。" + }, + roar: { + name: "ほえる", + effect: "相手を 逃がして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" + }, + sing: { + name: "うたう", + effect: "心地好い きれいな 歌声を 聞かせて 相手を 眠り状態に する。" + }, + supersonic: { + name: "ちょうおんぱ", + effect: "特殊な 音波を 体から 発して 相手を 混乱させる。" + }, + sonicBoom: { + name: "ソニックブーム", + effect: "衝撃波を 相手に ぶつけて 攻撃する。 20の ダメージを 決まって 与える。" + }, + disable: { + name: "かなしばり", + effect: "相手の 動きを とめて 直前に だしていた 技を 4ターンの 間 使えなくする。" + }, + acid: { + name: "ようかいえき", + effect: "強い 酸を 相手に かけて 攻撃する。 相手の 特防を さげることが ある。" + }, + ember: { + name: "ひのこ", + effect: "小さな 炎を 相手に 発射して 攻撃する。 やけど状態に することが ある。" + }, + flamethrower: { + name: "かえんほうしゃ", + effect: "激しい 炎を 相手に 発射して 攻撃する。 やけど状態に することが ある。" + }, + mist: { + name: "しろいきり", + effect: "白い霧で 体を おおう。 5ターンの 間 相手に 能力を さげられなく なる。" + }, + waterGun: { + name: "みずでっぽう", + effect: "水を 勢いよく 相手に 発射して 攻撃する。" + }, + hydroPump: { + name: "ハイドロポンプ", + effect: "大量の 水を 激しい 勢いで 相手に 発射して 攻撃する。" + }, + surf: { + name: "なみのり", + effect: "大きな 波で 自分の 周りに いるものを 攻撃する。" + }, + iceBeam: { + name: "れいとうビーム", + effect: "凍える ビームを 相手に 発射して 攻撃する。 こおり状態に することが ある。" + }, + blizzard: { + name: "ふぶき", + effect: "激しい 吹雪を 相手に 吹きつけて 攻撃する。 こおり状態に することが ある。" + }, + psybeam: { + name: "サイケこうせん", + effect: "不思議な 光線を 相手に 発射して 攻撃する。 混乱させることが ある。" + }, + bubbleBeam: { + name: "バブルこうせん", + effect: "泡を 勢いよく 相手に 発射して 攻撃する。 素早さを さげる ことが ある。" + }, + auroraBeam: { + name: "オーロラビーム", + effect: "にじいろの ビームを 相手に 発射して 攻撃する。 攻撃を さげる ことが ある。" + }, + hyperBeam: { + name: "はかいこうせん", + effect: "強い 光線を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" + }, + peck: { + name: "つつく", + effect: "鋭く とがった くちばしや つので 相手を 突いて 攻撃する。" + }, + drillPeck: { + name: "ドリルくちばし", + effect: "回転しながら とがった くちばしを 相手に 突き刺して 攻撃する。" + }, + submission: { + name: "じごくぐるま", + effect: "地面に 自分ごと 相手を 投げつけて 攻撃する。 自分も 少し ダメージを 受ける。" + }, + lowKick: { + name: "けたぐり", + effect: "足を 強く けり 相手を 転ばせて 攻撃する。 相手が 重いほど 威力が あがる。" + }, + counter: { + name: "カウンター", + effect: "相手から 受けた 物理攻撃の ダメージを 2倍に して 同じ 相手に 返す。" + }, + seismicToss: { + name: "ちきゅうなげ", + effect: "引力を 使い 投げとばす。 自分の レベルと 同じ ダメージを 相手に 与える。" + }, + strength: { + name: "かいりき", + effect: "こん身の 力で 相手を なぐりつけて 攻撃する。" + }, + absorb: { + name: "すいとる", + effect: "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" + }, + megaDrain: { + name: "メガドレイン", + effect: "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" + }, + leechSeed: { + name: "やどりぎのタネ", + effect: "植えつけた 相手の HPを 毎ターン 少しだけ 吸い取り 自分の HPを 回復する。" + }, + growth: { + name: "せいちょう", + effect: "体を 一気に 大きく 生長させて 攻撃と 特攻を あげる。" + }, + razorLeaf: { + name: "はっぱカッター", + effect: "はっぱを とばして 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" + }, + solarBeam: { + name: "ソーラービーム", + effect: "1ターン目に 光を いっぱいに 集め 2ターン目に 光の 束を 発射して 攻撃する。" + }, + poisonPowder: { + name: "どくのこな", + effect: "毒の ある 粉を たくさん ふりまいて 相手を 毒状態に する。" + }, + stunSpore: { + name: "しびれごな", + effect: "しびれる 粉を たくさん ふりまいて 相手を まひ状態に する。" + }, + sleepPowder: { + name: "ねむりごな", + effect: "眠くなる 粉を たくさん ふりまいて 相手を 眠り状態に する。" + }, + petalDance: { + name: "はなびらのまい", + effect: "2ー3ターンの 間 花を まきちらして 相手を 攻撃する。 まきちらした あとは 混乱する。" + }, + stringShot: { + name: "いとをはく", + effect: "口から 吹きだした 糸を まきつけて 相手の 素早さを がくっと さげる。" + }, + dragonRage: { + name: "りゅうのいかり", + effect: "怒りの 衝撃波を 相手に ぶつけて 攻撃する。 40の ダメージを 決まって 与える。" + }, + fireSpin: { + name: "ほのおのうず", + effect: "激しく 渦をまく 炎の中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + }, + thunderShock: { + name: "でんきショック", + effect: "電気の 刺激を 相手に 浴びせて 攻撃する。 まひ状態に することが ある。" + }, + thunderbolt: { + name: "10まんボルト", + effect: "強い 電撃を 相手に 浴びせて 攻撃する。 まひ状態に することが ある。" + }, + thunderWave: { + name: "でんじは", + effect: "弱い 電撃を 浴びせることで 相手を まひ状態に する。" + }, + thunder: { + name: "かみなり", + effect: "激しい 雷を 相手に 落として 攻撃する。 まひ状態に することが ある。" + }, + rockThrow: { + name: "いわおとし", + effect: "小さな 岩を 持ちあげて 相手に 投げつけて 攻撃する。" + }, + earthquake: { + name: "じしん", + effect: "地震の 衝撃で 自分の 周りに いるものを 攻撃する。" + }, + fissure: { + name: "じわれ", + effect: "地割れの 裂け目に 相手を 落として 攻撃する。 当たれば 一撃で ひんしに する。" + }, + dig: { + name: "あなをほる", + effect: "1ターン目に 潜り 2ターン目で 相手を 攻撃する。" + }, + toxic: { + name: "どくどく", + effect: "相手を 猛毒の 状態に する。 ターンが すすむほど 毒の ダメージが 増えていく。" + }, + confusion: { + name: "ねんりき", + effect: "弱い 念力を 相手に 送って 攻撃する。 相手を 混乱させることが ある。" + }, + psychic: { + name: "サイコキネシス", + effect: "強い 念力を 相手に 送って 攻撃する。 相手の 特防を さげることが ある。" + }, + hypnosis: { + name: "さいみんじゅつ", + effect: "眠気を 誘う 暗示を かけて 相手を 眠り状態に する。" + }, + meditate: { + name: "ヨガのポーズ", + effect: "眠っている 力を 体の 奥から ひきだして 自分の 攻撃を あげる。" + }, + agility: { + name: "こうそくいどう", + effect: "力を ぬいて 体を 軽くして 高速で 動く。 自分の 素早さを ぐーんと あげる。" + }, + quickAttack: { + name: "でんこうせっか", + effect: "目にも 留まらぬ ものすごい 速さで 相手に つっこむ。 必ず 先制攻撃 できる。" + }, + rage: { + name: "いかり", + effect: "技を だしたときに 攻撃を 受けると 怒りの 力で 攻撃が あがる。" + }, + teleport: { + name: "テレポート", + effect: "ひかえの ポケモンが いるときに 使うと 入れ替わる。 野生の ポケモンは 逃げてしまう。" + }, + nightShade: { + name: "ナイトヘッド", + effect: "恐ろしい 幻を みせて 自分の レベルと 同じだけの ダメージを 相手に 与える。" + }, + mimic: { + name: "ものまね", + effect: "相手が 最後に 使った 技を 戦闘の あいだ 自分の 技に することが できる。" + }, + screech: { + name: "いやなおと", + effect: "おもわず 耳を ふさぎたくなる いやなおとを だして 相手の 防御を がくっと さげる。" + }, + doubleTeam: { + name: "かげぶんしん", + effect: "素早い 動きで 分身を つくり 相手を まどわせて 回避率を あげる。" + }, + recover: { + name: "じこさいせい", + effect: "細胞を 再生させて 自分の 最大HPの 半分の HPを 回復する。" + }, + harden: { + name: "かたくなる", + effect: "全身に 力を こめて 体を 硬くして 自分の 防御を あげる。" + }, + minimize: { + name: "ちいさくなる", + effect: "体を ちぢめて 小さく みせて 自分の 回避率を ぐーんと あげる。" + }, + smokescreen: { + name: "えんまく", + effect: "煙や 墨などを 吹きかけて 相手の 命中率を さげる。" + }, + confuseRay: { + name: "あやしいひかり", + effect: "怪しい 光を 相手に みせて まどわせる。 相手を 混乱させる。" + }, + withdraw: { + name: "からにこもる", + effect: "殻に 潜りこんで 身を守り 自分の 防御を あげる。" + }, + defenseCurl: { + name: "まるくなる", + effect: "体を まるめて ちぢこまり 自分の 防御を あげる。" + }, + barrier: { + name: "バリアー", + effect: "頑丈な 壁を つくって 自分の 防御を ぐーんと あげる。" + }, + lightScreen: { + name: "ひかりのかべ", + effect: "5ターンの 間 不思議な かべで 相手から 受ける 特殊攻撃の ダメージを 弱める。" + }, + haze: { + name: "くろいきり", + effect: "黒い霧を だして 戦闘に でている ポケモン 全員の 能力変化を もとに もどす。" + }, + reflect: { + name: "リフレクター", + effect: "5ターンの 間 不思議な かべで 相手から 受ける 物理攻撃の ダメージを 弱める。" + }, + focusEnergy: { + name: "きあいだめ", + effect: "深く 息を 吸い 気合を こめる。 自分の 攻撃が 急所に 当たりやすくなる。" + }, + bide: { + name: "がまん", + effect: "2ターンの 間 攻撃に たえて 受けた ダメージを 2倍にして 相手に 返す。" + }, + metronome: { + name: "ゆびをふる", + effect: "指をふり 自分の 脳を 刺激して すべての 技の なかから どれか 1つを くりだす。" + }, + mirrorMove: { + name: "オウムがえし", + effect: "相手の 使った 技を まねして 自分も 同じ技を 使う。" + }, + selfDestruct: { + name: "じばく", + effect: "爆発を おこして 自分の 周りに いるものを 攻撃する。 使ったあとに ひんしに なる。" + }, + eggBomb: { + name: "タマゴばくだん", + effect: "大きな タマゴを 力いっぱい 相手に 投げつけて 攻撃する。" + }, + lick: { + name: "したでなめる", + effect: "長い 舌で 相手を なめまわして 攻撃する。 まひ状態に することが ある。" + }, + smog: { + name: "スモッグ", + effect: "汚れた ガスを 相手に 吹きつけて 攻撃する。 毒状態に することが ある。" + }, + sludge: { + name: "ヘドロこうげき", + effect: "汚い ヘドロを 相手に 投げつけて 攻撃する。 毒状態に することが ある。" + }, + boneClub: { + name: "ホネこんぼう", + effect: "手に 持った ホネで 相手を なぐりつけて 攻撃する。 相手を ひるませることが ある。" + }, + fireBlast: { + name: "だいもんじ", + effect: "大の字の 炎で 相手を 焼きつくす。 やけど状態に することが ある。" + }, + waterfall: { + name: "たきのぼり", + effect: "すごい 勢いで 相手に つっこむ。 相手を ひるませることが ある。" + }, + clamp: { + name: "からではさむ", + effect: "とても 頑丈な ぶあつい 殻に 4ー5ターンの 間 相手を はさんで 攻撃する。" + }, + swift: { + name: "スピードスター", + effect: "星型の 光を 発射して 相手を 攻撃する。 攻撃は 必ず 命中する。" + }, + skullBash: { + name: "ロケットずつき", + effect: "1ターン目に 頭を ひっこめて 防御を あげる。 2ターン目に 相手を 攻撃する。" + }, + spikeCannon: { + name: "とげキャノン", + effect: "鋭い ハリを 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + }, + constrict: { + name: "からみつく", + effect: "触手や ツタなどを からみつけて 攻撃する。相手の 素早さを さげることが ある。" + }, + amnesia: { + name: "ドわすれ", + effect: "頭を からにして 一瞬 なにかを 忘れることで 自分の 特防を ぐーんと あげる。" + }, + kinesis: { + name: "スプーンまげ", + effect: "スプーンを まげて 注意を ひき 相手の 命中率を さげる。" + }, + softBoiled: { + name: "タマゴうみ", + effect: "最大HPの 半分 自分の HPを 回復する。" + }, + highJumpKick: { + name: "とびひざげり", + effect: "ジャンプからの ひざげりで 相手を 攻撃する。 はずすと 自分が ダメージを 受ける。" + }, + glare: { + name: "へびにらみ", + effect: "おなかの 模様で おびえさせて 相手を まひの 状態に する。" + }, + dreamEater: { + name: "ゆめくい", + effect: "寝ている 相手の 夢を 食べて 攻撃する。 ダメージの 半分の HPを 回復する。" + }, + poisonGas: { + name: "どくガス", + effect: "毒ガスを 相手の 顔に 吹きかけて 毒の 状態に する。" + }, + barrage: { + name: "たまなげ", + effect: "まるい ものを 相手に 投げつけて 攻撃する。 2ー5回の 間 連続で だす。" + }, + leechLife: { + name: "きゅうけつ", + effect: "血を 吸い取って 相手を 攻撃する。 与えた ダメージの 半分の HPを 回復できる。" + }, + lovelyKiss: { + name: "あくまのキッス", + effect: "恐ろしい 顔で キスを せまる。 相手を 眠り状態に する。" + }, + skyAttack: { + name: "ゴッドバード", + effect: "2ターン目に 相手を 攻撃する。 たまに ひるませる。 急所にも 当たりやすい。" + }, + transform: { + name: "へんしん", + effect: "相手の ポケモンに 変身することで 相手と まったく 同じ 技が 使える。" + }, + bubble: { + name: "あわ", + effect: "無数の 泡を 相手に 吹きかけて 攻撃する。 相手の 素早さを さげることが ある。" + }, + dizzyPunch: { + name: "ピヨピヨパンチ", + effect: "リズミカルに パンチを くりだして 相手を 攻撃する。 混乱させることが ある。" + }, + spore: { + name: "キノコのほうし", + effect: "催眠効果の ある 胞子を パラパラと ふりまき 相手を 眠り状態に する。" + }, + flash: { + name: "フラッシュ", + effect: "まぶしい 光で 相手の 命中率を さげる。" + }, + psywave: { + name: "サイコウェーブ", + effect: "不思議な 念波を 相手に 発射して 攻撃する。 使うたびに ダメージが 変わる。" + }, + splash: { + name: "はねる", + effect: "攻撃もせずに ピョン ピョンと 跳ねるだけで なにも おこらない……。" + }, + acidArmor: { + name: "とける", + effect: "細胞の 変化で 液状に なり 自分の 防御を ぐーんと あげる。" + }, + crabhammer: { + name: "クラブハンマー", + effect: "大きな ハサミを 相手に たたきつけて 攻撃する。 急所に 当たりやすい。" + }, + explosion: { + name: "だいばくはつ", + effect: "大きな 爆発で 自分の 周りに いるものを 攻撃する。 使ったあとに ひんしに なる。" + }, + furySwipes: { + name: "みだれひっかき", + effect: "ツメや カマなどで 相手を ひっかいて 攻撃する。 2ー5回の 間 連続で だす。" + }, + bonemerang: { + name: "ホネブーメラン", + effect: "手に 持った ホネを 相手に 投げつけ 行きと 帰りの 2回連続で ダメージを 与える。" + }, + rest: { + name: "ねむる", + effect: "2ターンの 間 眠り続ける。 自分の HPと 状態異常を すべて 回復する。" + }, + rockSlide: { + name: "いわなだれ", + effect: "大きな 岩を 激しく ぶつけて 攻撃する。 相手を ひるませることが ある。" + }, + hyperFang: { + name: "ひっさつまえば", + effect: "鋭い 前歯で 強く かみついて 攻撃する。 相手を ひるませることが ある。" + }, + sharpen: { + name: "かくばる", + effect: "体の かどを 増やして カクカクに なることで 自分の 攻撃を あげる。" + }, + conversion: { + name: "テクスチャー", + effect: "自分の タイプを おぼえている 技で 一番 上の 技と 同じ タイプに する。" + }, + triAttack: { + name: "トライアタック", + effect: "3つの 光線で 攻撃する。 まひか やけどか こおり状態の どれかに することが ある。" + }, + superFang: { + name: "いかりのまえば", + effect: "鋭い 前歯で 激しく かみついて 攻撃する。 相手の HPは 半分に なる。" + }, + slash: { + name: "きりさく", + effect: "ツメや カマなどで 相手を 切り裂いて 攻撃する。 急所に 当たりやすい。" + }, + substitute: { + name: "みがわり", + effect: "自分の HPを 少し 削って 分身を だす。 分身は 自分の 身代わりに なる。" + }, + struggle: { + name: "わるあがき", + effect: "自分の PPが なくなると あがいて 相手を 攻撃する。 自分も 少し ダメージを 受ける。" + }, + sketch: { + name: "スケッチ", + effect: "相手が 使った 技を 自分の ものに する。 1回 使うと スケッチは 消える。" + }, + tripleKick: { + name: "トリプルキック", + effect: "3回連続で キックを くりだして 攻撃する。 技が 当たるたびに 威力は あがる。" + }, + thief: { + name: "どろぼう", + effect: "攻撃と 同時に 道具を 盗もうとする。 盗む 可能性は 30%。" + }, + spiderWeb: { + name: "クモのす", + effect: "ネバネバした 細い 糸を グルグルと からませて 相手を 戦闘から 逃げられなくする。" + }, + mindReader: { + name: "こころのめ", + effect: "相手の 動きを 心で 感じて 次の 攻撃が 必ず 相手に 当たるように する。" + }, + nightmare: { + name: "あくむ", + effect: "眠り状態の 相手に 悪夢を みせて 毎ターン 少しずつ HPを 減らしていく。" + }, + flameWheel: { + name: "かえんぐるま", + effect: "炎を まとい 相手に 突進して 攻撃する。 やけど状態に することが ある。" + }, + snore: { + name: "いびき", + effect: "自分が 寝ているときに 雑音を だして 攻撃する。 相手を ひるませることが ある。" + }, + curse: { + name: "のろい", + effect: "使う ポケモンが ゴーストタイプと それ以外 とでは 効果が 変わる。" + }, + flail: { + name: "じたばた", + effect: "じたばた 暴れて 攻撃する。 自分の HPが 少ないほど 技の 威力は あがる。" + }, + conversion2: { + name: "テクスチャー2", + effect: "相手が 最後に 使った技に 抵抗できる ように 自分の タイプを 変化させる。" + }, + aeroblast: { + name: "エアロブラスト", + effect: "空気の 渦を 発射して 攻撃する。 急所に 当たりやすい。" + }, + cottonSpore: { + name: "わたほうし", + effect: "綿のような フワフワの 胞子を まとわり つかせて 相手の 素早さを がくっと さげる。" + }, + reversal: { + name: "きしかいせい", + effect: "力を ふりしぼり 攻撃する。 自分の HPが 少ないほど 技の 威力は あがる。" + }, + spite: { + name: "うらみ", + effect: "相手が 最後に 使った技に 恨みを 抱いて その技の PPを 4だけ 減らす。" + }, + powderSnow: { + name: "こなゆき", + effect: "冷たい 粉雪を 相手に 吹きつけて 攻撃する。 こおり状態に することが ある。" + }, + protect: { + name: "まもる", + effect: "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" + }, + machPunch: { + name: "マッハパンチ", + effect: "目にも 留まらぬ ものすごい 速さで パンチを くりだす。 必ず 先制攻撃 できる。" + }, + scaryFace: { + name: "こわいかお", + effect: "恐ろしい 顔で にらみ おびえさせて 相手の 素早さを がくっと さげる。" + }, + feintAttack: { + name: "だましうち", + effect: "さりげなく 相手に ちかづき 油断した すきを みて なぐりつける。 攻撃は 必ず 命中する。" + }, + sweetKiss: { + name: "てんしのキッス", + effect: "天使のように かわいく キスして 相手を 混乱させる。" + }, + bellyDrum: { + name: "はらだいこ", + effect: "自分の HPを 最大HPの 半分 減らして 自分の 攻撃を 最大に あげる。" + }, + sludgeBomb: { + name: "ヘドロばくだん", + effect: "汚い ヘドロを 相手に 投げつけて 攻撃する。 毒状態に することが ある。" + }, + mudSlap: { + name: "どろかけ", + effect: "相手の 顔などに 泥を 投げつけて 攻撃する。 命中率を さげる。" + }, + octazooka: { + name: "オクタンほう", + effect: "相手の 顔などに 墨を 吹きかけて 攻撃する。 命中率を さげることが ある。" + }, + spikes: { + name: "まきびし", + effect: "相手の 足下に まきびしを しかける。交代で でてきた 相手の ポケモンに ダメージを 与える。" + }, + zapCannon: { + name: "でんじほう", + effect: "大砲の ような 電気を 発射して 攻撃する。 相手を まひの 状態に する。" + }, + foresight: { + name: "みやぶる", + effect: "ゴーストタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" + }, + destinyBond: { + name: "みちづれ", + effect: "技のあと 相手の 攻撃で ひんしに なると 攻撃 相手も ひんしにする。 連続して 出すと 失敗する。" + }, + perishSong: { + name: "ほろびのうた", + effect: "歌を 聴いた ポケモンは 3ターン たつと ひんしに なる。 交代すると 効果は なくなる。" + }, + icyWind: { + name: "こごえるかぜ", + effect: "凍てつく 冷気を 相手に 吹きつけて 攻撃する。 相手の 素早さを さげる。" + }, + detect: { + name: "みきり", + effect: "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" + }, + boneRush: { + name: "ボーンラッシュ", + effect: "硬い ホネで 相手を なぐりつけて 攻撃する。 2ー5回の 間 連続で だす。" + }, + lockOn: { + name: "ロックオン", + effect: "照準を しっかり あわせて 次の 攻撃が 必ず 相手に 当たるように する。" + }, + outrage: { + name: "げきりん", + effect: "2ー3ターンの 間 暴れまくって 相手を 攻撃する。 暴れたあとは 混乱する。" + }, + sandstorm: { + name: "すなあらし", + effect: "5ターンの 間 砂あらしで いわ じめん はがねタイプ 以外に ダメージ。 いわタイプの 特防が あがる。" + }, + gigaDrain: { + name: "ギガドレイン", + effect: "養分を 吸い取り 攻撃する。 相手に 与えた ダメージの 半分の HPを 回復できる。" + }, + endure: { + name: "こらえる", + effect: "攻撃を 受けても HPを 必ず 1だけ 残せる。 連続で だすと 失敗しやすい。" + }, + charm: { + name: "あまえる", + effect: "かわいく みつめて 油断を 誘い 相手の 攻撃を がくっと さげる。" + }, + rollout: { + name: "ころがる", + effect: "5ターンの 間 転がり続けて 攻撃する。 技が 当たるたびに 威力が あがる。" + }, + falseSwipe: { + name: "みねうち", + effect: "相手の HPが 必ず 1だけ 残るように 手加減して 攻撃する。" + }, + swagger: { + name: "いばる", + effect: "相手を 怒らせて 混乱させる。 怒りで 相手の 攻撃は ぐーんと あがってしまう。" + }, + milkDrink: { + name: "ミルクのみ", + effect: "最大HPの 半分 自分の HPを 回復する。" + }, + spark: { + name: "スパーク", + effect: "電気を まとい 相手に 突進して 攻撃する。 まひ状態に することが ある。" + }, + furyCutter: { + name: "れんぞくぎり", + effect: "カマや ツメなどで 相手を 切りつけて 攻撃する。 連続で 当てると 威力が あがる。" + }, + steelWing: { + name: "はがねのつばさ", + effect: "硬い 翼を 相手に たたきつけて 攻撃する。 自分の 防御が あがることが ある。" + }, + meanLook: { + name: "くろいまなざし", + effect: "吸いこまれるような 黒い まなざしで じっと みつめて 相手を 戦闘から 逃げられなくする。" + }, + attract: { + name: "メロメロ", + effect: "♂なら♀を ♀なら♂を 誘惑して メロメロに する。 相手は 技が だしにくくなる。" + }, + sleepTalk: { + name: "ねごと", + effect: "自分が おぼえている 技の うち どれか 1つを くりだす。 自分が 寝ているときだけ 使える。" + }, + healBell: { + name: "いやしのすず", + effect: "心地好い 鈴の 音色を 聞かせて 味方 全員の 状態異常を 回復 する。" + }, + return: { + name: "おんがえし", + effect: "トレーナーの ために 全力で 相手を 攻撃する。 なついているほど 威力は あがる。" + }, + present: { + name: "プレゼント", + effect: "わなを しかけた 箱を 相手に わたして 攻撃する。HPが 回復して しまうことも ある。" + }, + frustration: { + name: "やつあたり", + effect: "不満を はらすため 全力で 相手を 攻撃する。 なついていないほど 威力は あがる。" + }, + safeguard: { + name: "しんぴのまもり", + effect: "5ターンの 間 不思議な 力に 守られて 状態異常に ならなくなる。" + }, + painSplit: { + name: "いたみわけ", + effect: "自分の HPと 相手の HPを あわせて それを 自分と 相手で なかよく わける。" + }, + sacredFire: { + name: "せいなるほのお", + effect: "神秘の 炎で 相手を 焼きつくして 攻撃する。 やけど状態に することが ある。" + }, + magnitude: { + name: "マグニチュード", + effect: "地面を 揺らして 自分の 周りに いるものを 攻撃する。 技の 威力は いろいろ 変わる。" + }, + dynamicPunch: { + name: "ばくれつパンチ", + effect: "こん身の 力で パンチを くりだして 攻撃する。 相手を 必ず 混乱させる。" + }, + megahorn: { + name: "メガホーン", + effect: "硬くて りっぱな つので おもいっきり 相手を 突き刺して 攻撃する。" + }, + dragonBreath: { + name: "りゅうのいぶき", + effect: "ものすごい 息を 相手に 吹きつけて 攻撃する。 まひ状態に することが ある。" + }, + batonPass: { + name: "バトンタッチ", + effect: "控えの ポケモンと 入れ替わる。 能力変化は 替わった ポケモンが そのまま 受けつぐ。" + }, + encore: { + name: "アンコール", + effect: "相手に アンコールした 技を 3回 続けて 出させる。" + }, + pursuit: { + name: "おいうち", + effect: "相手 ポケモンが 入れ替わるときに 技を だしていると 倍の 威力で 攻撃できる。" + }, + rapidSpin: { + name: "こうそくスピン", + effect: "回転して 相手を 攻撃する。 しめつける まきつく やどりぎのタネ など 吹きとばす。自分の 素早さも あがる。" + }, + sweetScent: { + name: "あまいかおり", + effect: "香りで 相手の 回避率を がくっと さげる。" + }, + ironTail: { + name: "アイアンテール", + effect: "硬い しっぽで 相手を たたきつけて 攻撃する。 相手の 防御を さげることが ある。" + }, + metalClaw: { + name: "メタルクロー", + effect: "鋼鉄の ツメで 相手を 切り裂いて 攻撃する。 自分の 攻撃が あがることが ある。" + }, + vitalThrow: { + name: "あてみなげ", + effect: "相手より あとに 攻撃する。 そのかわり 自分の 攻撃は 必ず 命中する。" + }, + morningSun: { + name: "あさのひざし", + effect: "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" + }, + synthesis: { + name: "こうごうせい", + effect: "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" + }, + moonlight: { + name: "つきのひかり", + effect: "自分の HPを 回復する。 天気に よって 回復の 量が 変化する。" + }, + hiddenPower: { + name: "めざめるパワー", + effect: "技を 使った ポケモンに よって 技の タイプが 変わる。" + }, + crossChop: { + name: "クロスチョップ", + effect: "両手チョップを 相手に たたきつけて 攻撃する。 急所に 当たりやすい。" + }, + twister: { + name: "たつまき", + effect: "竜巻を おこして 相手を まきこみ 攻撃する。 相手を ひるませることが ある。" + }, + rainDance: { + name: "あまごい", + effect: "5ターンの 間 雨を 降らせて みずタイプの 威力を あげる。 ほのおタイプの 威力は さがる。" + }, + sunnyDay: { + name: "にほんばれ", + effect: "5ターンの 間 日差しを 強くして ほのおタイプの 威力を あげる。 みずタイプの 威力は さがる。" + }, + crunch: { + name: "かみくだく", + effect: "鋭い 歯で 相手を かみくだいて 攻撃する。 相手の 防御を さげることが ある。" + }, + mirrorCoat: { + name: "ミラーコート", + effect: "相手から 受けた 特殊攻撃の ダメージを 2倍に して その相手に 返す。" + }, + psychUp: { + name: "じこあんじ", + effect: "自分に 暗示を かけることで 能力変化の 状態を 相手と 同じにする。" + }, + extremeSpeed: { + name: "しんそく", + effect: "目にも 留まらぬ ものすごい 速さで 相手に 突進して 攻撃する。 必ず 先制攻撃 できる。" + }, + ancientPower: { + name: "げんしのちから", + effect: "原始の 力で 攻撃する。 自分の すべての 能力が あがることが ある。" + }, + shadowBall: { + name: "シャドーボール", + effect: "黒い影の 塊を 投げつけて 攻撃する。 相手の 特防を さげることが ある。" + }, + futureSight: { + name: "みらいよち", + effect: "技を 使った 2ターン後に 相手に 念力の 塊を 送って 攻撃する。" + }, + rockSmash: { + name: "いわくだき", + effect: "パンチで 攻撃する。相手の 防御を さげる ことが ある。" + }, + whirlpool: { + name: "うずしお", + effect: "激しく 渦をまく 水の中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + }, + beatUp: { + name: "ふくろだたき", + effect: "味方 全員で 攻撃する。 仲間の ポケモンが 多いほど 技の 攻撃回数が 増える。" + }, + fakeOut: { + name: "ねこだまし", + effect: "先制攻撃で 相手を ひるませる。 戦闘に でたら すぐに ださないと 成功しない。" + }, + uproar: { + name: "さわぐ", + effect: "3ターンの 間 騒いで 相手を 攻撃する。 そのあいだは だれも 眠れなくなる。" + }, + stockpile: { + name: "たくわえる", + effect: "力を 蓄えて 自分の 防御と 特防を あげる。 最大 3回まで 蓄えられる。" + }, + spitUp: { + name: "はきだす", + effect: "蓄えた 力を 相手に ぶつけて 攻撃する。 蓄えているほど 威力が あがる。" + }, + swallow: { + name: "のみこむ", + effect: "蓄えた 力を のみこんで 自分の HPを 回復する。 蓄えているほど 回復する。" + }, + heatWave: { + name: "ねっぷう", + effect: "熱い 息を 相手に 吹きつけて 攻撃する。 やけど状態に することが ある。" + }, + hail: { + name: "あられ", + effect: "5ターンの 間 あられを 降らして こおりタイプで ない ポケモン 全員に ダメージを 与える。" + }, + torment: { + name: "いちゃもん", + effect: "相手に いちゃもんを つけて 同じ 技を 2回連続で だせなくする。" + }, + flatter: { + name: "おだてる", + effect: "相手を おだてて 混乱させる。 同時に 相手の 特攻も あげてしまう。" + }, + willOWisp: { + name: "おにび", + effect: "不気味で 怪しい 炎を 放って 相手を やけどの 状態に する。" + }, + memento: { + name: "おきみやげ", + effect: "自分は ひんしに なるが そのかわりに 相手の 攻撃と 特攻を がくっと さげる。" + }, + facade: { + name: "からげんき", + effect: "自分が 毒 まひ やけど 状態のとき 相手に くりだすと 技の 威力が 2倍に なる。" + }, + focusPunch: { + name: "きあいパンチ", + effect: "精神を 高めて パンチを くりだす。 技を だすまでに 攻撃を 受けると 失敗する。" + }, + smellingSalts: { + name: "きつけ", + effect: "まひ状態の 相手には 威力が 2倍に なるが かわりに 相手の まひが 治る。" + }, + followMe: { + name: "このゆびとまれ", + effect: "自分に 注目させて 相手からの 攻撃を すべて 自分に むけさせる。" + }, + naturePower: { + name: "しぜんのちから", + effect: "自然の 力で 攻撃する。 使う 場所で でてくる 技が 変化する。" + }, + charge: { + name: "じゅうでん", + effect: "次の ターンに だす でんきタイプの 技の 威力を あげる。 自分の 特防も あがる。" + }, + taunt: { + name: "ちょうはつ", + effect: "相手を 怒らせる。 3ターンの 間 相手は ダメージを 与える 技しか だせなくなる。" + }, + helpingHand: { + name: "てだすけ", + effect: "仲間を 助ける。 てだすけ された ポケモンの 技の 威力は いつもより 大きくなる。" + }, + trick: { + name: "トリック", + effect: "相手の すきを ついて 自分と 相手の 持ち物を 交換する。" + }, + rolePlay: { + name: "なりきり", + effect: "相手に なりきって 自分も 相手と 同じ 特性に 変化する。" + }, + wish: { + name: "ねがいごと", + effect: "次の ターンに 自分 もしくは 入れ替わった ポケモンの HPを 最大HPの 半分 回復する。" + }, + assist: { + name: "ねこのて", + effect: "大急ぎで 味方の 助けを かりて 味方の ポケモンが おぼえている 技を どれか 1つ 使う。" + }, + ingrain: { + name: "ねをはる", + effect: "大地に 根を 張り 毎ターン 自分の HPを 回復する。 根を 張っているので 入れ替えられない。" + }, + superpower: { + name: "ばかぢから", + effect: "すごい 力を 発揮して 相手を 攻撃する。自分の 攻撃と 防御が さがる。" + }, + magicCoat: { + name: "マジックコート", + effect: "状態異常に なる 技や やどりぎのタネ などを だされたとき 相手に 跳ね返す。" + }, + recycle: { + name: "リサイクル", + effect: "戦闘中に 使って なくなった 自分の 持ち物を 再生させて 使えるように する。" + }, + revenge: { + name: "リベンジ", + effect: "相手から 技を 受けていると その相手に 対して 与える ダメージが 2倍に なる。" + }, + brickBreak: { + name: "かわらわり", + effect: "手刀を 勢いよく 振りおろして 相手を 攻撃する。 ひかりのかべや リフレクター なども 破壊できる。" + }, + yawn: { + name: "あくび", + effect: "大きな あくびで 眠気を 誘う。 次の ターンに 相手を 眠り状態に する。" + }, + knockOff: { + name: "はたきおとす", + effect: "相手の 持ち物を はたき 落として 戦闘が 終わるまで 使えなくする。 物を持つ 相手には ダメージが増す。" + }, + endeavor: { + name: "がむしゃら", + effect: "相手の HPが 自分の HPと 同じくらいに なるように ダメージを 与える。" + }, + eruption: { + name: "ふんか", + effect: "怒りを 爆発させて 相手を 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" + }, + skillSwap: { + name: "スキルスワップ", + effect: "超能力で 自分の 特性と 相手の 特性を 入れ替える。" + }, + imprison: { + name: "ふういん", + effect: "相手が 自分と 同じ 技を おぼえていたら 相手だけ その技を 使えなくする。" + }, + refresh: { + name: "リフレッシュ", + effect: "体を やすめて 自分が おっている 毒 まひ やけどの 状態異常を 治す。" + }, + grudge: { + name: "おんねん", + effect: "相手の 技で ひんしに されたとき おんねんを かけて その技の PPを 0に する。" + }, + snatch: { + name: "よこどり", + effect: "相手が 使おうと した 回復技や 能力変化の 技を うばって 自分に 使う。" + }, + secretPower: { + name: "ひみつのちから", + effect: "使う場所で 追加効果が 変化する 攻撃。" + }, + dive: { + name: "ダイビング", + effect: "1ターン目で 潜り 2ターン目に 浮きあがって 攻撃する。" + }, + armThrust: { + name: "つっぱり", + effect: "ひらいた 両手で 相手を つっぱって 攻撃する。 2ー5回の 間 連続で だす。" + }, + camouflage: { + name: "ほごしょく", + effect: "水辺や 草むら どうくつなど いる 場所に あわせて 自分の タイプを 変える。" + }, + tailGlow: { + name: "ほたるび", + effect: "点滅する 光を 眺めて 自分の 精神を 統一し 特攻を ぐぐーんと あげる。" + }, + lusterPurge: { + name: "ラスターパージ", + effect: "まばゆい 光を 解放して 攻撃する。 相手の 特防を さげることが ある。" + }, + mistBall: { + name: "ミストボール", + effect: "霧状の 羽毛で 包みこみ 攻撃する。 相手の 特攻を さげることが ある。" + }, + featherDance: { + name: "フェザーダンス", + effect: "羽毛を ふりまいて 相手の 体に からませる。 相手の 攻撃を がくっと さげる。" + }, + teeterDance: { + name: "フラフラダンス", + effect: "フラフラと ダンスを おどって 自分の 周りに いるものを 混乱状態に させる。" + }, + blazeKick: { + name: "ブレイズキック", + effect: "攻撃した 相手を やけど状態に することが ある。 急所にも 当たりやすい。" + }, + mudSport: { + name: "どろあそび", + effect: "あたりを 泥まみれにする。 5ターンの 間 でんきタイプの 技を 弱める。" + }, + iceBall: { + name: "アイスボール", + effect: "5ターンの 間 相手を 攻撃する。 技が 当たるたび 威力が あがる。" + }, + needleArm: { + name: "ニードルアーム", + effect: "トゲの 腕を 激しく ふるって 攻撃する。 相手を ひるませることが ある。" + }, + slackOff: { + name: "なまける", + effect: "怠けて やすむ。 自分の HPを 最大HPの 半分 回復する。" + }, + hyperVoice: { + name: "ハイパーボイス", + effect: "うるさく 響く 大きな 振動を 相手に 与えて 攻撃する。" + }, + poisonFang: { + name: "どくどくのキバ", + effect: "毒の ある キバで 相手に かみついて 攻撃する。 猛毒を おわせる ことが ある。" + }, + crushClaw: { + name: "ブレイククロー", + effect: "硬く 鋭い ツメで 切り裂いて 攻撃する。 相手の 防御を さげることが ある。" + }, + blastBurn: { + name: "ブラストバーン", + effect: "爆発の 炎で 相手を 焼きつくして 攻撃する。 次の ターンは 動けなくなる。" + }, + hydroCannon: { + name: "ハイドロカノン", + effect: "水の 大砲を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" + }, + meteorMash: { + name: "コメットパンチ", + effect: "すい星の ごとく パンチを くりだして 相手を 攻撃する。 自分の 攻撃が あがることが ある。" + }, + astonish: { + name: "おどろかす", + effect: "大きな 声などで 不意に 驚かして 攻撃する。 相手を ひるませることが ある。" + }, + weatherBall: { + name: "ウェザーボール", + effect: "使ったときの 天気に よって 技の タイプと 威力が 変わる。" + }, + aromatherapy: { + name: "アロマセラピー", + effect: "心地好い やすらぐ 香りを かがせて 味方全員の 状態異常を 回復する。" + }, + fakeTears: { + name: "うそなき", + effect: "ないた ふりをして 涙を 流す。 こまらせる ことで 相手の 特防を がくっと さげる。" + }, + airCutter: { + name: "エアカッター", + effect: "鋭い 風で 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" + }, + overheat: { + name: "オーバーヒート", + effect: "フルパワーで 相手を 攻撃する。 使うと 反動で 自分の 特攻が がくっと さがる。" + }, + odorSleuth: { + name: "かぎわける", + effect: "ゴーストタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" + }, + rockTomb: { + name: "がんせきふうじ", + effect: "岩石を 投げつけて 攻撃する。 相手の 動きを 封じることで 素早さを さげる。" + }, + silverWind: { + name: "ぎんいろのかぜ", + effect: "風に りんぷんを のせて 相手を 攻撃する。自分の すべての 能力が あがることが ある。" + }, + metalSound: { + name: "きんぞくおん", + effect: "金属を こすって でるような いやな 音を 聞かせる。 相手の 特防を がくっと さげる。" + }, + grassWhistle: { + name: "くさぶえ", + effect: "心地好い 笛の 音色を 聞かせて 相手を 眠りの 状態に する。" + }, + tickle: { + name: "くすぐる", + effect: "体を くすぐり 笑わせる ことで 相手の 攻撃と 防御を さげる。" + }, + cosmicPower: { + name: "コスモパワー", + effect: "宇宙から 神秘の 力を とりこむ ことで 自分の 防御と 特防を あげる。" + }, + waterSpout: { + name: "しおふき", + effect: "潮を 吹きつけて 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" + }, + signalBeam: { + name: "シグナルビーム", + effect: "不思議な 光を 発射して 攻撃する。 相手を 混乱させることが ある。" + }, + shadowPunch: { + name: "シャドーパンチ", + effect: "影に まぎれて パンチを くりだす。 攻撃は 必ず 命中する。" + }, + extrasensory: { + name: "じんつうりき", + effect: "みえない 不思議な 力を 送って 攻撃する。 相手を ひるませることが ある。" + }, + skyUppercut: { + name: "スカイアッパー", + effect: "空に むかうような 高い アッパーで 相手を 突きあげて 攻撃する。" + }, + sandTomb: { + name: "すなじごく", + effect: "激しく 吹きあれる 砂あらしの 中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + }, + sheerCold: { + name: "ぜったいれいど", + effect: "相手を 一撃で 瀕死に する。 こおりタイプ 以外の ポケモンが 使うと 当たりにくい。" + }, + muddyWater: { + name: "だくりゅう", + effect: "濁った 水を 相手に 発射して 攻撃する。 命中率を さげることが ある。" + }, + bulletSeed: { + name: "タネマシンガン", + effect: "タネを 勢いよく 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + }, + aerialAce: { + name: "つばめがえし", + effect: "素早い 動きで 相手を ほんろうして 切りつける。 攻撃は 必ず 命中する。" + }, + icicleSpear: { + name: "つららばり", + effect: "鋭い 氷柱を 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + }, + ironDefense: { + name: "てっぺき", + effect: "皮膚を 鉄のように 硬くする ことで 自分の 防御を ぐーんと あげる。" + }, + block: { + name: "とおせんぼう", + effect: "両手を ひろげて たちはだかり 相手の 逃げ道を ふさいで 逃げられなくする。" + }, + howl: { + name: "とおぼえ", + effect: "大声で ほえて 気合を 高め 自分と 味方の 攻撃を あげる。" + }, + dragonClaw: { + name: "ドラゴンクロー", + effect: "鋭く とがった 巨大な ツメで 相手を 切り裂いて 攻撃する。" + }, + frenzyPlant: { + name: "ハードプラント", + effect: "大きな 樹木で 相手を たたきつけて 攻撃する。 次の ターンは 動けなくなる。" + }, + bulkUp: { + name: "ビルドアップ", + effect: "体に 力を こめて 筋肉を ぶあつく することで 自分の 攻撃と 防御を あげる。" + }, + bounce: { + name: "とびはねる", + effect: "空高く 飛び跳ねて 2ターン目に 相手を 攻撃する。 まひ状態に することが ある。" + }, + mudShot: { + name: "マッドショット", + effect: "泥の 塊を 相手に 投げつけて 攻撃する。 同時に 相手の 素早さを さげる。" + }, + poisonTail: { + name: "ポイズンテール", + effect: "しっぽで たたく。 毒状態に することが あり 急所にも 当たりやすい。" + }, + covet: { + name: "ほしがる", + effect: "かわいく あまえながら 相手に ちかづき 持っている 道具を うばおうとする。 うばう 可能性は 30%。" + }, + voltTackle: { + name: "ボルテッカー", + effect: "電気を まとって 突進する。 自分も かなり ダメージを 受ける。 まひ状態に することが ある。" + }, + magicalLeaf: { + name: "マジカルリーフ", + effect: "相手を 追跡する 不思議な はっぱを まきちらす。 攻撃は 必ず 命中する。" + }, + waterSport: { + name: "みずあそび", + effect: "あたりを 水で びしょびしょにする。 5ターンの 間 ほのおタイプの 技を 弱める。" + }, + calmMind: { + name: "めいそう", + effect: "静かに 精神を 統一し 心を 鎮めることで 自分の 特攻と 特防を あげる。" + }, + leafBlade: { + name: "リーフブレード", + effect: "はっぱを 剣のように あやつり 相手を 切りつけて 攻撃する。 急所に 当たりやすい。" + }, + dragonDance: { + name: "りゅうのまい", + effect: "神秘的で 力強い 舞を 激しく おどる。 自分の 攻撃と 素早さを あげる。" + }, + rockBlast: { + name: "ロックブラスト", + effect: "硬い 岩石を 相手に 発射して 攻撃する。 2ー5回の 間 連続で だす。" + }, + shockWave: { + name: "でんげきは", + effect: "電撃を 素早く 相手に 浴びせる。 攻撃は 必ず 命中する。" + }, + waterPulse: { + name: "みずのはどう", + effect: "水の 振動を 相手に 与えて 攻撃する。 相手を 混乱させることが ある。" + }, + doomDesire: { + name: "はめつのねがい", + effect: "技を 使った 2ターン後に 無数の 光の 束で 相手を 攻撃する。" + }, + psychoBoost: { + name: "サイコブースト", + effect: "フルパワーで 相手を 攻撃する。 使うと 反動で 自分の 特攻が がくっと さがる。" + }, + roost: { + name: "はねやすめ", + effect: "地面に 降りて 体を やすめる。 最大HPの 半分の HPを 回復する。" + }, + gravity: { + name: "じゅうりょく", + effect: "5ターンの間 ふゆうや ひこうタイプに じめんタイプの 技が 当たるようになる。 空中に 飛ぶ 技も 使えない。" + }, + miracleEye: { + name: "ミラクルアイ", + effect: "あくタイプに 効果がない 技や 回避率の 高い 相手に 攻撃が 当たるように なる。" + }, + wakeUpSlap: { + name: "めざましビンタ", + effect: "眠り状態の 相手に 大きな ダメージを 与える。 かわりに 相手は 眠りから さめる。" + }, + hammerArm: { + name: "アームハンマー", + effect: "強くて 重い こぶしを ふるって ダメージを 与える。 自分の 素早さが さがる。" + }, + gyroBall: { + name: "ジャイロボール", + effect: "体を 高速に 回転させて 体当たりする。相手より 素早さが 低いほど 強い。" + }, + healingWish: { + name: "いやしのねがい", + effect: "自分は ひんしに なるが 控えから でてくる ポケモンの 状態異常と HPを 回復する。" + }, + brine: { + name: "しおみず", + effect: "相手が HPの 半分くらい きずを おっていると 技の 威力が 2倍に なる。" + }, + naturalGift: { + name: "しぜんのめぐみ", + effect: "きのみから 力を もらい 攻撃する。持たせた きのみで 技の タイプと 威力が 変わる。" + }, + feint: { + name: "フェイント", + effect: "まもるや みきり などを している 相手に 攻撃が できる。 守りの 効果を 解除させる。" + }, + pluck: { + name: "ついばむ", + effect: "くちばしで 攻撃。 相手が きのみを 持っているとき 食べて きのみの 効果を 受けられる。" + }, + tailwind: { + name: "おいかぜ", + effect: "激しく 吹きあれる 風の渦を つくり 4ターンの 間 味方 全員の 素早さを あげる。" + }, + acupressure: { + name: "つぼをつく", + effect: "つぼおしで 体を 活性化させる。 能力の どれか 1つを ぐーんと あげる。" + }, + metalBurst: { + name: "メタルバースト", + effect: "技を だす前に 最後に 受けた 技の ダメージを 大きくして だした 相手に 返す。" + }, + uTurn: { + name: "とんぼがえり", + effect: "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" + }, + closeCombat: { + name: "インファイト", + effect: "守りを 捨てて 相手の ふところに 突撃する。 自分の 防御と 特防が さがる。" + }, + payback: { + name: "しっぺがえし", + effect: "ためこんで 攻撃する。 相手より あとに 攻撃できると 技の 威力は 2倍に なる。" + }, + assurance: { + name: "ダメおし", + effect: "そのターンに 相手が すでに ダメージを 受けていたら 技の 威力は 2倍に なる。" + }, + embargo: { + name: "さしおさえ", + effect: "持たせた 道具を 5ターンの 間 使えなくする。 トレーナーも その ポケモンには 道具を 使えない。" + }, + fling: { + name: "なげつける", + effect: "持たせた 道具を 素早く 投げつけて 攻撃する。 道具で 威力と 効果が 変わる。" + }, + psychoShift: { + name: "サイコシフト", + effect: "超能力で 暗示を かけて 自分の 受けている 状態異常を 相手に うつす。" + }, + trumpCard: { + name: "きりふだ", + effect: "きりふだの 残り PPが 少なければ 少ないほど 技の 威力が あがる。" + }, + healBlock: { + name: "かいふくふうじ", + effect: "5ターンの 間 技や 特性や 持っている 道具によって HPを 回復 できなくする。" + }, + wringOut: { + name: "しぼりとる", + effect: "強く 締めあげて 攻撃を する。 相手の HPが 残っているほど 威力は あがる。" + }, + powerTrick: { + name: "パワートリック", + effect: "超能力で 自分の 攻撃と 防御の 力を 交換する。" + }, + gastroAcid: { + name: "いえき", + effect: "胃液を 相手の 体に 吐きつける。 ついた 胃液は 相手の 特性の 効果を 消す。" + }, + luckyChant: { + name: "おまじない", + effect: "天に むかって おいのりを ささげ 5ターンの 間 相手の 攻撃を 急所に 当たらなくする。" + }, + meFirst: { + name: "さきどり", + effect: "威力を あげて 相手が だそうとする 技を 先にだす。 先に だせないと 失敗する。" + }, + copycat: { + name: "まねっこ", + effect: "直前に でた 技を まねして 同じ 技を だす。 技が でていないと 失敗する。" + }, + powerSwap: { + name: "パワースワップ", + effect: "超能力で 自分と 相手の 攻撃と 特攻の 能力変化を 入れ替える。" + }, + guardSwap: { + name: "ガードスワップ", + effect: "超能力で 自分と 相手の 防御と 特防の 能力変化を 入れ替える。" + }, + punishment: { + name: "おしおき", + effect: "能力変化で 相手が パワーアップ しているほど 技の 威力が あがる。" + }, + lastResort: { + name: "とっておき", + effect: "戦闘中に おぼえている 技を すべて 使うと はじめて だせる とっておきの 技。" + }, + worrySeed: { + name: "なやみのタネ", + effect: "心を なやませる タネを 植えつける。 相手を 眠れなくして 特性を ふみんに する。" + }, + suckerPunch: { + name: "ふいうち", + effect: "相手より 先に 攻撃 できる。 相手が だす技が 攻撃技でないと 失敗する。" + }, + toxicSpikes: { + name: "どくびし", + effect: "相手の 足下に どくびしを しかける。 交代で でてきた 相手の ポケモンに 毒を おわせる。" + }, + heartSwap: { + name: "ハートスワップ", + effect: "超能力で 自分と 相手に かかっている 能力変化を 入れ替える。" + }, + aquaRing: { + name: "アクアリング", + effect: "自分の 体の 周りを 水で つくった ベールで おおう。 毎ターン HPを 回復する。" + }, + magnetRise: { + name: "でんじふゆう", + effect: "電気で つくった 磁力の 力で 宙に 浮かぶ。 5ターンの 間 浮遊できる。" + }, + flareBlitz: { + name: "フレアドライブ", + effect: "炎を まとって 突進する。 自分も かなり ダメージを 受ける。 やけど状態に することが ある。" + }, + forcePalm: { + name: "はっけい", + effect: "相手の 体に 衝撃波を 当てて 攻撃する。 まひ状態に することが ある。" + }, + auraSphere: { + name: "はどうだん", + effect: "体の 奥から 波導の 力を 相手に うち放つ。 攻撃は 必ず 命中する。" + }, + rockPolish: { + name: "ロックカット", + effect: "自分の 体を 磨いて 空気の 抵抗を 少なくする。素早さを ぐーんと あげることが できる。" + }, + poisonJab: { + name: "どくづき", + effect: "毒に そまった 触手や 腕で 相手を 突き刺す。 毒状態に することが ある。" + }, + darkPulse: { + name: "あくのはどう", + effect: "体から 悪意に みちた 恐ろしい オーラを 発する。 相手を ひるませることが ある。" + }, + nightSlash: { + name: "つじぎり", + effect: "一瞬の すきを ついて 相手を 切りはらう。 急所に 当たりやすい。" + }, + aquaTail: { + name: "アクアテール", + effect: "激しく あれくるう 荒波の ように 大きな しっぽを ふって 相手を 攻撃する。" + }, + seedBomb: { + name: "タネばくだん", + effect: "硬い 殻を もつ 大きな タネを 上から たたきつけて 相手を 攻撃する。" + }, + airSlash: { + name: "エアスラッシュ", + effect: "空をも 切り裂く 空気の 刃で 攻撃する。 相手を ひるませることが ある。" + }, + xScissor: { + name: "シザークロス", + effect: "カマや ツメを ハサミのように 交差させながら 相手を 切り裂く。" + }, + bugBuzz: { + name: "むしのさざめき", + effect: "振動で 音波を おこして 攻撃する。相手の 特防を さげることが ある。" + }, + dragonPulse: { + name: "りゅうのはどう", + effect: "大きな 口から 衝撃波を まきおこして 相手を 攻撃する。" + }, + dragonRush: { + name: "ドラゴンダイブ", + effect: "すさまじい 殺気で 威圧しながら 体当たりする。 相手を ひるませることが ある。" + }, + powerGem: { + name: "パワージェム", + effect: "宝石のように きらめく 光を 発射して 相手を 攻撃する。" + }, + drainPunch: { + name: "ドレインパンチ", + effect: "こぶしから 相手の 力を 吸い取る。 与えた ダメージの 半分の HPを 回復できる。" + }, + vacuumWave: { + name: "しんくうは", + effect: "こぶしを ふって 真空の 波を まきおこす。 必ず 先制攻撃できる。" + }, + focusBlast: { + name: "きあいだま", + effect: "気合を 高めて ありったけの 力を 放出する。 相手の 特防を さげることが ある。" + }, + energyBall: { + name: "エナジーボール", + effect: "自然から 集めた 命の力を 発射する。 相手の 特防を さげることがある。" + }, + braveBird: { + name: "ブレイブバード", + effect: "はねを おりたたみ 低空飛行で 突撃する。 自分も かなり ダメージを 受ける。" + }, + earthPower: { + name: "だいちのちから", + effect: "相手の 足下へ 大地の力を 放出する。相手の 特防を さげることが ある。" + }, + switcheroo: { + name: "すりかえ", + effect: "目にも とまらぬ 速さで 自分と 相手の 持ち物を 交換する。" + }, + gigaImpact: { + name: "ギガインパクト", + effect: "持てる 力を すべて 使って 相手に 突撃する。 次の ターンは 動けなくなる。" + }, + nastyPlot: { + name: "わるだくみ", + effect: "悪いことを 考えて 頭を 活性化させる。 自分の 特攻を ぐーんと あげる。" + }, + bulletPunch: { + name: "バレットパンチ", + effect: "弾丸の ような 速くて 硬い パンチを 相手に くりだす。 必ず 先制攻撃 できる。" + }, + avalanche: { + name: "ゆきなだれ", + effect: "相手から 技を 受けていると その 相手に 対して 技の 威力が 2倍に なる。" + }, + iceShard: { + name: "こおりのつぶて", + effect: "氷の塊を 一瞬で つくり 相手に 素早く 放つ。 必ず 先制攻撃 できる。" + }, + shadowClaw: { + name: "シャドークロー", + effect: "影から つくった 鋭い ツメで 相手を 切り裂く。 急所に 当たりやすい。" + }, + thunderFang: { + name: "かみなりのキバ", + effect: "電気を ためた キバで かみつく。 相手を ひるませたり まひ状態に することが ある。" + }, + iceFang: { + name: "こおりのキバ", + effect: "冷気を ひめた キバで かみつく。 相手を ひるませたり こおり状態に することが ある。" + }, + fireFang: { + name: "ほのおのキバ", + effect: "炎を まとった キバで かみつく。 相手を ひるませたり やけど状態に することが ある。" + }, + shadowSneak: { + name: "かげうち", + effect: "影を のばして 相手の 背後から 攻撃する。 必ず 先制攻撃 できる。" + }, + mudBomb: { + name: "どろばくだん", + effect: "硬い 泥の 弾を 相手に 発射して 攻撃する。 命中率を さげることが ある。" + }, + psychoCut: { + name: "サイコカッター", + effect: "実体化させた 心の 刃で 相手を 切り裂く。 急所に 当たりやすい。" + }, + zenHeadbutt: { + name: "しねんのずつき", + effect: "思念の 力を 額に 集めて 攻撃する。 相手を ひるませることが ある。" + }, + mirrorShot: { + name: "ミラーショット", + effect: "磨きあげられた 体から せん光の 力を 相手に 放つ。 命中率を さげることが ある。" + }, + flashCannon: { + name: "ラスターカノン", + effect: "体の 光を 一点に 集めて 力を 放つ。 相手の 特防を さげることが ある。" + }, + rockClimb: { + name: "ロッククライム", + effect: "すごい 勢いで 相手に つっこみ 攻撃する。 相手を 混乱させることが ある。" + }, + defog: { + name: "きりばらい", + effect: "強い風で 相手の リフレクターや ひかりのかべ などを はらいのける。 回避率も さげる。" + }, + trickRoom: { + name: "トリックルーム", + effect: "まか不思議な 空間を つくる。 5ターンの 間 遅い ポケモンから 行動できる。" + }, + dracoMeteor: { + name: "りゅうせいぐん", + effect: "天空から 隕石を 相手に 落とす。使うと 反動で 自分の 特攻が がくっと さがる。" + }, + discharge: { + name: "ほうでん", + effect: "まばゆい 電撃で 自分の 周りに いるものを 攻撃する。 まひ状態に することが ある。" + }, + lavaPlume: { + name: "ふんえん", + effect: "真っ赤な 炎で 自分の 周りに いるものを 攻撃する。 やけど状態に することが ある。" + }, + leafStorm: { + name: "リーフストーム", + effect: "とがった はっぱで 相手に あらしを おこす。使うと 反動で 自分の 特攻が がくっと さがる。" + }, + powerWhip: { + name: "パワーウィップ", + effect: "ツタや 触手を 激しく ふるって 相手を たたきつけ 攻撃する。" + }, + rockWrecker: { + name: "がんせきほう", + effect: "巨大な 岩を 相手に 発射して 攻撃する。 次の ターンは 動けなくなる。" + }, + crossPoison: { + name: "クロスポイズン", + effect: "毒の 刃で 相手を 切り裂く。 毒状態に することが あり 急所にも 当たりやすい。" + }, + gunkShot: { + name: "ダストシュート", + effect: "汚い ゴミを 相手に ぶつけて 攻撃する。 毒状態に することが ある。" + }, + ironHead: { + name: "アイアンヘッド", + effect: "鋼の ような 硬い 頭で 攻撃する。 相手を ひるませることが ある。" + }, + magnetBomb: { + name: "マグネットボム", + effect: "相手に 吸いつく 鋼の 爆弾を 発射する。 攻撃は 必ず 命中 する。" + }, + stoneEdge: { + name: "ストーンエッジ", + effect: "とがった 岩を 相手に 突き刺して 攻撃する。 急所に 当たりやすい。" + }, + captivate: { + name: "ゆうわく", + effect: "♂なら♀を ♀なら♂を 誘惑して 相手の 特攻を がくっと さげる。" + }, + stealthRock: { + name: "ステルスロック", + effect: "相手の 周りに 無数の 岩を 浮かべて 交代で でてきた 相手の ポケモンに ダメージを 与える。" + }, + grassKnot: { + name: "くさむすび", + effect: "草を からませて 相手を 転ばせる。相手が 重いほど 威力が あがる。" + }, + chatter: { + name: "おしゃべり", + effect: "とても うるさい おしゃべりの 音波で 相手を 攻撃する。 相手を 混乱させる。" + }, + judgment: { + name: "さばきのつぶて", + effect: "無数の 光弾を 相手に 放出する。 自分の 持つ プレートに より タイプが 変わる。" + }, + bugBite: { + name: "むしくい", + effect: "かみついて 攻撃する。 相手が きのみを 持っているとき 食べて きのみの 効果を 受けられる。" + }, + chargeBeam: { + name: "チャージビーム", + effect: "電撃の 束を 相手に 発射する。電気を ためて 自分の 特攻を あげることが ある。" + }, + woodHammer: { + name: "ウッドハンマー", + effect: "硬い 胴体を 相手に たたきつけて 攻撃する。 自分も かなり ダメージを 受ける。" + }, + aquaJet: { + name: "アクアジェット", + effect: "目にも 留まらぬ ものすごい 速さで 相手に つっこむ。 必ず 先制攻撃 できる。" + }, + attackOrder: { + name: "こうげきしれい", + effect: "しもべを 呼びだして 相手に むかって 攻撃させる。 急所に 当たりやすい。" + }, + defendOrder: { + name: "ぼうぎょしれい", + effect: "しもべを 呼びだして 自分の 体に おおい つかせる。防御と 特防を あげることが できる。" + }, + healOrder: { + name: "かいふくしれい", + effect: "しもべを 呼びだして きずを 治す。 最大HPの 半分 自分の HPを 回復する。" + }, + headSmash: { + name: "もろはのずつき", + effect: "命を 懸けて こん身の 力で 相手に ずつきを する。 自分も ものすごい ダメージを 受ける。" + }, + doubleHit: { + name: "ダブルアタック", + effect: "しっぽなどを 使い 相手を たたいて 攻撃する。 2回連続で ダメージを 与える。" + }, + roarOfTime: { + name: "ときのほうこう", + effect: "時間が ゆがむほどの 力を うちだして 相手を 攻撃する。 次の ターンは 動けなくなる。" + }, + spacialRend: { + name: "あくうせつだん", + effect: "周りの 空間ごと 相手を 引き裂き ダメージを 与える。 急所に 当たりやすい。" + }, + lunarDance: { + name: "みかづきのまい", + effect: "自分は ひんしに なるが 控えから でてくる ポケモンの すべての 状態を 回復する。" + }, + crushGrip: { + name: "にぎりつぶす", + effect: "すさまじい 力で 相手を にぎりつぶす。 相手の HPが 残っているほど 威力が あがる。" + }, + magmaStorm: { + name: "マグマストーム", + effect: "激しく 燃えたぎる 炎の なかに 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + }, + darkVoid: { + name: "ダークホール", + effect: "暗黒の 世界に ひきずり 落として 相手を 眠り状態に する。" + }, + seedFlare: { + name: "シードフレア", + effect: "体の 中から 衝撃波を 発生させる。相手の 特防を がくっと さげることが ある。" + }, + ominousWind: { + name: "あやしいかぜ", + effect: "みのけも よだつ 突風で 相手を 攻撃する。自分の すべての 能力が あがることが ある。" + }, + shadowForce: { + name: "シャドーダイブ", + effect: "1ターン目で 姿を 消して 2ターン目に 相手を 攻撃する。 守っていても 攻撃は 当たる。" + }, + honeClaws: { + name: "つめとぎ", + effect: "ツメを 磨いて 鋭く する。 自分の 攻撃と 命中率を あげる。" + }, + wideGuard: { + name: "ワイドガード", + effect: "味方全員に 当たる 攻撃を 1ターンの 間 防ぐ。" + }, + guardSplit: { + name: "ガードシェア", + effect: "超能力で 自分と 相手の 防御と 特防を たして 半分に わける。" + }, + powerSplit: { + name: "パワーシェア", + effect: "超能力で 自分と 相手の 攻撃と 特攻を たして 半分に わける。" + }, + wonderRoom: { + name: "ワンダールーム", + effect: "まか不思議な 空間を つくる。 5ターンのあいだ すべてのポケモンの 防御と 特防が 入れ替わる。" + }, + psyshock: { + name: "サイコショック", + effect: "不思議な 念波を 実体化して 相手を 攻撃する。 物理的な ダメージを 与える。" + }, + venoshock: { + name: "ベノムショック", + effect: "特殊な 毒液を 浴びせかける。 毒状態の 相手には 威力が 2倍に なる。" + }, + autotomize: { + name: "ボディパージ", + effect: "体の ムダな 部分を 削る。 自分の 素早さを ぐーんと あげて 体重も 軽くなる。" + }, + ragePowder: { + name: "いかりのこな", + effect: "イライラさせる 粉を 自分に ふりかけて 注意を ひく。 相手の 攻撃を すべて 自分に むける。" + }, + telekinesis: { + name: "テレキネシス", + effect: "超能力で 相手を 浮かせる。 3ターンの 間 攻撃が 相手に 当たりやすく なる。" + }, + magicRoom: { + name: "マジックルーム", + effect: "まか不思議な 空間を つくる。 5ターンの間 すべてのポケモンの 道具の 効果が なくなる。" + }, + smackDown: { + name: "うちおとす", + effect: "石や 弾を 投げて 飛んでいる 相手を 攻撃する。 相手は うち落とされて 地面に 落ちる。" + }, + stormThrow: { + name: "やまあらし", + effect: "強烈な 一撃を 相手に くりだす。攻撃は 必ず 急所に 当たる。" + }, + flameBurst: { + name: "はじけるほのお", + effect: "当たると はじける 炎で 相手を 攻撃する。はじけた 炎は 隣の 相手にも ふりかかる。" + }, + sludgeWave: { + name: "ヘドロウェーブ", + effect: "ヘドロの 波で 自分の 周りに いるものを 攻撃する。 毒状態に することが ある。" + }, + quiverDance: { + name: "ちょうのまい", + effect: "神秘的で 美しい 舞を 軽やかに おどる。 自分の 特攻と 特防と 素早さを あげる。" + }, + heavySlam: { + name: "ヘビーボンバー", + effect: "重たい 体で 相手に ぶつかって 攻撃する。 自分が 相手より 重いほど 威力が あがる。" + }, + synchronoise: { + name: "シンクロノイズ", + effect: "不思議な 電波で 周りに いる 自分と 同じ タイプの ポケモンに ダメージを 与える。" + }, + electroBall: { + name: "エレキボール", + effect: "電気の 塊を 相手に ぶつける。相手より 素早さが 速いほど 威力が あがる。" + }, + soak: { + name: "みずびたし", + effect: "たくさんの 水を 浴びせかけて 相手を みずタイプに する。" + }, + flameCharge: { + name: "ニトロチャージ", + effect: "炎を まとい 相手を 攻撃する。 力を ためて 自分の 素早さを あげる。" + }, + coil: { + name: "とぐろをまく", + effect: "とぐろを まいて 集中する。 自分の 攻撃と 防御と 命中率を あげる。" + }, + lowSweep: { + name: "ローキック", + effect: "素早い 動きで 相手の 足を ねらって 攻撃する。 相手の 素早さを さげる。" + }, + acidSpray: { + name: "アシッドボム", + effect: "相手を とかす 液体を 吐きだして 攻撃する。 相手の 特防を がくっと さげる。" + }, + foulPlay: { + name: "イカサマ", + effect: "相手の 力を 利用する。 戦っている 相手の 攻撃が 高いほど ダメージが あがる。" + }, + simpleBeam: { + name: "シンプルビーム", + effect: "なぞの 念波を 相手に 送る。 念波を 受けとった 相手は 特性が たんじゅんに なる。" + }, + entrainment: { + name: "なかまづくり", + effect: "不思議な リズムで おどる。 動きを まねさせて 自分と 相手の 特性を 同じに する。" + }, + afterYou: { + name: "おさきにどうぞ", + effect: "相手の 行動を サポートして 自分の 行動の あとに 続けて 動けるように する。" + }, + round: { + name: "りんしょう", + effect: "歌で 相手を 攻撃する。 みんなで 輪唱すると 続けて だすことが でき 威力も あがる。" + }, + echoedVoice: { + name: "エコーボイス", + effect: "響く 声で 相手を 攻撃する。 毎ターン だれかが 技を 使い続けると 威力が あがる。" + }, + chipAway: { + name: "なしくずし", + effect: "すきを みて 堅実に 攻撃する。 相手の 能力変化に 関係なく ダメージを 与える。" + }, + clearSmog: { + name: "クリアスモッグ", + effect: "特殊な 泥の 塊を 相手に 投げつけて 攻撃する。 能力変化を もとに もどす。" + }, + storedPower: { + name: "アシストパワー", + effect: "蓄積された パワーで 相手を 攻撃する。自分の 能力が あがっているほど 威力が あがる。" + }, + quickGuard: { + name: "ファストガード", + effect: "自分と 味方を 相手の 先制攻撃から 守る。" + }, + allySwitch: { + name: "サイドチェンジ", + effect: "不思議な 力で テレポートして 自分と 味方の 居場所を 入れ替える。" + }, + scald: { + name: "ねっとう", + effect: "熱く 煮えたぎる 水を 相手に 発射して 攻撃する。 やけど状態に することが ある。" + }, + shellSmash: { + name: "からをやぶる", + effect: "殻を やぶって 自分の 防御 特防を さげるが 攻撃 特攻 素早さを ぐーんと あげる。" + }, + healPulse: { + name: "いやしのはどう", + effect: "いやしのはどうを とばして 最大HPの 半分 相手の HPを 回復する。" + }, + hex: { + name: "たたりめ", + effect: "たたみかける ように 攻撃する。 状態異常の 相手に 大きな ダメージを 与える。" + }, + skyDrop: { + name: "フリーフォール", + effect: "1ターン目で 相手を 空へ 連れさり 2ターン目に 落として 攻撃する。 連れさられた 相手は 動けない。" + }, + shiftGear: { + name: "ギアチェンジ", + effect: "歯車を 回して 自分の 攻撃を あげる だけでなく 素早さも ぐーんと あげる。" + }, + circleThrow: { + name: "ともえなげ", + effect: "相手を 投げとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" + }, + incinerate: { + name: "やきつくす", + effect: "炎で 相手を 攻撃する。 相手が きのみなどを 持っているとき 燃やして 使えなくする。" + }, + quash: { + name: "さきおくり", + effect: "相手を おさえつけて 行動の 順番を 最後に する。" + }, + acrobatics: { + name: "アクロバット", + effect: "軽やかに 相手を 攻撃する。 自分が 道具を 持っていないとき 大きな ダメージを 与える。" + }, + reflectType: { + name: "ミラータイプ", + effect: "相手の タイプを 反射して 自分も 同じ タイプに なる。" + }, + retaliate: { + name: "かたきうち", + effect: "倒れた 味方の かたきを 討つ。 前の ターンに 味方が 倒されていると 威力が あがる。" + }, + finalGambit: { + name: "いのちがけ", + effect: "命懸けで 相手を 攻撃する。 自分は ひんしに なるが 相手に HP分の ダメージを 与える。" + }, + bestow: { + name: "ギフトパス", + effect: "相手が 道具を 持っていないとき 自分が 持っている 道具を 相手に わたす。" + }, + inferno: { + name: "れんごく", + effect: "激しい 炎で 相手を 包みこみ 攻撃する。 やけど状態に する。" + }, + waterPledge: { + name: "みずのちかい", + effect: "水の柱で 攻撃する。 ほのおと 組みあわせると 威力が あがって 空に にじが かかる。" + }, + firePledge: { + name: "ほのおのちかい", + effect: "炎の柱で 攻撃する。 くさと 組みあわせると 威力が あがって 周りが 火の海に なる。" + }, + grassPledge: { + name: "くさのちかい", + effect: "草の柱で 攻撃する。 みずと 組みあわせると 威力が あがって あたりが 湿原に なる。" + }, + voltSwitch: { + name: "ボルトチェンジ", + effect: "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" + }, + struggleBug: { + name: "むしのていこう", + effect: "抵抗して 相手を 攻撃する。 相手の 特攻を さげる。" + }, + bulldoze: { + name: "じならし", + effect: "地面を 踏みならして 自分の 周りに いるものを 攻撃する。 相手の 素早さを さげる。" + }, + frostBreath: { + name: "こおりのいぶき", + effect: "冷たい 息を 相手に 吹きつけて 攻撃する。 必ず 急所に 当たる。" + }, + dragonTail: { + name: "ドラゴンテール", + effect: "相手を はじきとばして 控えの ポケモンを ひきずりだす。 野生の 場合は 戦闘が 終わる。" + }, + workUp: { + name: "ふるいたてる", + effect: "自分を 奮いたてて 攻撃と 特攻を あげる。" + }, + electroweb: { + name: "エレキネット", + effect: "電気の ネットで 相手を 捕まえて 攻撃する。 相手の 素早さを さげる。" + }, + wildCharge: { + name: "ワイルドボルト", + effect: "電気を まとって 相手に ぶつかって 攻撃する。 自分も 少し ダメージを 受ける。" + }, + drillRun: { + name: "ドリルライナー", + effect: "ドリルのように 体を 回転しながら 相手に 体当たりする。 急所に 当たりやすい。" + }, + dualChop: { + name: "ダブルチョップ", + effect: "体の 硬い部分で 相手を たたいて 攻撃する。 2回連続で ダメージを 与える。" + }, + heartStamp: { + name: "ハートスタンプ", + effect: "かわいい しぐさで 油断させて 強烈な 一撃を 浴びせる。 相手を ひるませることが ある。" + }, + hornLeech: { + name: "ウッドホーン", + effect: "つのを 突き刺して 相手の 養分を 吸い取る。 与えた ダメージの 半分の HPを 回復できる。" + }, + sacredSword: { + name: "せいなるつるぎ", + effect: "長い つので 切りつけ 攻撃する。 相手の 能力変化に 関係なく ダメージを 与える。" + }, + razorShell: { + name: "シェルブレード", + effect: "鋭い 貝殻で 切りつけて 攻撃する。 相手の 防御を さげることが ある。" + }, + heatCrash: { + name: "ヒートスタンプ", + effect: "燃える 体で 相手に ぶつかって 攻撃する。 自分が 相手より 重いほど 威力が あがる。" + }, + leafTornado: { + name: "グラスミキサー", + effect: "鋭い はっぱで 相手を 包みこんで 攻撃する。 命中率を さげることが ある。" + }, + steamroller: { + name: "ハードローラー", + effect: "まるめた 体で 回転して 相手を おしつぶす。 相手を ひるませることが ある。" + }, + cottonGuard: { + name: "コットンガード", + effect: "フワフワの 綿毛で 自分の 体を 包みこんで 守る。 防御を ぐぐーんと あげる。" + }, + nightDaze: { + name: "ナイトバースト", + effect: "暗黒の 衝撃波を とばして 相手を 攻撃する。 命中率を さげることが ある。" + }, + psystrike: { + name: "サイコブレイク", + effect: "不思議な 念波を 実体化して 相手を 攻撃する。 物理的な ダメージを 与える。" + }, + tailSlap: { + name: "スイープビンタ", + effect: "硬い しっぽで 相手を たたいて 攻撃する。 2ー5回の 間 連続で だす。" + }, + hurricane: { + name: "ぼうふう", + effect: "強烈な 風で 相手を 包みこんで 攻撃する。 相手を 混乱させることが ある。" + }, + headCharge: { + name: "アフロブレイク", + effect: "すごい アフロの 頭で 相手に 突進して 攻撃する。 自分も 少し ダメージを 受ける。" + }, + gearGrind: { + name: "ギアソーサー", + effect: "鋼鉄の ギアを 相手に 投げつけて 攻撃する。 2回連続で ダメージを 与える。" + }, + searingShot: { + name: "かえんだん", + effect: "真っ赤な 炎で 自分の 周りに いるものを 攻撃する。 やけど状態に することが ある。" + }, + technoBlast: { + name: "テクノバスター", + effect: "光弾を 相手に 放出する。 自分の 持つ カセットにより タイプが 変わる。" + }, + relicSong: { + name: "いにしえのうた", + effect: "いにしえのうたを 相手に 聞かせて 心に うったえて 攻撃する。 眠り状態に することが ある。" + }, + secretSword: { + name: "しんぴのつるぎ", + effect: "長い つので 切りつけ 攻撃する。 つのが まとった 不思議な 力は 物理的な ダメージを 与える。" + }, + glaciate: { + name: "こごえるせかい", + effect: "凍えるような 冷気を 相手に 吹きつけて 攻撃する。 相手の 素早さを さげる。" + }, + boltStrike: { + name: "らいげき", + effect: "ぼうだいな 電気を 身に まとって 相手に 突進して 攻撃する。 まひ状態に することが ある。" + }, + blueFlare: { + name: "あおいほのお", + effect: "美しくも 激しい 青い炎で 相手を 包みこんで 攻撃する。 やけど状態に することが ある。" + }, + fieryDance: { + name: "ほのおのまい", + effect: "炎を まとい はばたいて 相手を 攻撃する。自分の 特攻が あがることが ある。" + }, + freezeShock: { + name: "フリーズボルト", + effect: "電気を まとった 氷の 塊で 2ターン目に 相手を たたきつける。 まひ状態に することが ある。" + }, + iceBurn: { + name: "コールドフレア", + effect: "すべてを 凍らせる 激しい 冷気で 2ターン目に 相手を 包みこむ。 やけど状態に することが ある。" + }, + snarl: { + name: "バークアウト", + effect: "まくしたてる ように 怒鳴りつけて 相手の 特攻を さげる。" + }, + icicleCrash: { + name: "つららおとし", + effect: "大きな 氷柱を 激しく ぶつけて 攻撃する。 相手を ひるませることが ある。" + }, + vCreate: { + name: "Vジェネレート", + effect: "灼熱の 炎を 額から 発生させて 捨て身の 体当たり。 防御 特防 素早さが さがる。" + }, + fusionFlare: { + name: "クロスフレイム", + effect: "巨大な 炎を たたきつける。 巨大な 雷の 影響を受け 技の 威力が あがる。" + }, + fusionBolt: { + name: "クロスサンダー", + effect: "巨大な 雷を たたきつける。 巨大な 炎の 影響を受け 技の 威力が あがる。" + }, + flyingPress: { + name: "フライングプレス", + effect: "空中から 相手に ダイブする。 この技は かくとうタイプと 同時に ひこうタイプでも ある。" + }, + matBlock: { + name: "たたみがえし", + effect: "かえした タタミを 盾にして 自分や 味方への 技の ダメージを 防ぐ。 変化技は 防ぐことが できない。" + }, + belch: { + name: "ゲップ", + effect: "相手に 向かって ゲップを 浴びせて ダメージを 与える。 きのみを 食べないと だせない。" + }, + rototiller: { + name: "たがやす", + effect: "地面を 耕して 草木が 育ちやすいようにする。 くさタイプの 攻撃と 特攻が あがる。" + }, + stickyWeb: { + name: "ねばねばネット", + effect: "相手の 周りに ねばねばした ネットを はりめぐらせ 交代で でてきた 相手の 素早さを さげる。" + }, + fellStinger: { + name: "とどめばり", + effect: "この 技を 使って 相手を 倒すと 攻撃が ぐぐーんと あがる。" + }, + phantomForce: { + name: "ゴーストダイブ", + effect: "1ターンめで どこかに 消えて 2ターンめに 相手を 攻撃する。 守りを 無視して 攻撃できる。" + }, + trickOrTreat: { + name: "ハロウィン", + effect: "相手を ハロウィンに 誘う。 相手の タイプに ゴーストタイプが 追加される。" + }, + nobleRoar: { + name: "おたけび", + effect: "おたけびを あげて 相手を 威嚇し 相手の 攻撃と 特攻を さげる。" + }, + ionDeluge: { + name: "プラズマシャワー", + effect: "電気を 帯びた 粒子を 拡散し ノーマルタイプの 技を でんきタイプに してしまう。" + }, + parabolicCharge: { + name: "パラボラチャージ", + effect: "周りにいる ポケモン 全員に ダメージ。 与えた ダメージの 半分を 自分が 回復する。" + }, + forestsCurse: { + name: "もりののろい", + effect: "相手に 森ののろいを かける。 のろいを かけられた 相手は タイプに くさタイプが 追加される。" + }, + petalBlizzard: { + name: "はなふぶき", + effect: "激しい 花吹雪を 起こし 周りに いるものに 攻撃して ダメージを 与える。" + }, + freezeDry: { + name: "フリーズドライ", + effect: "相手を 急激に 冷やして こおり 状態に することが ある。 みずタイプにも 効果バツグンになる。" + }, + disarmingVoice: { + name: "チャームボイス", + effect: "魅惑の 鳴き声を だして 相手に 精神的な ダメージを 与える。 攻撃は 必ず 命中 する。" + }, + partingShot: { + name: "すてゼリフ", + effect: "すてゼリフで 相手を いかくし 攻撃と 特攻を さげたのち 控えの ポケモンと 入れ替わる。" + }, + topsyTurvy: { + name: "ひっくりかえす", + effect: "相手に かかっている すべての 能力変化を ひっくり返して 逆にする。" + }, + drainingKiss: { + name: "ドレインキッス", + effect: "キッスによって 相手から HPを 吸い取る。 与えた ダメージの 半分以上 HPを 回復する。" + }, + craftyShield: { + name: "トリックガード", + effect: "不思議な 力を 使って 味方への 変化技を 防ぐ。 ダメージ技は 受けてしまう。" + }, + flowerShield: { + name: "フラワーガード", + effect: "不思議な 力を 使って 場にいる くさタイプの ポケモン 全員の 防御を あげる。" + }, + grassyTerrain: { + name: "グラスフィールド", + effect: "5ターンの 間 グラスフィールドにする。 地面にいると 毎ターン 回復する。 くさタイプの 威力が あがる。" + }, + mistyTerrain: { + name: "ミストフィールド", + effect: "5ターンの 間 地面にいると 状態異常に ならず ドラゴン技の ダメージも 半分になる。" + }, + electrify: { + name: "そうでん", + effect: "相手が 技を だす前に そうでん すると そのターン 相手の 技は でんきタイプになる。" + }, + playRough: { + name: "じゃれつく", + effect: "相手に じゃれついて 攻撃する。 相手の 攻撃を さげる ことがある。" + }, + fairyWind: { + name: "ようせいのかぜ", + effect: "ようせいのかぜを 起こし 相手に 吹きつけて 攻撃する。" + }, + moonblast: { + name: "ムーンフォース", + effect: "月の パワーを かりて 相手を 攻撃する。 相手の 特攻を さげる ことがある。" + }, + boomburst: { + name: "ばくおんぱ", + effect: "すさまじい 爆音の 破壊力に よって 周りに いるものを 攻撃する。" + }, + fairyLock: { + name: "フェアリーロック", + effect: "ロックを かけることによって 次のターン すべての ポケモンを 逃げられなくする。" + }, + kingsShield: { + name: "キングシールド", + effect: "相手の 攻撃を 防ぐと 同時に 防御態勢になる。 触れた 相手の 攻撃を さげる。" + }, + playNice: { + name: "なかよくする", + effect: "相手と なかよくなって 戦う 気力を 失わせ 相手の 攻撃を さげる。" + }, + confide: { + name: "ないしょばなし", + effect: "ないしょばなしを することで 相手の 集中力を 失わせ 相手の 特攻を さげる。" + }, + diamondStorm: { + name: "ダイヤストーム", + effect: "ダイヤの 嵐を 巻き起こし ダメージを 与える。 自分の 防御を ぐーんと あげることが ある。" + }, + steamEruption: { + name: "スチームバースト", + effect: "ものすごく 熱い 蒸気を 相手に 浴びせる。 相手は やけどする ことがある。" + }, + hyperspaceHole: { + name: "いじげんホール", + effect: "異次元ホールで 突然 相手の 真横に 現れ 攻撃する。 まもるや みきり なども 無視 できる。" + }, + waterShuriken: { + name: "みずしゅりけん", + effect: "粘液で できた 手裏剣を 2ー5回の 間 連続で だす。 必ず 先制攻撃 できる。" + }, + mysticalFire: { + name: "マジカルフレイム", + effect: "口から 吐きだす 特別 熱い 炎で 攻撃する。 相手の 特攻を さげる。" + }, + spikyShield: { + name: "ニードルガード", + effect: "相手の 攻撃を 防ぐと 同時に 触れた 相手の 体力を 削って しまう。" + }, + aromaticMist: { + name: "アロマミスト", + effect: "不思議な アロマの 香りによって 味方の 特防を あげる。" + }, + eerieImpulse: { + name: "かいでんぱ", + effect: "体から かいでんぱを 放ち 相手に 浴びせる ことによって 特攻を がくっと さげる。" + }, + venomDrench: { + name: "ベノムトラップ", + effect: "特殊な 毒液を 浴びせかける。 毒状態の 相手は 攻撃 特攻 素早さが さがる。" + }, + powder: { + name: "ふんじん", + effect: "ふんじんを 浴びせた 相手が ほのお技を 使うと 爆発して ダメージを 与える。" + }, + geomancy: { + name: "ジオコントロール", + effect: "1ターン目で エネルギーを 吸収し 2ターン目に 特攻 特防 素早さを ぐーんと あげる。" + }, + magneticFlux: { + name: "じばそうさ", + effect: "磁場を 操作 することによって 特性 プラスと マイナスの 防御 特防が あがる。" + }, + happyHour: { + name: "ハッピータイム", + effect: "ハッピータイムの 技を 使うと 戦闘の あとで もらえる お金が 倍になる。" + }, + electricTerrain: { + name: "エレキフィールド", + effect: "5ターンの 間 エレキフィールドにする。 地面にいる ポケモンは 眠らない。 でんきタイプの 威力が あがる。" + }, + dazzlingGleam: { + name: "マジカルシャイン", + effect: "強力な 光を 放ち 相手に ダメージを 与える。" + }, + celebrate: { + name: "おいわい", + effect: "ポケモンが とっても ハッピーな あなたのことを お祝い してくれる。" + }, + holdHands: { + name: "てをつなぐ", + effect: "味方の ポケモン 同士が 手をつなぐ。 とっても 幸せな 気持ちに なれる。" + }, + babyDollEyes: { + name: "つぶらなひとみ", + effect: "つぶらなひとみで 相手を みつめて 攻撃を さげる。 必ず 先制攻撃 できる。" + }, + nuzzle: { + name: "ほっぺすりすり", + effect: "電気を 帯びた ほっぺを すりつけて 攻撃。 相手を まひ状態に する。" + }, + holdBack: { + name: "てかげん", + effect: "手加減 した 攻撃で 相手の HPを 必ず 1だけ 残す。" + }, + infestation: { + name: "まとわりつく", + effect: "4ー5ターンの 間 相手に まとわりついて 攻撃する。 そのあいだ 相手は 逃げられない。" + }, + powerUpPunch: { + name: "グロウパンチ", + effect: "繰り返し 打つことで だんだん こぶしが 固くなる。 相手に 当てると 攻撃が あがる。" + }, + oblivionWing: { + name: "デスウイング", + effect: "ねらいを 定めた 相手から HPを 吸い取る。 与えた ダメージの 半分以上 HPを 回復する。" + }, + thousandArrows: { + name: "サウザンアロー", + effect: "浮いている ポケモンにも 当たる。 浮いていた 相手は 撃ち落とされて 地面に 落ちる。" + }, + thousandWaves: { + name: "サウザンウェーブ", + effect: "地をはう 波によって 攻撃。 波に 巻き込まれた 相手は 戦闘から 逃げられなくなる。" + }, + landsWrath: { + name: "グランドフォース", + effect: "大地の パワーを 集め 力を 相手に 集中させて ダメージを 与える。" + }, + lightOfRuin: { + name: "はめつのひかり", + effect: "永遠の花 の パワーを かりて 強力な 光線を 撃ちだす。 自分も かなりの ダメージを 受ける。" + }, + originPulse: { + name: "こんげんのはどう", + effect: "青白く 輝く 無数の 光線で 相手を 攻撃する。" + }, + precipiceBlades: { + name: "だんがいのつるぎ", + effect: "大地の 力を 刃に 変えて 相手を 攻撃する。" + }, + dragonAscent: { + name: "ガリョウテンセイ", + effect: "大空から 急速落下 して 相手を 攻撃する。 自分の 防御と 特防が さがる。" + }, + hyperspaceFury: { + name: "いじげんラッシュ", + effect: "たくさんの 腕で まもるや みきり などを 無視した 連続攻撃。 自分の 防御が さがる。" + }, + breakneckBlitzPhysical: { + name: "ウルトラダッシュアタック", + effect: "Zパワーで 勢いを つけて 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" + }, + breakneckBlitzSpecial: { + name: "ウルトラダッシュアタック", + effect: "ダミーデータ" + }, + allOutPummelingPhysical: { + name: "ぜんりょくむそうげきれつけん", + effect: "Zパワーで 作った エネルギーの 弾を 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" + }, + allOutPummelingSpecial: { + name: "ぜんりょくむそうげきれつけん", + effect: "ダミーデータ" + }, + supersonicSkystrikePhysical: { + name: "ファイナルダイブクラッシュ", + effect: "Zパワーで 勢いよく 飛びあがり 相手に 向かって 全力で 落下。 元になった 技で 威力が 変わる。" + }, + supersonicSkystrikeSpecial: { + name: "ファイナルダイブクラッシュ", + effect: "ダミーデータ" + }, + acidDownpourPhysical: { + name: "アシッドポイズンデリート", + effect: "Zパワーで 毒の 沼を 湧きあがらせ 全力で 相手を 沈める。 元になった 技で 威力が 変わる。" + }, + acidDownpourSpecial: { + name: "アシッドポイズンデリート", + effect: "ダミーデータ" + }, + tectonicRagePhysical: { + name: "ライジングランドオーバー", + effect: "Zパワーで 地面の 奥深くに 潜り 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" + }, + tectonicRageSpecial: { + name: "ライジングランドオーバー", + effect: "ダミーデータ" + }, + continentalCrushPhysical: { + name: "ワールズエンドフォール", + effect: "Zパワーで 大きな 岩山を 呼びだし 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" + }, + continentalCrushSpecial: { + name: "ワールズエンドフォール", + effect: "ダミーデータ" + }, + savageSpinOutPhysical: { + name: "ぜったいほしょくかいてんざん", + effect: "Zパワーで 吐きだした 糸が 全力で 相手を 縛りつける。 元になった 技で 威力が 変わる。" + }, + savageSpinOutSpecial: { + name: "ぜったいほしょくかいてんざん", + effect: "ダミーデータ" + }, + neverEndingNightmarePhysical: { + name: "むげんあんやへのいざない", + effect: "Zパワーで 呼びよせた 強い 怨念が 全力で 相手に 降りかかる。 元になった 技で 威力が 変わる。" + }, + neverEndingNightmareSpecial: { + name: "むげんあんやへのいざない", + effect: "ダミーデータ" + }, + corkscrewCrashPhysical: { + name: "ちょうぜつらせんれんげき", + effect: "Zパワーで 高速回転を おこない 全力で 相手に ぶつかる。 元になった 技で 威力が 変わる。" + }, + corkscrewCrashSpecial: { + name: "ちょうぜつらせんれんげき", + effect: "ダミーデータ" + }, + infernoOverdrivePhysical: { + name: "ダイナミックフルフレイム", + effect: "Zパワーで 燃えさかる 炎を 吐きだし 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" + }, + infernoOverdriveSpecial: { + name: "ダイナミックフルフレイム", + effect: "ダミーデータ" + }, + hydroVortexPhysical: { + name: "スーパーアクアトルネード", + effect: "Zパワーで 大きな 渦潮を 作り 全力で 相手を 飲みこむ。 元になった 技で 威力が 変わる。" + }, + hydroVortexSpecial: { + name: "スーパーアクアトルネード", + effect: "ダミーデータ" + }, + bloomDoomPhysical: { + name: "ブルームシャインエクストラ", + effect: "Zパワーで 草花の エネルギーを 借り 全力で 相手を 攻撃する。 元になった 技で 威力が 変わる。" + }, + bloomDoomSpecial: { + name: "ブルームシャインエクストラ", + effect: "ダミーデータ" + }, + gigavoltHavocPhysical: { + name: "スパーキングギガボルト", + effect: "Zパワーで 溜めた 強い 電気を 全力で 相手に ぶつける。 元になった 技で 威力が 変わる。" + }, + gigavoltHavocSpecial: { + name: "スパーキングギガボルト", + effect: "ダミーデータ" + }, + shatteredPsychePhysical: { + name: "マキシマムサイブレイカー", + effect: "Zパワーで 相手を 操り 全力で 痛い 思いを させる。 元になった 技で 威力が 変わる。" + }, + shatteredPsycheSpecial: { + name: "マキシマムサイブレイカー", + effect: "ダミーデータ" + }, + subzeroSlammerPhysical: { + name: "レイジングジオフリーズ", + effect: "Zパワーで 気温を 急激に 下げ 全力で 相手を 凍らせる。 元になった 技で 威力が 変わる。" + }, + subzeroSlammerSpecial: { + name: "レイジングジオフリーズ", + effect: "ダミーデータ" + }, + devastatingDrakePhysical: { + name: "アルティメットドラゴンバーン", + effect: "Zパワーで オーラを 実体化し 全力で 相手に 襲いかかる。 元になった 技で 威力が 変わる。" + }, + devastatingDrakeSpecial: { + name: "アルティメットドラゴンバーン", + effect: "ダミーデータ" + }, + blackHoleEclipsePhysical: { + name: "ブラックホールイクリプス", + effect: "Zパワーで 悪の エネルギーを 集め 全力で 相手を 吸いよせる。 元になった 技で 威力が 変わる。" + }, + blackHoleEclipseSpecial: { + name: "ブラックホールイクリプス", + effect: "ダミーデータ" + }, + twinkleTacklePhysical: { + name: "ラブリースターインパクト", + effect: "Zパワーで 魅惑の 空間を 作り 全力で 相手を もてあそぶ。 元になった 技で 威力が 変わる。" + }, + twinkleTackleSpecial: { + name: "ラブリースターインパクト", + effect: "ダミーデータ" + }, + catastropika: { + name: "ひっさつのピカチュート", + effect: "Zパワーで 最大 電力を 身に まとったピカチュウが 全力で 相手に 飛び掛る。" + }, + shoreUp: { + name: "すなあつめ", + effect: "最大HPの 半分 自分の HPを 回復する。 すなあらしの時は 多く 回復。" + }, + firstImpression: { + name: "であいがしら", + effect: "威力が 高い 技 だが 戦闘に 出たら すぐに 出さないと 成功 しない。" + }, + banefulBunker: { + name: "トーチカ", + effect: "相手の 攻撃を 防ぐと 同時に 触れた 相手に 毒を 与えてしまう。" + }, + spiritShackle: { + name: "かげぬい", + effect: "攻撃と 同時に 相手の 影を 縫い付けて 逃げられなくする。" + }, + darkestLariat: { + name: "DDラリアット", + effect: "両腕を 回し 相手に 当てる。 相手の 能力変化に 関係なく ダメージを 与える。" + }, + sparklingAria: { + name: "うたかたのアリア", + effect: "歌うことによって たくさんの バルーンを 放出する。 技を 受けると やけどが 治る。" + }, + iceHammer: { + name: "アイスハンマー", + effect: "強くて 重い こぶしを ふるって ダメージを 与える。 自分の 素早さが さがる。" + }, + floralHealing: { + name: "フラワーヒール", + effect: "最大HPの 半分 相手の HPを 回復する。 グラスフィールドの時 効果が あがる。" + }, + highHorsepower: { + name: "10まんばりき", + effect: "全身を 使って 相手に 猛アタックする。" + }, + strengthSap: { + name: "ちからをすいとる", + effect: "相手の 攻撃力と 同じだけ 自分の HPを 回復する。 そして 相手の 攻撃を さげる。" + }, + solarBlade: { + name: "ソーラーブレード", + effect: "1ターン目に 光を いっぱいに 集め 2ターン目に その 力を 剣に 込めて 攻撃する。" + }, + leafage: { + name: "このは", + effect: "はっぱを 相手に 当てて 攻撃する。" + }, + spotlight: { + name: "スポットライト", + effect: "ポケモンに スポットライトを 当て そのターンに そのポケモンしか 狙えない ようにする。" + }, + toxicThread: { + name: "どくのいと", + effect: "毒の 混じった 糸を 吹き付ける。 相手を 毒にして 素早さを さげる。" + }, + laserFocus: { + name: "とぎすます", + effect: "精神を 集中して 次の 攻撃を 必ず 急所に 当てる。" + }, + gearUp: { + name: "アシストギア", + effect: "ギアを 入れる ことによって 特性 プラスと マイナスの 攻撃と 特攻が あがる。" + }, + throatChop: { + name: "じごくづき", + effect: "この 技を 受けた 相手は 地獄の 苦しみから 2ターンの間 音の 技を 出すことが できなくなる。" + }, + pollenPuff: { + name: "かふんだんご", + effect: "敵には 爆発する だんごを 使って 攻撃。 味方には 回復する だんごを 与える。" + }, + anchorShot: { + name: "アンカーショット", + effect: "アンカーを 相手に からませて 攻撃する。 相手は 逃げることが できなくなる。" + }, + psychicTerrain: { + name: "サイコフィールド", + effect: "5ターンの間 地面にいると 先制技を 受けない。 エスパータイプの 威力が あがる。" + }, + lunge: { + name: "とびかかる", + effect: "全力で 相手に 飛びかかって 攻撃。 相手の 攻撃を さげる。" + }, + fireLash: { + name: "ほのおのムチ", + effect: "焼けたムチで 相手を 打ちつける。 攻撃を 受けた 相手は 防御が さがる。" + }, + powerTrip: { + name: "つけあがる", + effect: "自分の 強さを 鼻高々に 攻撃する。自分の 能力が あがって いるほど 威力が あがる。" + }, + burnUp: { + name: "もえつきる", + effect: "全身の ほのおを すべて 燃やして 大ダメージを 与える。 自分の ほのおタイプが なくなる。" + }, + speedSwap: { + name: "スピードスワップ", + effect: "相手の 素早さと 自分の 素早さを 入れ替えてしまう。" + }, + smartStrike: { + name: "スマートホーン", + effect: "とがった つので 相手を 突き刺して 攻撃する。 攻撃は 必ず 命中する。" + }, + purify: { + name: "じょうか", + effect: "相手の 状態異常を 治す。 治すと 自分は HPを 回復 することが できる。" + }, + revelationDance: { + name: "めざめるダンス", + effect: "全力で 踊って 攻撃する。 この 技の タイプは 自分の タイプと 同じになる。" + }, + coreEnforcer: { + name: "コアパニッシャー", + effect: "ダメージを 与えた 相手が すでに 行動を 終えていたら 相手の 特性を 消してしまう。" + }, + tropKick: { + name: "トロピカルキック", + effect: "南国 由来の 熱い キックを 相手に 浴びせる。 相手の 攻撃を さげる。" + }, + instruct: { + name: "さいはい", + effect: "相手が 出した 技を 指示して もう一度 出させることが できる。" + }, + beakBlast: { + name: "くちばしキャノン", + effect: "最初に クチバシを 加熱してから 攻撃を くりだす。 加熱中に さわると やけどする。" + }, + clangingScales: { + name: "スケイルノイズ", + effect: "全身の うろこを こすり 大きな 音を 出して 攻撃する。 攻撃後 自分の 防御が さがる。" + }, + dragonHammer: { + name: "ドラゴンハンマー", + effect: "体を ハンマーのように 使って 相手に 襲いかかり ダメージを 与える。" + }, + brutalSwing: { + name: "ぶんまわす", + effect: "自分の 体を ぶんまわして 相手に ダメージを 与える。" + }, + auroraVeil: { + name: "オーロラベール", + effect: "5ターンの 間 物理と 特殊の ダメージを 弱める。 ゆきの 時しか 出すことが できない。" + }, + sinisterArrowRaid: { + name: "シャドーアローズストライク", + effect: "Zパワーで 無数の 矢を 作りだした ジュナイパーが 全力で 相手を 射抜く 攻撃。" + }, + maliciousMoonsault: { + name: "ハイパーダーククラッシャー", + effect: "Zパワーで タフな 肉体を 得た ガオガエンが 全力で 相手に ぶつかって 攻撃する。" + }, + oceanicOperetta: { + name: "わだつみのシンフォニア", + effect: "Zパワーで 大量の 水を 呼んだ アシレーヌが 全力で 相手を 攻撃する。" + }, + guardianOfAlola: { + name: "ガーディアン・デ・アローラ", + effect: "Zパワーで アローラの 力を 得た とちがみポケモン 全力の 攻撃。 相手の 残りHPを たくさん 減らす。" + }, + soulStealing7StarStrike: { + name: "しちせいだっこんたい", + effect: "Zパワーを 得た マーシャドーが パンチと キックの 連続技を 全力で 相手に 叩き込む。" + }, + stokedSparksurfer: { + name: "ライトニングサーフライド", + effect: "Zパワーを 得た アローラ地方の ライチュウが 全力で 攻撃する。 相手を まひ 状態に する。" + }, + pulverizingPancake: { + name: "ほんきをだす こうげき", + effect: "Zパワーで 本気を 出した カビゴンが 巨体を 躍動させて 全力で 相手に 襲いかかる。" + }, + extremeEvoboost: { + name: "ナインエボルブースト", + effect: "Zパワーを 得た イーブイが 進化した 仲間たちの 力を 借りて 能力を ぐーんと 上げる。" + }, + genesisSupernova: { + name: "オリジンズスーパーノヴァ", + effect: "Zパワーを 得た ミュウが 全力で 相手を 攻撃する。 足元が サイコフィールドになる。" + }, + shellTrap: { + name: "トラップシェル", + effect: "こうらの トラップを しかける。 相手が 物理技を 出すと 爆発して ダメージを 与える。" + }, + fleurCannon: { + name: "フルールカノン", + effect: "強力な ビームを 放ったあと 自分の 特攻が がくっと さがる。" + }, + psychicFangs: { + name: "サイコファング", + effect: "サイコパワーで かみついて 相手を 攻撃する。 ひかりのかべや リフレクター なども 破壊できる。" + }, + stompingTantrum: { + name: "じだんだ", + effect: "悔しさを バネにして 攻撃する。 前の ターンに 技を 外していると 威力が 倍に なる。" + }, + shadowBone: { + name: "シャドーボーン", + effect: "魂の 宿った ホネで 相手を なぐりつけて 攻撃する。 相手の 防御を さげる ことがある。" + }, + accelerock: { + name: "アクセルロック", + effect: "素早い スピードで 相手に ぶつかって 攻撃する。 必ず 先制攻撃 できる。" + }, + liquidation: { + name: "アクアブレイク", + effect: "水の 力で 相手に ぶつかって 攻撃する。 相手の 防御を さげる ことがある。" + }, + prismaticLaser: { + name: "プリズムレーザー", + effect: "プリズムの 力で 強力な 光線を 発射する。 次の ターンは 動けなくなる。" + }, + spectralThief: { + name: "シャドースチール", + effect: "相手の 影に 潜り込み 相手の 能力アップを 奪って 攻撃する。" + }, + sunsteelStrike: { + name: "メテオドライブ", + effect: "流星の ような 勢いで 突進する。 相手の 特性を 無視して 攻撃 することが できる。" + }, + moongeistBeam: { + name: "シャドーレイ", + effect: "怪しい 光線を 放って 攻撃する。相手の 特性を 無視して 攻撃 することが できる。" + }, + tearfulLook: { + name: "なみだめ", + effect: "なみだめに なって 相手の 戦力を 喪失させる。 相手の 攻撃と 特攻が さがる。" + }, + zingZap: { + name: "びりびりちくちく", + effect: "相手に ぶつかって 強力な 電気を浴びせ びりびりちくちく させる。 相手を ひるませる ことが ある。" + }, + naturesMadness: { + name: "しぜんのいかり", + effect: "自然の 怒りを 相手に ぶつける。 相手の HPは 半分に なる。" + }, + multiAttack: { + name: "マルチアタック", + effect: "高い エネルギーを まといつつ 相手に ぶつかって 攻撃する。 メモリに より タイプが 変わる。" + }, + tenMillionVoltThunderbolt: { + name: "1000まんボルト", + effect: "帽子を かぶった ピカチュウが Zパワーで パワーアップした 電撃を 放つ。 急所に 当たりやすい。" + }, + mindBlown: { + name: "ビックリヘッド", + effect: "自分の 頭を 爆発 させて 周りの すべてを 攻撃する。 自分も ダメージを 受けてしまう。" + }, + plasmaFists: { + name: "プラズマフィスト", + effect: "電気を まとった こぶしで 攻撃。 ノーマルタイプの 技を でんきタイプに してしまう。" + }, + photonGeyser: { + name: "フォトンゲイザー", + effect: "光の 柱で 攻撃する。 攻撃と 特攻を 比べて 高いほうで ダメージを 与える。" + }, + lightThatBurnsTheSky: { + name: "てんこがすめつぼうのひかり", + effect: "ネクロズマが 相手の 特性の 効果を 無視して 攻撃と 特攻の 高い方で ダメージを 与える。" + }, + searingSunrazeSmash: { + name: "サンシャインスマッシャー", + effect: "Zパワーを 得た ソルガレオが 全力で 攻撃する。 相手の 特性の 効果を 無視できる。" + }, + menacingMoonrazeMaelstrom: { + name: "ムーンライトブラスター", + effect: "Zパワーを 得た ルナアーラが 全力で 攻撃する。 相手の 特性の 効果を 無視できる。" + }, + letsSnuggleForever: { + name: "ぽかぼかフレンドタイム", + effect: "Zパワーを 得た ミミッキュが 全力で ぽかぽか 攻撃。" + }, + splinteredStormshards: { + name: "ラジアルエッジストーム", + effect: "Zパワーを 得た ルガルガンが 全力で 攻撃する。 追加で フィールド状態を 打ち消す。" + }, + clangorousSoulblaze: { + name: "ブレイジングソウルビート", + effect: "Zパワーを 得た ジャラランガが 全力で 相手を 攻撃する。 追加で 自分の 能力が 上がる。" + }, + zippyZap: { + name: "ばちばちアクセル", + effect: "猛スピードの 電撃 アタック。 必ず 先制攻撃 できて 急所に あたる。" + }, + splishySplash: { + name: "ざぶざぶサーフ", + effect: "大きな 波に 電気を あびせ 相手に ぶつけて 攻撃する。 まひ状態に することが ある。" + }, + floatyFall: { + name: "ふわふわフォール", + effect: "ふんわりと 浮かび あがり 一気に 急降下して 攻撃。 相手を ひるませることが ある。" + }, + pikaPapow: { + name: "ピカピカサンダー", + effect: "トレーナーへの 大好きな 気持ちが 強いほど 威力が あがる 電撃。 必ず 命中する。" + }, + bouncyBubble: { + name: "いきいきバブル", + effect: "水のかたまりを ぶつけて 攻撃。 水を 吸いとり ダメージの 半分の HPを 回復する。" + }, + buzzyBuzz: { + name: "びりびりエレキ", + effect: "電気を 飛ばし 相手に あびせて 攻撃する。 相手を まひ状態に する。" + }, + sizzlySlide: { + name: "めらめらバーン", + effect: "炎を まとった 体で 勢いよく 相手に ぶつかる。 相手を やけど状態に する。" + }, + glitzyGlow: { + name: "どばどばオーラ", + effect: "念力を これでもかと あびせる。 相手の 特殊攻撃を 弱める 不思議な かべを つくりだす。" + }, + baddyBad: { + name: "わるわるゾーン", + effect: "わるさを アピールして 攻撃。 相手の 物理攻撃を 弱める 不思議な かべを つくりだす。" + }, + sappySeed: { + name: "すくすくボンバー", + effect: "巨大な ツルを 生やし タネを 撒きちらかして 攻撃する。 タネは 毎ターン 相手の HPを 吸いとる。" + }, + freezyFrost: { + name: "こちこちフロスト", + effect: "冷たく 凍った くろいきりの 結晶で 攻撃。 全員の 能力変化を もとに もどす。" + }, + sparklySwirl: { + name: "きらきらストーム", + effect: "むせかえる ような 香りの 竜巻で 相手を つつんで 攻撃。 味方の 状態異常を 回復する。" + }, + veeveeVolley: { + name: "ブイブイブレイク", + effect: "イーブイの トレーナーへの 大好きな 気持ちが 強いほど 威力が あがる 体当たり。 必ず 命中する。" + }, + doubleIronBash: { + name: "ダブルパンツァー", + effect: "胸の ナットを 軸に 回転して 2回 続けて うでを たたきつける。 相手を ひるませる ことが ある。" + }, + maxGuard: { + name: "ダイウォール", + effect: "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。" + }, + dynamaxCannon: { + name: "ダイマックスほう", + effect: "コアから ビームを 放つ。相手の レベルが 過度に 上がっている 場合は 与える ダメージが 最大 2倍に 増える。" + }, + snipeShot: { + name: "ねらいうち", + effect: "相手の 技を 引き受ける 特性や 技の 影響を 無視して 選んだ 相手を 攻撃 できる。" + }, + jawLock: { + name: "くらいつく", + effect: "お互い ひんしに なるまで 交代が できなくなる。 どちらかの ポケモンが いなくなると 効果は消える。" + }, + stuffCheeks: { + name: "ほおばる", + effect: "持っている きのみを 食べて 防御を ぐーんと あげる。" + }, + noRetreat: { + name: "はいすいのじん", + effect: "自分の すべての 能力が 上がるが 交代 したり 逃げることが できなくなる。" + }, + tarShot: { + name: "タールショット", + effect: "ねばねばの タールを 浴びせて 相手の 素早さを 下げる。 相手は ほのおが 弱点に なる。" + }, + magicPowder: { + name: "まほうのこな", + effect: "まほうのこなを 浴びせて 相手を エスパータイプに 変化させる。" + }, + dragonDarts: { + name: "ドラゴンアロー", + effect: "ドラメシヤで 2回 攻撃。 相手が 2匹 いるときは それぞれに 1回ずつ 攻撃する。" + }, + teatime: { + name: "おちゃかい", + effect: "おちゃかいを ひらいて 場にいる ポケモンが それぞれ 持っている きのみを 食べる。" + }, + octolock: { + name: "たこがため", + effect: "相手を 逃げられなくする。 かためられた 相手は 毎ターン 防御と 特防が 下がる。" + }, + boltBeak: { + name: "でんげきくちばし", + effect: "電気を まとった くちばしで 刺す。 相手より 先に 攻撃できると 技の 威力は 2倍に なる。" + }, + fishiousRend: { + name: "エラがみ", + effect: "かたい エラで かみつく。 相手より 先に 攻撃できると 技の 威力は 2倍に なる。" + }, + courtChange: { + name: "コートチェンジ", + effect: "不思議な 力で お互いの 場の 効果を 入れ替える。" + }, + maxFlare: { + name: "ダイバーン", + effect: "ダイマックスした ポケモンが 繰りだす ほのおタイプの 攻撃。 5ターンの 間 日差しを 強くする。" + }, + maxFlutterby: { + name: "ダイワーム", + effect: "ダイマックスした ポケモンが 繰りだす むしタイプの 攻撃。 相手の 特攻を 下げる。" + }, + maxLightning: { + name: "ダイサンダー", + effect: "ダイマックスした ポケモンが 繰りだす でんきタイプの 攻撃。 5ターンの 間 エレキフィールドにする。" + }, + maxStrike: { + name: "ダイアタック", + effect: "ダイマックスした ポケモンが 繰りだす ノーマルタイプの 攻撃。 相手の 素早さを 下げる。" + }, + maxKnuckle: { + name: "ダイナックル", + effect: "ダイマックスした ポケモンが 繰りだす かくとうタイプの 攻撃。 味方の 攻撃を 上げる。" + }, + maxPhantasm: { + name: "ダイホロウ", + effect: "ダイマックスした ポケモンが 繰りだす ゴーストタイプの 攻撃。 相手の 防御を 下げる。" + }, + maxHailstorm: { + name: "ダイアイス", + effect: "ダイマックスした ポケモンが 繰りだす こおりタイプの 攻撃。 5ターンの 間 あられを 降らす。" + }, + maxOoze: { + name: "ダイアシッド", + effect: "ダイマックスした ポケモンが 繰りだす どくタイプの 攻撃。 味方の 特攻を 上げる。" + }, + maxGeyser: { + name: "ダイストリーム", + effect: "ダイマックスした ポケモンが 繰りだす みずタイプの 攻撃。 5ターンの 間 雨を 降らせる。" + }, + maxAirstream: { + name: "ダイジェット", + effect: "ダイマックスした ポケモンが 繰りだす ひこうタイプの 攻撃。 味方の 素早さを 上げる。" + }, + maxStarfall: { + name: "ダイフェアリー", + effect: "ダイマックスした ポケモンが 繰りだす フェアリータイプの 攻撃。 5ターンの 間 ミストフィールドにする。" + }, + maxWyrmwind: { + name: "ダイドラグーン", + effect: "ダイマックスした ポケモンが 繰りだす ドラゴンタイプの 攻撃。 相手の 攻撃を 下げる。" + }, + maxMindstorm: { + name: "ダイサイコ", + effect: "ダイマックスした ポケモンが 繰りだす エスパータイプの 攻撃。 5ターンの 間 サイコフィールドにする。" + }, + maxRockfall: { + name: "ダイロック", + effect: "ダイマックスした ポケモンが 繰りだす いわタイプの 攻撃。 5ターンの 間 砂あらしにする。" + }, + maxQuake: { + name: "ダイアース", + effect: "ダイマックスした ポケモンが 繰りだす じめんタイプの 攻撃。 味方の 特防を 上げる。" + }, + maxDarkness: { + name: "ダイアーク", + effect: "ダイマックスした ポケモンが 繰りだす あくタイプの 攻撃。 相手の 特防を 下げる。" + }, + maxOvergrowth: { + name: "ダイソウゲン", + effect: "ダイマックスした ポケモンが 繰りだす くさタイプの 攻撃。 5ターンの 間 グラスフィールドにする。" + }, + maxSteelspike: { + name: "ダイスチル", + effect: "ダイマックスした ポケモンが 繰りだす はがねタイプの 攻撃。 味方の 防御を 上げる。" + }, + clangorousSoul: { + name: "ソウルビート", + effect: "自分の HPを 少し 削って すべての 能力を 上げる。" + }, + bodyPress: { + name: "ボディプレス", + effect: "体を ぶつけて 攻撃。 防御が 高いほど 与える ダメージが 増える。" + }, + decorate: { + name: "デコレーション", + effect: "かざりつけを することで 相手の 攻撃と 特攻を ぐーんと 上げる。" + }, + drumBeating: { + name: "ドラムアタック", + effect: "ドラムの 根っこを ドラミングで コントロールして こうげき することで 相手の 素早さを 下げる。" + }, + snapTrap: { + name: "トラバサミ", + effect: "トラバサミで 捕らえて 4-5ターンの 間 相手を はさんで 攻撃する。" + }, + pyroBall: { + name: "かえんボール", + effect: "小石を 燃やした 炎の ボールで 相手を 攻撃する。 やけど 状態に することが ある。" + }, + behemothBlade: { + name: "きょじゅうざん", + effect: "全身で 強大な剣を 振りかざし 勢いよく 切りかかって 攻撃する。" + }, + behemothBash: { + name: "きょじゅうだん", + effect: "全身を 強固な盾へと 変化させ 勢いよく ぶつかって 攻撃する。" + }, + auraWheel: { + name: "オーラぐるま", + effect: "ほほぶくろに 溜めた エネルギーで 攻撃し 自分の 素早さを あげる。 モルペコの 姿で タイプが 変わる。" + }, + breakingSwipe: { + name: "ワイドブレイカー", + effect: "きょうじんな しっぽを 激しく ふりはらって 相手を 攻撃する。 相手の 攻撃を 下げる。" + }, + branchPoke: { + name: "えだづき", + effect: "するどく とがった 枝で 相手を 突いて 攻撃する。" + }, + overdrive: { + name: "オーバードライブ", + effect: "ギターや ベースを かきならして 激しく 響く 大きな 振動を 相手に 与えて 攻撃する。" + }, + appleAcid: { + name: "りんごさん", + effect: "すっぱい りんごから つくりだした 酸性の 液体で 攻撃。 相手の 特防を 下げる。" + }, + gravApple: { + name: "Gのちから", + effect: "高いところから りんごを 落として ダメージを 与える。 相手の 防御を 下げる。" + }, + spiritBreak: { + name: "ソウルクラッシュ", + effect: "食らうと くじけるほどの 勢いで 攻撃。 相手の 特攻を 下げる。" + }, + strangeSteam: { + name: "ワンダースチーム", + effect: "煙を 噴出して 相手を 攻撃。 混乱 させることが ある。" + }, + lifeDew: { + name: "いのちのしずく", + effect: "不思議な 水を ふりまいて 自分と 場にいる 味方の HPを 回復する。" + }, + obstruct: { + name: "ブロッキング", + effect: "相手の 攻撃を まったく 受けない。 連続で だすと 失敗しやすい。 触れると 防御が がくっと 下がる。" + }, + falseSurrender: { + name: "どげざつき", + effect: "頭を 下げる ふりを しながら 振りみだした 髪の毛を 突き刺す。 攻撃は 必ず 命中する。" + }, + meteorAssault: { + name: "スターアサルト", + effect: "太い クキを ふりまわして 攻撃。 ただし 自分も よろめいてしまうため 次の ターンは 動けなくなる。" + }, + eternabeam: { + name: "ムゲンダイビーム", + effect: "本来の 姿と なった ムゲンダイナ 最大の 攻撃。 次の ターンは 動けなくなる。" + }, + steelBeam: { + name: "てっていこうせん", + effect: "全身から 集めた はがねを ビームとして 激しく 撃ちだす。 自分も ダメージを 受けてしまう。" + }, + expandingForce: { + name: "ワイドフォース", + effect: "サイコパワーで 相手を 攻撃する。 サイコフィールドの時 威力が あがり すべての 相手に ダメージを 与える。" + }, + steelRoller: { + name: "アイアンローラー", + effect: "フィールドを 破壊しながら 攻撃。 なんらかの フィールド状態に 変わっていないと 技は 失敗する。" + }, + scaleShot: { + name: "スケイルショット", + effect: "ウロコを 撃ちだして 攻撃する。 2ー5回の 間 連続で だす。 素早さが あがるが 防御が さがる。" + }, + meteorBeam: { + name: "メテオビーム", + effect: "1ターン目に 宇宙の 力を 集めることで 特攻が あがり 2ターン目に 相手を 攻撃する。" + }, + shellSideArm: { + name: "シェルアームズ", + effect: "物理か 特殊か より多く ダメージを 与えられる 能力で 攻撃する。 毒状態に することが ある。" + }, + mistyExplosion: { + name: "ミストバースト", + effect: "自分の 周りに いる すべてを 攻撃するが 使うと 瀕死になる。 ミストフィールドで 威力が あがる。" + }, + grassyGlide: { + name: "グラススライダー", + effect: "地面を 滑るように 相手を 攻撃。 グラスフィールドの時 必ず 先制攻撃 できる。" + }, + risingVoltage: { + name: "ライジングボルト", + effect: "地面から 立ちのぼる 電撃で 攻撃。 相手が エレキフィールドに いる時 技の 威力が 2倍に なる。" + }, + terrainPulse: { + name: "だいちのはどう", + effect: "フィールドの力を 借りて 攻撃。 使った時の フィールドの状態に よって 技の タイプと 威力が 変わる。" + }, + skitterSmack: { + name: "はいよるいちげき", + effect: "背後から はいより 攻撃する。 相手の 特攻を さげる。" + }, + burningJealousy: { + name: "しっとのほのお", + effect: "しっとの エネルギーで 相手を 攻撃。 そのターン 能力が あがった ポケモンを やけどの 状態に する。" + }, + lashOut: { + name: "うっぷんばらし", + effect: "相手への いらだちを ぶつけて 攻撃。 そのターンに 能力を さげられていると 技の 威力が 2倍に なる。" + }, + poltergeist: { + name: "ポルターガイスト", + effect: "相手の 持ち物を あやつって 攻撃。 相手が 道具を 持っていない 場合は 失敗する。" + }, + corrosiveGas: { + name: "ふしょくガス", + effect: "強い 酸性の ガスで 周りに いるものを 包みこみ 持っている 道具を 溶かしてしまう。" + }, + coaching: { + name: "コーチング", + effect: "的確な 指導を おこなうことで 味方 全員の 攻撃と 防御を 上げる。" + }, + flipTurn: { + name: "クイックターン", + effect: "攻撃したあと ものすごい スピードで もどってきて 控えの ポケモンと 入れ替わる。" + }, + tripleAxel: { + name: "トリプルアクセル", + effect: "3回連続で キックを くりだして 攻撃する。 技が 当たるたびに 威力は あがる。" + }, + dualWingbeat: { + name: "ダブルウイング", + effect: "翼を 相手に ぶつけて 攻撃する。 2回連続で ダメージを 与える。" + }, + scorchingSands: { + name: "ねっさのだいち", + effect: "熱く 焼けた 砂を 相手に ぶつけて 攻撃する。 やけど状態に することが ある。" + }, + jungleHealing: { + name: "ジャングルヒール", + effect: "ジャングルと 一体化して 自分と 場にいる 味方の HPと 状態を 回復する。" + }, + wickedBlow: { + name: "あんこくきょうだ", + effect: "あくの型を 極めし 強烈な 一撃。 必ず 急所に 当たる。" + }, + surgingStrikes: { + name: "すいりゅうれんだ", + effect: "みずの型を 極めし 流れるような 3回の 連撃。 必ず 急所に 当たる。" + }, + thunderCage: { + name: "サンダープリズン", + effect: "ほとばしる 電気の おりの 中に 4ー5ターンの 間 相手を 閉じこめて 攻撃する。" + }, + dragonEnergy: { + name: "ドラゴンエナジー", + effect: "生命力を パワーに 変え 相手を 攻撃する。 自分の HPが 少ないほど 技の 威力は さがる。" + }, + freezingGlare: { + name: "いてつくしせん", + effect: "両目から サイコパワーを 撃ちだして 攻撃する。 こおり状態に することが ある。" + }, + fieryWrath: { + name: "もえあがるいかり", + effect: "怒りを 炎の ような オーラに 変えて 攻撃する。 相手を ひるませることが ある。" + }, + thunderousKick: { + name: "らいめいげり", + effect: "雷の ような 動きで 相手を 翻弄しながら キックする。 相手の 防御を さげる。" + }, + glacialLance: { + name: "ブリザードランス", + effect: "吹雪を まとった 氷の 槍を 相手に 投げつけて 攻撃する。" + }, + astralBarrage: { + name: "アストラルビット", + effect: "たくさんの 小さな 霊体を 相手に ぶつけて 攻撃する。" + }, + eerieSpell: { + name: "ぶきみなじゅもん", + effect: "強力な サイコパワーで 攻撃。 相手が 最後に 使った技の PPを 3だけ 減らす。" + }, + direClaw: { + name: "フェイタルクロー", + effect: "破滅的なツメで 攻撃する。 相手を どく まひ ねむりの いずれかの状態に することも ある。" + }, + psyshieldBash: { + name: "バリアーラッシュ", + effect: "思念のエネルギーを まといながら 相手に ぶつかっていく。 自分の 防御を あげる。" + }, + powerShift: { + name: "パワーシフト", + effect: "自分の 攻撃と防御を 入れ替える。" + }, + stoneAxe: { + name: "がんせきアックス", + effect: "岩の斧で 攻撃する。 ばらまかれた 岩の破片が 相手の 周りに 浮かぶ。" + }, + springtideStorm: { + name: "はるのあらし", + effect: "愛憎 入りまじった 強烈な風で 相手を 包みこんで 攻撃する。 相手の 攻撃を さげることが ある。" + }, + mysticalPower: { + name: "しんぴのちから", + effect: "不思議な力を 放出して 攻撃する。 自分の 特攻が あがる。" + }, + ragingFury: { + name: "だいふんげき", + effect: "2-3ターンの 間 炎を 放ちながら 暴れまわる。 暴れたあとは 混乱する。" + }, + waveCrash: { + name: "ウェーブタックル", + effect: "水を まといつつ 全身で 相手に ぶつかるが 自分も かなりの ダメージ を受ける。" + }, + chloroblast: { + name: "クロロブラスト", + effect: "自身の 葉緑素を 集約し 放出して 攻撃する。 自分も ダメージを 受けてしまう。" + }, + mountainGale: { + name: "ひょうざんおろし", + effect: "氷山のような 大きな 氷塊を ぶつけて 攻撃する。 相手を ひるませることが ある。" + }, + victoryDance: { + name: "しょうりのまい", + effect: "勝利を 呼びこむ 舞を 激しく 踊って 自分の 攻撃と 防御と 素早さを あげる。" + }, + headlongRush: { + name: "ぶちかまし", + effect: "全身全霊の たいあたりを くらわせる。 自分の 防御と 特防が さがる。" + }, + barbBarrage: { + name: "どくばりセンボン", + effect: "無数の毒針で 相手を 毒状態に することもある。 相手が 毒状態だと 威力は 2倍になる。" + }, + esperWing: { + name: "オーラウイング", + effect: "オーラで 強化した翼で 切り裂く。 急所に 当たりやすい。 自分の 素早さを あげる。" + }, + bitterMalice: { + name: "うらみつらみ", + effect: "背筋が 凍るような 怨念で 攻撃して 相手の 攻撃を さげる。" + }, + shelter: { + name: "たてこもる", + effect: "皮膚を 鉄の盾のように 硬くすることで 自分の 防御を ぐーんと あげる。" + }, + tripleArrows: { + name: "3ぼんのや", + effect: "足技のあと 3本の矢を 同時に放つ。 相手の 防御を さげたり ひるませることが ある。 急所に 当たりやすい。" + }, + infernalParade: { + name: "ひゃっきやこう", + effect: "無数の火の玉で 攻撃して やけど状態に することが ある。 相手が 状態異常だと 威力は 2倍。" + }, + ceaselessEdge: { + name: "ひけん・ちえなみ", + effect: "貝殻の剣で 攻撃する。 ばらまかれた 貝殻の破片は 相手の 足下に まきびし となって 散らばる。" + }, + bleakwindStorm: { + name: "こがらしあらし", + effect: "身も心も 震える 冷たく 激しい風で 攻撃する。 相手の 素早さを さげることが ある。" + }, + wildboltStorm: { + name: "かみなりあらし", + effect: "嵐を 起こし 雷雲を 呼びよせ 雷と風で 激しく 攻撃をする。 相手を まひ状態に することもある。" + }, + sandsearStorm: { + name: "ねっさのあらし", + effect: "熱く焼けた砂と 強烈な風で 包みこんで 攻撃する。 相手を やけど状態に することがある。" + }, + lunarBlessing: { + name: "みかづきのいのり", + effect: "みかづきに いのりを ささげて 自分と 場にいる 味方の HPと 状態を 回復する。" + }, + takeHeart: { + name: "ブレイブチャージ", + effect: "心を 奮わせて 自分の 状態異常を 治し さらには 特攻と 特防を あげる。" + }, + gMaxWildfire: { + name: "キョダイゴクエン", + effect: "キョダイマックスした リザードンが 繰りだす ほのおタイプの 攻撃。 4ターンの 間 ダメージを 与える。" + }, + gMaxBefuddle: { + name: "キョダイコワク", + effect: "キョダイマックスした バタフリーが 繰り出す むしタイプの 攻撃。 毒・まひ・眠りの どれかに する。" + }, + gMaxVoltCrash: { + name: "キョダイバンライ", + effect: "キョダイマックスした ピカチュウが 繰りだす でんきタイプの 攻撃。 相手を まひ状態に する。" + }, + gMaxGoldRush: { + name: "キョダイコバン", + effect: "キョダイマックスした ニャースが 繰り出す ノーマルタイプの 攻撃。 相手を 混乱させ お金も もらえる。" + }, + gMaxChiStrike: { + name: "キョダイシンゲキ", + effect: "キョダイマックスした カイリキーが 繰りだす かくとうタイプの 攻撃。 急所に 当たりやすく なる。" + }, + gMaxTerror: { + name: "キョダイゲンエイ", + effect: "キョダイマックスした ゲンガーが 繰りだす ゴーストタイプの 攻撃。 影を 踏み 交代 できなくする。" + }, + gMaxResonance: { + name: "キョダイセンリツ", + effect: "キョダイマックスした ラプラスが 繰りだす こおりタイプの 攻撃。 5ターンの 間 ダメージを 弱める。" + }, + gMaxCuddle: { + name: "キョダイホーヨー", + effect: "キョダイマックスした イーブイが 繰りだす ノーマルタイプの 攻撃。 相手を メロメロに する。" + }, + gMaxReplenish: { + name: "キョダイサイセイ", + effect: "キョダイマックスした カビゴンが 繰りだす ノーマルタイプの 攻撃。 食べた きのみを 再生する。" + }, + gMaxMalodor: { + name: "キョダイシュウキ", + effect: "キョダイマックスした ダストダスが 繰りだす どくタイプの 攻撃。 相手を 毒 状態に する。" + }, + gMaxStonesurge: { + name: "キョダイガンジン", + effect: "キョダイマックスした カジリガメが 繰りだす みずタイプの 攻撃。 鋭い 無数の 岩を ばらまく。" + }, + gMaxWindRage: { + name: "キョダイフウゲキ", + effect: "キョダイマックスした アーマーガアが 繰りだす ひこうタイプの 攻撃。 リフレクターや ひかりのかべを 消し去る。" + }, + gMaxStunShock: { + name: "キョダイカンデン", + effect: "キョダイマックスした ストリンダーが 繰り出す でんきタイプの 攻撃。 相手を 毒 か まひ どちらかにする。" + }, + gMaxFinale: { + name: "キョダイダンエン", + effect: "キョダイマックスした マホイップが 繰りだす フェアリータイプの 攻撃。 味方の HPを 回復する。" + }, + gMaxDepletion: { + name: "キョダイゲンスイ", + effect: "キョダイマックスした ジュラルドンが 繰りだす ドラゴンタイプの 攻撃。 最後に 使われた わざPPを 減らす。" + }, + gMaxGravitas: { + name: "キョダイテンドウ", + effect: "キョダイマックスした イオルブが 繰りだす エスパータイプの 攻撃。 5ターンの 間 重力が 変わる。" + }, + gMaxVolcalith: { + name: "キョダイフンセキ", + effect: "キョダイマックスした セキタンザンが 繰りだす いわタイプの 攻撃。 4ターンの 間 ダメージを 与える。" + }, + gMaxSandblast: { + name: "キョダイサジン", + effect: "キョダイマックスした サダイジャが 繰りだす じめんタイプの 攻撃。 4-5ターンの間 砂が 吹き荒れる。" + }, + gMaxSnooze: { + name: "キョダイスイマ", + effect: "キョダイマックスした オーロンゲが 繰りだす あくタイプの 攻撃。 大きな あくびで 眠気を 誘う。" + }, + gMaxTartness: { + name: "キョダイサンゲキ", + effect: "キョダイマックスした アップリューが 繰りだす くさタイプの 攻撃。 相手の 回避率を 下げる。" + }, + gMaxSweetness: { + name: "キョダイカンロ", + effect: "キョダイマックスした タルップルが 繰りだす くさタイプの 攻撃。 味方の 状態異常を 回復する。" + }, + gMaxSmite: { + name: "キョダイテンバツ", + effect: "キョダイマックスした ブリムオンが 繰りだす フェアリータイプの 攻撃。 相手を 混乱させる。" + }, + gMaxSteelsurge: { + name: "キョダイコウジン", + effect: "キョダイマックスした ダイオウドウが 繰りだす タイプの 攻撃。 鋭い 無数の とげを ばらまく。" + }, + gMaxMeltdown: { + name: "キョダイユウゲキ", + effect: "キョダイマックスした メルメタルが 繰りだす はがねタイプの 攻撃。 同じ 技を 連続で 出せなくする。" + }, + gMaxFoamBurst: { + name: "キョダイホウマツ", + effect: "キョダイマックスした キングラーが 繰りだす みずタイプの 攻撃。 相手の 素早さを がくっと さげる。" + }, + gMaxCentiferno: { + name: "キョダイヒャッカ", + effect: "キョダイマックスした マルヤクデが 繰りだす ほのおタイプの 攻撃。 4-5ターンの間 炎に 閉じこめる。" + }, + gMaxVineLash: { + name: "キョダイベンタツ", + effect: "キョダイマックスした フシギバナが 繰りだす くさタイプの 攻撃。 4ターンの 間 ダメージを 与える。" + }, + gMaxCannonade: { + name: "キョダイホウゲキ", + effect: "キョダイマックスした カメックスが 繰りだす みずタイプの 攻撃。 4ターンの 間 ダメージを 与える。" + }, + gMaxDrumSolo: { + name: "キョダイコランダ", + effect: "キョダイマックスした ゴリランダーが 繰りだす くさタイプの 攻撃。 相手の 特性に ジャマされない。" + }, + gMaxFireball: { + name: "キョダイカキュウ", + effect: "キョダイマックスした エースバーンが 繰りだす ほのおタイプの 攻撃。 相手の 特性に ジャマされない。" + }, + gMaxHydrosnipe: { + name: "キョダイソゲキ", + effect: "キョダイマックスした インテレオンが 繰りだす みずタイプの 攻撃。 相手の 特性に ジャマされない。" + }, + gMaxOneBlow: { + name: "キョダイイチゲキ", + effect: "キョダイマックスした ウーラオスが 繰りだす あくタイプの 攻撃。 ダイウォールを 無視できる 一撃。" + }, + gMaxRapidFlow: { + name: "キョダイレンゲキ", + effect: "キョダイマックスした ウーラオスが 繰りだす みずタイプの 攻撃。 ダイウォールを 無視できる 連撃。" + }, + teraBlast: { + name: "テラバースト", + effect: "テラスタルだと テラスタイプの エネルギーを 放出して 攻撃する。 攻撃と 特攻を 比べて 高いほうで ダメージを 与える。" + }, + silkTrap: { + name: "スレッドトラップ", + effect: "糸の罠を はりめぐらせる。 相手の 攻撃を 防ぐと 同時に 触れた 相手の 素早さを さげる。" + }, + axeKick: { + name: "かかとおとし", + effect: "蹴りあげた かかとを 落として 攻撃する。 相手を 混乱させることが ある。 はずすと 自分が ダメージを 受ける。" + }, + lastRespects: { + name: "おはかまいり", + effect: "仲間の 無念を 晴らすため 攻撃する。 倒された 味方のポケモンが 多いほど 技の 威力が 増える。" + }, + luminaCrash: { + name: "ルミナコリジョン", + effect: "精神にも 作用する 奇妙な光を 放って 攻撃する。 相手の 特防を がくっと さげる。" + }, + orderUp: { + name: "いっちょうあがり", + effect: "いなせな 身のこなしで 攻撃。 口の中に シャリタツが いると そのすがたによって 能力が あがる。" + }, + jetPunch: { + name: "ジェットパンチ", + effect: "激流を こぶしに まとって 目にも 留まらぬ パンチを くりだす。 必ず 先制攻撃 できる。" + }, + spicyExtract: { + name: "ハバネロエキス", + effect: "とんでもなく 辛いエキスを 出す。 相手の 攻撃が ぐーんと あがり 防御が がくっと さがる。" + }, + spinOut: { + name: "ホイールスピン", + effect: "足に 負荷を かけることにより 激しく 回転して ダメージを 与える。 自分の 素早さが がくっと さがる。" + }, + populationBomb: { + name: "ネズミざん", + effect: "仲間たちが わらわらと 集まって コンビネーションで 攻撃を 与えていく。 1-10回の 間 連続で あたる。" + }, + iceSpinner: { + name: "アイススピナー", + effect: "足に 薄い氷を まとい クルクルと 回りながら ぶつかる。 回転の 動きによって フィールドを 壊す。" + }, + glaiveRush: { + name: "きょけんとつげき", + effect: "体を 投げだす 無謀な突撃。 技のあと 相手からの 攻撃は 必ず 命中し ダメージが 2倍に なってしまう。" + }, + revivalBlessing: { + name: "さいきのいのり", + effect: "慈愛の心で いのることにより 控えにいる ひんしの ポケモンを HPを 半分の状態で 復活させる。" + }, + saltCure: { + name: "しおづけ", + effect: "相手を しおづけ状態に して 毎ターン ダメージを 与える。 はがね みずタイプは より 苦しむ。" + }, + tripleDive: { + name: "トリプルダイブ", + effect: "息のあった 飛びこみを することで 相手に 水しぶきを あてる。 3回連続で ダメージを 与える。" + }, + mortalSpin: { + name: "キラースピン", + effect: "回転して 相手を 攻撃する。 しめつける まきつく やどりぎのタネ など 吹きとばす。 相手を 毒状態に する。" + }, + doodle: { + name: "うつしえ", + effect: "相手の本質を とらえて うつしだし 自分と 味方を 相手と 同じ 特性に 変化させる。" + }, + filletAway: { + name: "みをけずる", + effect: "自分の HPを けずって 自分の 攻撃と 特攻と 素早さを ぐーんと あげる。" + }, + kowtowCleave: { + name: "ドゲザン", + effect: "土下座して 相手を 油断させておいて 切りかかる。 攻撃は 必ず 命中する。" + }, + flowerTrick: { + name: "トリックフラワー", + effect: "細工がある 花たばを 相手に 投げて 攻撃する。 必ず 命中して 急所にも 当たる。" + }, + torchSong: { + name: "フレアソング", + effect: "燃えたぎる 火炎を 歌うように 吹きつけて 相手を 焦がす。 自分の 特攻を あげる。" + }, + aquaStep: { + name: "アクアステップ", + effect: "水もしたたる かろやかな 足どりで 相手を 翻弄し ダメージを 与える。 自分の 素早さを あげる。" + }, + ragingBull: { + name: "レイジングブル", + effect: "怒り狂う あばれうしの 猛烈な タックル。 フォルムで 技のタイプが 変わり ひかりのかべや リフレクターなども 破壊できる。" + }, + makeItRain: { + name: "ゴールドラッシュ", + effect: "大量のコインを ぶちまけて 攻撃。 自分の 特攻が さがる。 戦闘の あとで お金も もらえる。" + }, + psyblade: { + name: "サイコブレイド", + effect: "実体のない刃で 相手を 切り裂く。 エレキフィールドに いるとき 技の威力が 1.5倍に なる。" + }, + hydroSteam: { + name: "ハイドロスチーム", + effect: "煮えたぎる水を 勢いよく 浴びせる。 日差しが 強いとき 技の威力が さがるどころか 1.5倍になる。" + }, + ruination: { + name: "カタストロフィ", + effect: "破滅的な 災厄を 巻き起こし 相手の HPを 半分に する。" + }, + collisionCourse: { + name: "アクセルブレイク", + effect: "変形しながら 荒々しく 落下し いにしえの 大爆発を 引き起こす。 弱点をつくと さらに 威力が 増す。" + }, + electroDrift: { + name: "イナズマドライブ", + effect: "変形しながら 超高速で 走行し 未知なる 電撃が 相手を つらぬく。 弱点をつくと さらに 威力が 増す。" + }, + shedTail: { + name: "しっぽきり", + effect: "自分の HPを 削って 分身を だしたあと もどってきて 控えの ポケモンと 入れ替わる。" + }, + chillyReception: { + name: "さむいギャグ", + effect: "場を 凍らせる ギャグを 言い残し 控えの ポケモンと 入れ替わる。 5ターンの 間 ゆきを 降らす。" + }, + tidyUp: { + name: "おかたづけ", + effect: "まきびし ステルスロック ねばねばネット どくびし みがわりを すべて かたづける。 自分の 攻撃と 素早さが あがる。" + }, + snowscape: { + name: "ゆきげしき", + effect: "5ターンの 間 ゆきを 降らせる。 こおりタイプの 防御が あがる。" + }, + pounce: { + name: "とびつく", + effect: "相手に 飛びついて 攻撃する。 相手の 素早さを さげる。" + }, + trailblaze: { + name: "くさわけ", + effect: "草むらから 飛びだすように 攻撃する。 軽快な 足どりに よって 自分の 素早さを あげる。" + }, + chillingWater: { + name: "ひやみず", + effect: "相手の 元気を 失わせるくらい 冷たい水を 浴びせて 攻撃する。 相手の 攻撃を さげる。" + }, + hyperDrill: { + name: "ハイパードリル", + effect: "とがった 体の部位を 急速に 回転させ つらぬく。 まもるや みきり なども 無視 できる。" + }, + twinBeam: { + name: "ツインビーム", + effect: "両目から 不可思議な 光線を 発射して 攻撃する。 2回連続で ダメージを 与える。" + }, + rageFist: { + name: "ふんどのこぶし", + effect: "怒りを エネルギーに 変えて 攻撃。 受けた 攻撃の 回数が 多いほど 技の 威力が あがる。" + }, + armorCannon: { + name: "アーマーキャノン", + effect: "みずからの ヨロイを 燃えたぎる 弾として 撃ち出して 攻撃する。 自分の 防御と 特防が さがる。" + }, + bitterBlade: { + name: "むねんのつるぎ", + effect: "この世への 未練を 剣先に こめて 切りつける。 与えた ダメージの 半分の HPを 回復できる。" + }, + doubleShock: { + name: "でんこうそうげき", + effect: "全身の でんきを すべて 放って 大ダメージを 与える。 自分の でんきタイプが なくなる。" + }, + gigatonHammer: { + name: "デカハンマー", + effect: "大きな ハンマーを 体ごと ぶんまわして 攻撃する。 この技は 2回連続で だせない。" + }, + comeuppance: { + name: "ほうふく", + effect: "技を だす前に 最後に 受けた 技の ダメージを 大きくして だした 相手に 返す。" + }, + aquaCutter: { + name: "アクアカッター", + effect: "加圧された 水を 刃のように 噴射して 相手を 切り裂く。 急所に 当たりやすい。" + }, + blazingTorque: { + name: "バーンアクセル", + effect: "メラメラの エンジンを 吹かして 相手に ぶつかる。やけど状態に することが ある。" + }, + wickedTorque: { + name: "ダークアクセル", + effect: "悪意で エンジンを 吹かして 相手に ぶつかる。眠り状態に することが ある。" + }, + noxiousTorque: { + name: "ポイズンアクセル", + effect: "有毒な エンジンを 吹かして 相手に ぶつかる。毒状態に することが ある。" + }, + combatTorque: { + name: "ファイトアクセル", + effect: "力いっぱい エンジンを 吹かして 相手に ぶつかる。まひ状態に することが ある。" + }, + magicalTorque: { + name: "マジカルアクセル", + effect: "幻想的な エンジンを 吹かして 相手に ぶつかる。混乱させることが ある。" + }, + bloodMoon: { + name: "ブラッドムーン", + effect: "血のように 赤い満月から ありったけの 気迫を 撃ちだす。 この技は 2回連続で だせない。" + }, + matchaGotcha: { + name: "シャカシャカほう", + effect: "かきまぜた お茶の 大砲は 与えた ダメージの 半分を 回復して やけど状態に することも ある。" + }, + syrupBomb: { + name: "みずあめボム", + effect: "ねっとりした みずあめを 爆発させ 相手を あめまみれ 状態にして 3ターンの間 素早さを さげ続ける。" + }, + ivyCudgel: { + name: "ツタこんぼう", + effect: "ツタを まきつけた こん棒で なぐる。 かぶっている お面で タイプが 変わる。 急所に 当たりやすい。" + }, + electroShot: { + name: "エレクトロビーム", + effect: "1ターン目に 電気を 集めて 特攻が あがり 2ターン目に 高圧の 電気を 発射する。 天気が 雨のときは すぐに 発射できる。" + }, + teraStarstorm: { + name: "テラクラスター", + effect: "結晶の力を 照射し 敵を 排除する。 テラパゴスが ステラフォルムで 放つと すべての 相手に ダメージを 与える。" + }, + fickleBeam: { + name: "きまぐレーザー", + effect: "光線を 発射して 攻撃する。 ときどき ほかの首も 協力して レーザーを 放ち 威力が 2倍に なる。" + }, + burningBulwark: { + name: "かえんのまもり", + effect: "相手の 攻撃を 超高熱の 体毛で 防ぎ 同時に 触れた 相手に やけどを 与えてしまう。" + }, + thunderclap: { + name: "じんらい", + effect: "相手より 先に 電撃を 浴びせる。 相手が だす技が 攻撃技でないと 失敗する。" + }, + mightyCleave: { + name: "パワフルエッジ", + effect: "頭部に 蓄積した 光で 切断する。 守りを 無視して 攻撃できる。" + }, + tachyonCutter: { + name: "タキオンカッター", + effect: "粒子の刃を たて続けに 発射して 2回連続で ダメージを 与える。 攻撃は 必ず 命中する。" + }, + hardPress: { + name: "ハードプレス", + effect: "腕やハサミで 相手を 圧迫する。 相手の HPが 残っているほど 威力が あがる。" + }, + dragonCheer: { + name: "ドラゴンエール", + effect: "竜の鼓舞で 士気を 上げて 味方の技が 急所に 当たりやすくなる。 ドラゴンタイプだと より 鼓舞される。" + }, + alluringVoice: { + name: "みわくのボイス", + effect: "天使のような 歌声で 相手を 攻撃。 そのターン 能力が あがった ポケモンを 混乱の 状態に する。" + }, + temperFlare: { + name: "やけっぱち", + effect: "自棄になった 勢いで 攻撃する。 前の ターンに 技を 外していると 威力が 倍に なる。" + }, + supercellSlam: { + name: "サンダーダイブ", + effect: "体を 帯電させ て相手に のしかかる。 はずすと 自分が ダメージを 受ける。" + }, + psychicNoise: { + name: "サイコノイズ", + effect: "不快な音波を 相手に 浴びせて 攻撃。 2ターンの間 技や 特性や 持っている 道具によって HPを 回復できなくなる。" + }, + upperHand: { + name: "はやてがえし", + effect: "動きに 反応して 掌底を 打ちこみ 相手を ひるませる。 相手が だす技が 先制攻撃でないと 失敗する。" + }, + malignantChain: { + name: "じゃどくのくさり", + effect: "毒でできた鎖を 相手に 巻きつけ 毒素を 流しこんで 蝕む。 猛毒の 状態に することが ある。" + }, +} as const; diff --git a/src/locales/ja/nature.ts b/src/locales/ja/nature.ts new file mode 100644 index 00000000000..d5f576159fd --- /dev/null +++ b/src/locales/ja/nature.ts @@ -0,0 +1,29 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const nature: SimpleTranslationEntries = { + "Hardy": "がんばりや", + "Lonely": "さみしがり", + "Brave": "ゆうかん", + "Adamant": "いじっぱり", + "Naughty": "やんちゃ", + "Bold": "ずぶとい", + "Docile": "すなお", + "Relaxed": "のんき", + "Impish": "わんぱく", + "Lax": "のうてんき", + "Timid": "おくびょう", + "Hasty": "せっかち", + "Serious": "まじめ", + "Jolly": "ようき", + "Naive": "むじゃき", + "Modest": "ひかえめ", + "Mild": "おっとり", + "Quiet": "れいせい", + "Bashful": "てれや", + "Rash": "うっかりや", + "Calm": "おだやか", + "Gentle": "おとなしい", + "Sassy": "なまいき", + "Careful": "しんちょう", + "Quirky": "きまぐれ" +} as const; diff --git a/src/locales/ja/party-ui-handler.ts b/src/locales/ja/party-ui-handler.ts new file mode 100644 index 00000000000..41917addd0b --- /dev/null +++ b/src/locales/ja/party-ui-handler.ts @@ -0,0 +1,54 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const partyUiHandler: SimpleTranslationEntries = { + "SEND_OUT": "いれかえる", + "SUMMARY": "つよさをみる", + "CANCEL": "やめる", + "RELEASE": "にがす", + "APPLY": "つかう", + "TEACH": "おしえる", + "SPLICE": "Splice", + "UNSPLICE": "Unsplice", + "ACTIVATE": "Activate", + "DEACTIVATE": "Deactivate", + "TRANSFER": "Transfer", + "ALL": "すべて", + "PASS_BATON": "Pass Baton", + "UNPAUSE_EVOLUTION": "Unpause Evolution", + "REVIVE": "Revive", + "RENAME": "Rename", + + "choosePokemon": "Choose a Pokémon.", + "doWhatWithThisPokemon": "Do what with this Pokémon?", + "noEnergy": "{{pokemonName}} has no energy\nleft to battle!", + "hasEnergy": "{{pokemonName}} still has energy\nto battle!", + "cantBeUsed": "{{pokemonName}} can't be used in\nthis challenge!", + "tooManyItems": "{{pokemonName}} has too many\nof this item!", + "anyEffect": "It won't have any effect.", + "unpausedEvolutions": "Evolutions have been unpaused for {{pokemonName}}.", + "unspliceConfirmation": "Do you really want to unsplice {{fusionName}}\nfrom {{pokemonName}}? {{fusionName}} will be lost.", + "wasReverted": "{{fusionName}} was reverted to {{pokemonName}}.", + "releaseConfirmation": "Do you really want to release {{pokemonName}}?", + "releaseInBattle": "You can't release a Pokémon that's in battle!", + "selectAMove": "Select a move.", + "changeQuantity": "Select a held item to transfer.\nUse < and > to change the quantity.", + "selectAnotherPokemonToSplice": "Select another Pokémon to splice.", + "cancel": "Cancel", + + // Slot TM text + "able": "Able", + "notAble": "Not able", + "learned": "Learned", + + // Releasing messages + "goodbye": "Goodbye, {{pokemonName}}!", + "byebye": "Byebye, {{pokemonName}}!", + "farewell": "Farewell, {{pokemonName}}!", + "soLong": "So long, {{pokemonName}}!", + "thisIsWhereWePart": "This is where we part, {{pokemonName}}!", + "illMissYou": "I'll miss you, {{pokemonName}}!", + "illNeverForgetYou": "I'll never forget you, {{pokemonName}}!", + "untilWeMeetAgain": "Until we meet again, {{pokemonName}}!", + "sayonara": "Sayonara, {{pokemonName}}!", + "smellYaLater": "Smell ya later, {{pokemonName}}!", +} as const; diff --git a/src/locales/ja/pokeball.ts b/src/locales/ja/pokeball.ts new file mode 100644 index 00000000000..753457391c1 --- /dev/null +++ b/src/locales/ja/pokeball.ts @@ -0,0 +1,10 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const pokeball: SimpleTranslationEntries = { + "pokeBall": "モンスターボール", + "greatBall": "スーパーボール", + "ultraBall": "ハイパーボール", + "rogueBall": "ローグボール", + "masterBall": "マスターボール", + "luxuryBall": "ゴージャスボール", +} as const; diff --git a/src/locales/ja/pokemon-form.ts b/src/locales/ja/pokemon-form.ts new file mode 100644 index 00000000000..e8d6fb8df4a --- /dev/null +++ b/src/locales/ja/pokemon-form.ts @@ -0,0 +1,197 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battlePokemonForm: SimpleTranslationEntries = { + "mega": "Mega {{pokemonName}}", + "mega-x": "Mega {{pokemonName}} X", + "mega-y": "Mega {{pokemonName}} Y", + "primal": "Primal {{pokemonName}}", + "gigantamax": "G-Max {{pokemonName}}", + "eternamax": "E-Max {{pokemonName}}", + + "megaChange": "{{preName}} Mega Evolved\ninto {{pokemonName}}!", + "gigantamaxChange": "{{preName}} Gigantamaxed\ninto {{pokemonName}}!", + "eternamaxChange": "{{preName}} Eternamaxed\ninto {{pokemonName}}!", + "revertChange": "{{pokemonName}} reverted\nto its original form!", + "formChange": "{{preName}} changed form!", +} as const; + +export const pokemonForm: SimpleTranslationEntries = { + // Starters forms + // 1G + "pikachuCosplay": "Cosplay", + "pikachuCoolCosplay": "Cool Cosplay", + "pikachuBeautyCosplay": "Beauty Cosplay", + "pikachuCuteCosplay": "Cute Cosplay", + "pikachuSmartCosplay": "Smart Cosplay", + "pikachuToughCosplay": "Tough Cosplay", + "pikachuPartner": "Partner", + "eeveePartner": "Partner", + // 2G + "pichuSpiky": "Spiky", + "unownA": "A", + "unownB": "B", + "unownC": "C", + "unownD": "D", + "unownE": "E", + "unownF": "F", + "unownG": "G", + "unownH": "H", + "unownI": "I", + "unownJ": "J", + "unownK": "K", + "unownL": "L", + "unownM": "M", + "unownN": "N", + "unownO": "O", + "unownP": "P", + "unownQ": "Q", + "unownR": "R", + "unownS": "S", + "unownT": "T", + "unownU": "U", + "unownV": "V", + "unownW": "W", + "unownX": "X", + "unownY": "Y", + "unownZ": "Z", + "unownExclamation": "!", + "unownQuestion": "?", + // 3G + "castformSunny": "Sunny", + "castformRainy": "Rainy", + "castformSnowy": "Snowy", + "deoxysNormal": "Normal", + // 4G + "burmyPlant": "Plant", + "burmySandy": "Sandy", + "burmyTrash": "Trash", + "shellosEast": "East", + "shellosWest": "West", + "rotomHeat": "Heat", + "rotomWash": "Wash", + "rotomFrost": "Frost", + "rotomFan": "Fan", + "rotomMow": "Mow", + "giratinaAltered": "Altered", + "shayminLand": "Land", + // 5G + "basculinRedStriped": "Red Striped", + "basculinBlueStriped": "Blue Striped", + "basculinWhiteStriped": "White Striped", + "deerlingSpring": "Spring", + "deerlingSummer": "Summer", + "deerlingAutumn": "Autumn", + "deerlingWinter": "Winter", + "tornadusIncarnate": "Incarnate", + "thundurusIncarnate": "Incarnate", + "landorusIncarnate": "Incarnate", + "keldeoOrdinary": "Ordinary", + "meloettaAria": "Aria", + // 6G + "froakieBattleBond": "Battle Bond", + "scatterbugMeadow": "Meadow", + "scatterbugIcySnow": "Icy Snow", + "scatterbugPolar": "Polar", + "scatterbugTundra": "Tundra", + "scatterbugContinental": "Continental", + "scatterbugGarden": "Garden", + "scatterbugElegant": "Elegant", + "scatterbugModern": "Modern", + "scatterbugMarine": "Marine", + "scatterbugArchipelago": "Archipelago", + "scatterbugHighPlains": "High Plains", + "scatterbugSandstorm": "Sandstorm", + "scatterbugRiver": "River", + "scatterbugMonsoon": "Monsoon", + "scatterbugSavanna": "Savanna", + "scatterbugSun": "Sun", + "scatterbugOcean": "Ocean", + "scatterbugJungle": "Jungle", + "scatterbugFancy": "Fancy", + "scatterbugPokeBall": "Poké Ball", + "flabebeRed": "Red", + "flabebeYellow": "Yellow", + "flabebeOrange": "Orange", + "flabebeBlue": "Blue", + "flabebeWhite": "White", + "furfrouHeart": "Heart", + "furfrouStar": "Star", + "furfrouDiamond": "Diamond", + "furfrouDebutante": "Debutante", + "furfrouMatron": "Matron", + "furfrouDandy": "Dandy", + "furfrouLaReine": "La Reine", + "furfrouKabuki": "Kabuki", + "furfrouPharaoh": "Pharaoh", + "pumpkabooSmall": "Small", + "pumpkabooLarge": "Large", + "pumpkabooSuper": "Super", + "xerneasNeutral": "Neutral", + "xerneasActive": "Active", + "zygarde50": "50% Forme", + "zygarde10": "10% Forme", + "zygarde50Pc": "50% Forme Power Construct", + "zygarde10Pc": "10% Forme Power Construct", + "zygardeComplete": "Complete Forme", + // 7G + "oricorioBaile": "Baile", + "oricorioPompom": "Pom-Pom", + "oricorioPau": "Pau", + "oricorioSensu": "Sensu", + "rockruffOwnTempo": "Own Tempo", + "miniorRedMeteor": "Red Meteor", + "miniorOrangeMeteor": "Orange Meteor", + "miniorYellowMeteor": "Yellow Meteor", + "miniorGreenMeteor": "Green Meteor", + "miniorBlueMeteor": "Blue Meteor", + "miniorIndigoMeteor": "Indigo Meteor", + "miniorVioletMeteor": "Violet Meteor", + "miniorRed": "Red", + "miniorOrange": "Orange", + "miniorYellow": "Yellow", + "miniorGreen": "Green", + "miniorBlue": "Blue", + "miniorIndigo": "Indigo", + "miniorViolet": "Violet", + "mimikyuDisguised": "Disguised", + "mimikyuBusted": "Busted", + "magearnaOriginal": "Original", + "marshadowZenith": "Zenith", + // 8G + "sinisteaPhony": "Phony", + "sinisteaAntique": "Antique", + "eiscueNoIce": "No Ice", + "indeedeeMale": "Male", + "indeedeeFemale": "Female", + "morpekoFullBelly": "Full Belly", + "zacianHeroOfManyBattles": "Hero Of Many Battles", + "zamazentaHeroOfManyBattles": "Hero Of Many Battles", + "zarudeDada": "Dada", + "enamorusIncarnate": "Incarnate", + // 9G + "squawkabillyGreenPlumage": "Green Plumage", + "squawkabillyBluePlumage": "Blue Plumage", + "squawkabillyYellowPlumage": "Yellow Plumage", + "squawkabillyWhitePlumage": "White Plumage", + "tatsugiriCurly": "Curly", + "tatsugiriDroopy": "Droopy", + "tatsugiriStretchy": "Stretchy", + "gimmighoulChest": "Chest", + "gimmighoulRoaming": "Roaming", + "koraidonApexBuild": "Apex Build", + "koraidonLimitedBuild":"Limited Build", + "koraidonSprintingBuild":"Sprinting Build", + "koraidonSwimmingBuild":"Swimming Build", + "koraidonGlidingBuild":"Gliding Build", + "miraidonUltimateMode":"Ultimate Mode", + "miraidonLowPowerMode":"Low Power Mode", + "miraidonDriveMode":"Drive Mode", + "miraidonAquaticMode":"Aquatic Mode", + "miraidonGlideMode":"Glide Mode", + "poltchageistCounterfeit": "Counterfeit", + "poltchageistArtisan": "Artisan", + "paldeaTaurosCombat": "Combat", + "paldeaTaurosBlaze": "Blaze", + "paldeaTaurosAqua": "Aqua", + +} as const; diff --git a/src/locales/ja/pokemon-info-container.ts b/src/locales/ja/pokemon-info-container.ts new file mode 100644 index 00000000000..df588db4595 --- /dev/null +++ b/src/locales/ja/pokemon-info-container.ts @@ -0,0 +1,9 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const pokemonInfoContainer: SimpleTranslationEntries = { + "moveset": "わざ", + "gender": "せいべつ:", + "ability": "とくせい:", + "nature": "せいかく:", + "form": "すがた:" +} as const; diff --git a/src/locales/ja/pokemon-info.ts b/src/locales/ja/pokemon-info.ts new file mode 100644 index 00000000000..dfa905cd395 --- /dev/null +++ b/src/locales/ja/pokemon-info.ts @@ -0,0 +1,43 @@ +import { PokemonInfoTranslationEntries } from "#app/interfaces/locales"; + +export const pokemonInfo: PokemonInfoTranslationEntries = { + Stat: { + "HP": "HP", + "HPshortened": "HP", + "ATK": "こうげき", + "ATKshortened": "こうげき", + "DEF": "ぼうぎょ", + "DEFshortened": "ぼうぎょ", + "SPATK": "とくこう", + "SPATKshortened": "とくこう", + "SPDEF": "とくぼう", + "SPDEFshortened": "とくぼう", + "SPD": "すばやさ", + "SPDshortened": "すばやさ", + "ACC": "めいちゅう", + "EVA": "かいひ" + }, + + Type: { + "UNKNOWN": "Unknown", + "NORMAL": "ノーマル", + "FIGHTING": "かくとう", + "FLYING": "ひこう", + "POISON": "どく", + "GROUND": "じめん", + "ROCK": "いわ", + "BUG": "むし", + "GHOST": "ゴースト", + "STEEL": "はがね", + "FIRE": "ほのお", + "WATER": "みず", + "GRASS": "くさ", + "ELECTRIC": "でんき", + "PSYCHIC": "エスパー", + "ICE": "こおり", + "DRAGON": "ドラゴン", + "DARK": "あく", + "FAIRY": "フェアリー", + "STELLAR": "ステラ", + }, +} as const; diff --git a/src/locales/ja/pokemon-summary.ts b/src/locales/ja/pokemon-summary.ts new file mode 100644 index 00000000000..484ea2a9d67 --- /dev/null +++ b/src/locales/ja/pokemon-summary.ts @@ -0,0 +1,20 @@ +import { TranslationEntries } from "#app/interfaces/locales"; + +export const pokemonSummary: TranslationEntries = { + "pokemonInfo": "Pokémon Info", + "status": "Status", + "powerAccuracyCategory": "Power\nAccuracy\nCategory", + "type": "Type", + "unknownTrainer": "Unknown", + "ot": "OT", + "nature": "nature", + "expPoints": "Exp. Points", + "nextLv": "Next Lv.", + "cancel": "Cancel", + + "memoString": "{{natureFragment}} nature,\n{{metFragment}}", + "metFragment": { + "normal": "met at Lv{{level}},\n{{biome}}.", + "apparently": "apparently met at Lv{{level}},\n{{biome}}.", + }, +} as const; diff --git a/src/locales/ja/pokemon.ts b/src/locales/ja/pokemon.ts new file mode 100644 index 00000000000..b145def1298 --- /dev/null +++ b/src/locales/ja/pokemon.ts @@ -0,0 +1,1086 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const pokemon: SimpleTranslationEntries = { + "bulbasaur": "フシギダネ", + "ivysaur": "フシギソウ", + "venusaur": "フシギバナ", + "charmander": "ヒトカゲ", + "charmeleon": "リザード", + "charizard": "リザードン", + "squirtle": "ゼニガメ", + "wartortle": "カメール", + "blastoise": "カメックス", + "caterpie": "キャタピー", + "metapod": "トランセル", + "butterfree": "バタフリー", + "weedle": "ビードル", + "kakuna": "コクーン", + "beedrill": "スピアー", + "pidgey": "ポッポ", + "pidgeotto": "ピジョン", + "pidgeot": "ピジョット", + "rattata": "コラッタ", + "raticate": "ラッタ", + "spearow": "オニスズメ", + "fearow": "オニドリル", + "ekans": "アーボ", + "arbok": "アーボック", + "pikachu": "ピカチュウ", + "raichu": "ライチュウ", + "sandshrew": "サンド", + "sandslash": "サンドパン", + "nidoran_f": "ニドラン♀", + "nidorina": "ニドリーナ", + "nidoqueen": "ニドクイン", + "nidoran_m": "ニドラン♂", + "nidorino": "ニドリーノ", + "nidoking": "ニドキング", + "clefairy": "ピッピ", + "clefable": "ピクシー", + "vulpix": "ロコン", + "ninetales": "キュウコン", + "jigglypuff": "プリン", + "wigglytuff": "プクリン", + "zubat": "ズバット", + "golbat": "ゴルバット", + "oddish": "ナゾノクサ", + "gloom": "クサイハナ", + "vileplume": "ラフレシア", + "paras": "パラス", + "parasect": "パラセクト", + "venonat": "コンパン", + "venomoth": "モルフォン", + "diglett": "ディグダ", + "dugtrio": "ダグトリオ", + "meowth": "ニャース", + "persian": "ペルシアン", + "psyduck": "コダック", + "golduck": "ゴルダック", + "mankey": "マンキー", + "primeape": "オコリザル", + "growlithe": "ガーディ", + "arcanine": "ウインディ", + "poliwag": "ニョロモ", + "poliwhirl": "ニョロゾ", + "poliwrath": "ニョロボン", + "abra": "ケーシィ", + "kadabra": "ユンゲラー", + "alakazam": "フーディン", + "machop": "ワンリキー", + "machoke": "ゴーリキー", + "machamp": "カイリキー", + "bellsprout": "マダツボミ", + "weepinbell": "ウツドン", + "victreebel": "ウツボット", + "tentacool": "メノクラゲ", + "tentacruel": "ドククラゲ", + "geodude": "イシツブテ", + "graveler": "ゴローン", + "golem": "ゴローニャ", + "ponyta": "ポニータ", + "rapidash": "ギャロップ", + "slowpoke": "ヤドン", + "slowbro": "ヤドラン", + "magnemite": "コイル", + "magneton": "レアコイル", + "farfetchd": "カモネギ", + "doduo": "ドードー", + "dodrio": "ドードリオ", + "seel": "パウワウ", + "dewgong": "ジュゴン", + "grimer": "ベトベター", + "muk": "ベトベトン", + "shellder": "シェルダー", + "cloyster": "パルシェン", + "gastly": "ゴース", + "haunter": "ゴースト", + "gengar": "ゲンガー", + "onix": "イワーク", + "drowzee": "スリープ", + "hypno": "スリーパー", + "krabby": "クラブ", + "kingler": "キングラー", + "voltorb": "ビリリダマ", + "electrode": "マルマイン", + "exeggcute": "タマタマ", + "exeggutor": "ナッシー", + "cubone": "カラカラ", + "marowak": "ガラガラ", + "hitmonlee": "サワムラー", + "hitmonchan": "エビワラー", + "lickitung": "ベロリンガ", + "koffing": "ドガース", + "weezing": "マタドガス", + "rhyhorn": "サイホーン", + "rhydon": "サイドン", + "chansey": "ラッキー", + "tangela": "モンジャラ", + "kangaskhan": "ガルーラ", + "horsea": "タッツー", + "seadra": "シードラ", + "goldeen": "トサキント", + "seaking": "アズマオウ", + "staryu": "ヒトデマン", + "starmie": "スターミー", + "mr_mime": "バリヤード", + "scyther": "ストライク", + "jynx": "ルージュラ", + "electabuzz": "エレブー", + "magmar": "ブーバー", + "pinsir": "カイロス", + "tauros": "ケンタロス", + "magikarp": "コイキング", + "gyarados": "ギャラドス", + "lapras": "ラプラス", + "ditto": "メタモン", + "eevee": "イーブイ", + "vaporeon": "シャワーズ", + "jolteon": "サンダース", + "flareon": "ブースター", + "porygon": "ポリゴン", + "omanyte": "オムナイト", + "omastar": "オムスター", + "kabuto": "カブト", + "kabutops": "カブトプス", + "aerodactyl": "プテラ", + "snorlax": "カビゴン", + "articuno": "フリーザー", + "zapdos": "サンダー", + "moltres": "ファイヤー", + "dratini": "ミニリュウ", + "dragonair": "ハクリュー", + "dragonite": "カイリュー", + "mewtwo": "ミュウツー", + "mew": "ミュウ", + "chikorita": "チコリータ", + "bayleef": "ベイリーフ", + "meganium": "メガニウム", + "cyndaquil": "ヒノアラシ", + "quilava": "マグマラシ", + "typhlosion": "バクフーン", + "totodile": "ワニノコ", + "croconaw": "アリゲイツ", + "feraligatr": "オーダイル", + "sentret": "オタチ", + "furret": "オオタチ", + "hoothoot": "ホーホー", + "noctowl": "ヨルノズク", + "ledyba": "レディバ", + "ledian": "レディアン", + "spinarak": "イトマル", + "ariados": "アリアドス", + "crobat": "クロバット", + "chinchou": "チョンチー", + "lanturn": "ランターン", + "pichu": "ピチュー", + "cleffa": "ピィ", + "igglybuff": "ププリン", + "togepi": "トゲピー", + "togetic": "トゲチック", + "natu": "ネイティ", + "xatu": "ネイティオ", + "mareep": "メリープ", + "flaaffy": "モココ", + "ampharos": "デンリュウ", + "bellossom": "キレイハナ", + "marill": "マリル", + "azumarill": "マリルリ", + "sudowoodo": "ウソッキー", + "politoed": "ニョロトノ", + "hoppip": "ハネッコ", + "skiploom": "ポポッコ", + "jumpluff": "ワタッコ", + "aipom": "エイパム", + "sunkern": "ヒマナッツ", + "sunflora": "キマワリ", + "yanma": "ヤンヤンマ", + "wooper": "ウパー", + "quagsire": "ヌオー", + "espeon": "エーフィ", + "umbreon": "ブラッキー", + "murkrow": "ヤミカラス", + "slowking": "ヤドキング", + "misdreavus": "ムウマ", + "unown": "アンノーン", + "wobbuffet": "ソーナンス", + "girafarig": "キリンリキ", + "pineco": "クヌギダマ", + "forretress": "フォレトス", + "dunsparce": "ノコッチ", + "gligar": "グライガー", + "steelix": "ハガネール", + "snubbull": "ブルー", + "granbull": "グランブル", + "qwilfish": "ハリーセン", + "scizor": "ハッサム", + "shuckle": "ツボツボ", + "heracross": "ヘラクロス", + "sneasel": "ニューラ", + "teddiursa": "ヒメグマ", + "ursaring": "リングマ", + "slugma": "マグマッグ", + "magcargo": "マグカルゴ", + "swinub": "ウリムー", + "piloswine": "イノムー", + "corsola": "サニーゴ", + "remoraid": "テッポウオ", + "octillery": "オクタン", + "delibird": "デリバード", + "mantine": "マンタイン", + "skarmory": "エアームド", + "houndour": "デルビル", + "houndoom": "ヘルガー", + "kingdra": "キングドラ", + "phanpy": "ゴマゾウ", + "donphan": "ドンファン", + "porygon2": "ポリゴン2", + "stantler": "オドシシ", + "smeargle": "ドーブル", + "tyrogue": "バルキー", + "hitmontop": "カポエラー", + "smoochum": "ムチュール", + "elekid": "エレキッド", + "magby": "ブビィ", + "miltank": "ミルタンク", + "blissey": "ハピナス", + "raikou": "ライコウ", + "entei": "エンテイ", + "suicune": "スイクン", + "larvitar": "ヨーギラス", + "pupitar": "サナギラス", + "tyranitar": "バンギラス", + "lugia": "ルギア", + "ho_oh": "ホウオウ", + "celebi": "セレビィ", + "treecko": "キモリ", + "grovyle": "ジュプトル", + "sceptile": "ジュカイン", + "torchic": "アチャモ", + "combusken": "ワカシャモ", + "blaziken": "バシャーモ", + "mudkip": "ミズゴロウ", + "marshtomp": "ヌマクロー", + "swampert": "ラグラージ", + "poochyena": "ポチエナ", + "mightyena": "グラエナ", + "zigzagoon": "ジグザグマ", + "linoone": "マッスグマ", + "wurmple": "ケムッソ", + "silcoon": "カラサリス", + "beautifly": "アゲハント", + "cascoon": "マユルド", + "dustox": "ドクケイル", + "lotad": "ハスボー", + "lombre": "ハスブレロ", + "ludicolo": "ルンパッパ", + "seedot": "タネボー", + "nuzleaf": "コノハナ", + "shiftry": "ダーテング", + "taillow": "スバメ", + "swellow": "オオスバメ", + "wingull": "キャモメ", + "pelipper": "ペリッパー", + "ralts": "ラルトス", + "kirlia": "キルリア", + "gardevoir": "サーナイト", + "surskit": "アメタマ", + "masquerain": "アメモース", + "shroomish": "キノココ", + "breloom": "キノガッサ", + "slakoth": "ナマケロ", + "vigoroth": "ヤルキモノ", + "slaking": "ケッキング", + "nincada": "ツチニン", + "ninjask": "テッカニン", + "shedinja": "ヌケニン", + "whismur": "ゴニョニョ", + "loudred": "ドゴーム", + "exploud": "バクオング", + "makuhita": "マクノシタ", + "hariyama": "ハリテヤマ", + "azurill": "ルリリ", + "nosepass": "ノズパス", + "skitty": "エネコ", + "delcatty": "エネコロロ", + "sableye": "ヤミラミ", + "mawile": "クチート", + "aron": "ココドラ", + "lairon": "コドラ", + "aggron": "ボスゴドラ", + "meditite": "アサナン", + "medicham": "チャーレム", + "electrike": "ラクライ", + "manectric": "ライボルト", + "plusle": "プラスル", + "minun": "マイナン", + "volbeat": "バルビート", + "illumise": "イルミーゼ", + "roselia": "ロゼリア", + "gulpin": "ゴクリン", + "swalot": "マルノーム", + "carvanha": "キバニア", + "sharpedo": "サメハダー", + "wailmer": "ホエルコ", + "wailord": "ホエルオー", + "numel": "ドンメル", + "camerupt": "バクーダ", + "torkoal": "コータス", + "spoink": "バネブー", + "grumpig": "ブーピッグ", + "spinda": "パッチール", + "trapinch": "ナックラー", + "vibrava": "ビブラーバ", + "flygon": "フライゴン", + "cacnea": "サボネア", + "cacturne": "ノクタス", + "swablu": "チルット", + "altaria": "チルタリス", + "zangoose": "ザングース", + "seviper": "ハブネーク", + "lunatone": "ルナトーン", + "solrock": "ソルロック", + "barboach": "ドジョッチ", + "whiscash": "ナマズン", + "corphish": "ヘイガニ", + "crawdaunt": "シザリガー", + "baltoy": "ヤジロン", + "claydol": "ネンドール", + "lileep": "リリーラ", + "cradily": "ユレイドル", + "anorith": "アノプス", + "armaldo": "アーマルド", + "feebas": "ヒンバス", + "milotic": "ミロカロス", + "castform": "ポワルン", + "kecleon": "カクレオン", + "shuppet": "カゲボウズ", + "banette": "ジュペッタ", + "duskull": "ヨマワル", + "dusclops": "サマヨール", + "tropius": "トロピウス", + "chimecho": "チリーン", + "absol": "アブソル", + "wynaut": "ソーナノ", + "snorunt": "ユキワラシ", + "glalie": "オニゴーリ", + "spheal": "タマザラシ", + "sealeo": "トドグラー", + "walrein": "トドゼルガ", + "clamperl": "パールル", + "huntail": "ハンテール", + "gorebyss": "サクラビス", + "relicanth": "ジーランス", + "luvdisc": "ラブカス", + "bagon": "タツベイ", + "shelgon": "コモルー", + "salamence": "ボーマンダ", + "beldum": "ダンバル", + "metang": "メタング", + "metagross": "メタグロス", + "regirock": "レジロック", + "regice": "レジアイス", + "registeel": "レジスチル", + "latias": "ラティアス", + "latios": "ラティオス", + "kyogre": "カイオーガ", + "groudon": "グラードン", + "rayquaza": "レックウザ", + "jirachi": "ジラーチ", + "deoxys": "デオキシス", + "turtwig": "ナエトル", + "grotle": "ハヤシガメ", + "torterra": "ドダイトス", + "chimchar": "ヒコザル", + "monferno": "モウカザル", + "infernape": "ゴウカザル", + "piplup": "ポッチャマ", + "prinplup": "ポッタイシ", + "empoleon": "エンペルト", + "starly": "ムックル", + "staravia": "ムクバード", + "staraptor": "ムクホーク", + "bidoof": "ビッパ", + "bibarel": "ビーダル", + "kricketot": "コロボーシ", + "kricketune": "コロトック", + "shinx": "コリンク", + "luxio": "ルクシオ", + "luxray": "レントラー", + "budew": "スボミー", + "roserade": "ロズレイド", + "cranidos": "ズガイドス", + "rampardos": "ラムパルド", + "shieldon": "タテトプス", + "bastiodon": "トリデプス", + "burmy": "ミノムッチ", + "wormadam": "ミノマダム", + "mothim": "ガーメイル", + "combee": "ミツハニー", + "vespiquen": "ビークイン", + "pachirisu": "パチリス", + "buizel": "ブイゼル", + "floatzel": "フローゼル", + "cherubi": "チェリンボ", + "cherrim": "チェリム", + "shellos": "カラナクシ", + "gastrodon": "トリトドン", + "ambipom": "エテボース", + "drifloon": "フワンテ", + "drifblim": "フワライド", + "buneary": "ミミロル", + "lopunny": "ミミロップ", + "mismagius": "ムウマージ", + "honchkrow": "ドンカラス", + "glameow": "ニャルマー", + "purugly": "ブニャット", + "chingling": "リーシャン", + "stunky": "スカンプー", + "skuntank": "スカタンク", + "bronzor": "ドーミラー", + "bronzong": "ドータクン", + "bonsly": "ウソハチ", + "mime_jr.": "マネネ", + "happiny": "ピンプク", + "chatot": "ペラップ", + "spiritomb": "ミカルゲ", + "gible": "フカマル", + "gabite": "ガバイト", + "garchomp": "ガブリアス", + "munchlax": "ゴンベ", + "riolu": "リオル", + "lucario": "ルカリオ", + "hippopotas": "ヒポポタス", + "hippowdon": "カバルドン", + "skorupi": "スコルピ", + "drapion": "ドラピオン", + "croagunk": "グレッグル", + "toxicroak": "ドクロッグ", + "carnivine": "マスキッパ", + "finneon": "ケイコウオ", + "lumineon": "ネオラント", + "mantyke": "タマンタ", + "snover": "ユキカブリ", + "abomasnow": "ユキノオー", + "weavile": "マニューラ", + "magnezone": "ジバコイル", + "lickilicky": "ベロベルト", + "rhyperior": "ドサイドン", + "tangrowth": "モジャンボ", + "electivire": "エレキブル", + "magmortar": "ブーバーン", + "togekiss": "トゲキッス", + "yanmega": "メガヤンマ", + "leafeon": "リーフィア", + "glaceon": "グレイシア", + "gliscor": "グライオン", + "mamoswine": "マンムー", + "porygon_z": "ポリゴンZ", + "gallade": "エルレイド", + "probopass": "ダイノーズ", + "dusknoir": "ヨノワール", + "froslass": "ユキメノコ", + "rotom": "ロトム", + "uxie": "ユクシー", + "mesprit": "エムリット", + "azelf": "アグノム", + "dialga": "ディアルガ", + "palkia": "パルキア", + "heatran": "ヒードラン", + "regigigas": "レジギガス", + "giratina": "ギラティナ", + "cresselia": "クレセリア", + "phione": "フィオネ", + "manaphy": "マナフィ", + "darkrai": "ダークライ", + "shaymin": "シェイミ", + "arceus": "アルセウス", + "victini": "ビクティニ", + "snivy": "ツタージャ", + "servine": "ジャノビー", + "serperior": "ジャローダ", + "tepig": "ポカブ", + "pignite": "チャオブー", + "emboar": "エンブオー", + "oshawott": "ミジュマル", + "dewott": "フタチマル", + "samurott": "ダイケンキ", + "patrat": "ミネズミ", + "watchog": "ミルホッグ", + "lillipup": "ヨーテリー", + "herdier": "ハーデリア", + "stoutland": "ムーランド", + "purrloin": "チョロネコ", + "liepard": "レパルダス", + "pansage": "ヤナップ", + "simisage": "ヤナッキー", + "pansear": "バオップ", + "simisear": "バオッキー", + "panpour": "ヒヤップ", + "simipour": "ヒヤッキー", + "munna": "ムンナ", + "musharna": "ムシャーナ", + "pidove": "マメパト", + "tranquill": "ハトーボー", + "unfezant": "ケンホロウ", + "blitzle": "シママ", + "zebstrika": "ゼブライカ", + "roggenrola": "ダンゴロ", + "boldore": "ガントル", + "gigalith": "ギガイアス", + "woobat": "コロモリ", + "swoobat": "ココロモリ", + "drilbur": "モグリュー", + "excadrill": "ドリュウズ", + "audino": "タブンネ", + "timburr": "ドッコラー", + "gurdurr": "ドテッコツ", + "conkeldurr": "ローブシン", + "tympole": "オタマロ", + "palpitoad": "ガマガル", + "seismitoad": "ガマゲロゲ", + "throh": "ナゲキ", + "sawk": "ダゲキ", + "sewaddle": "クルミル", + "swadloon": "クルマユ", + "leavanny": "ハハコモリ", + "venipede": "フシデ", + "whirlipede": "ホイーガ", + "scolipede": "ペンドラー", + "cottonee": "モンメン", + "whimsicott": "エルフーン", + "petilil": "チュリネ", + "lilligant": "ドレディア", + "basculin": "バスラオ", + "sandile": "メグロコ", + "krokorok": "ワルビル", + "krookodile": "ワルビアル", + "darumaka": "ダルマッカ", + "darmanitan": "ヒヒダルマ", + "maractus": "マラカッチ", + "dwebble": "イシズマイ", + "crustle": "イワパレス", + "scraggy": "ズルッグ", + "scrafty": "ズルズキン", + "sigilyph": "シンボラー", + "yamask": "デスマス", + "cofagrigus": "デスカーン", + "tirtouga": "プロトーガ", + "carracosta": "アバゴーラ", + "archen": "アーケン", + "archeops": "アーケオス", + "trubbish": "ヤブクロン", + "garbodor": "ダストダス", + "zorua": "ゾロア", + "zoroark": "ゾロアーク", + "minccino": "チラーミィ", + "cinccino": "チラチーノ", + "gothita": "ゴチム", + "gothorita": "ゴチミル", + "gothitelle": "ゴチルゼル", + "solosis": "ユニラン", + "duosion": "ダブラン", + "reuniclus": "ランクルス", + "ducklett": "コアルヒー", + "swanna": "スワンナ", + "vanillite": "バニプッチ", + "vanillish": "バニリッチ", + "vanilluxe": "バイバニラ", + "deerling": "シキジカ", + "sawsbuck": "メブキジカ", + "emolga": "エモンガ", + "karrablast": "カブルモ", + "escavalier": "シュバルゴ", + "foongus": "タマゲタケ", + "amoonguss": "モロバレル", + "frillish": "プルリル", + "jellicent": "ブルンゲル", + "alomomola": "ママンボウ", + "joltik": "バチュル", + "galvantula": "デンチュラ", + "ferroseed": "テッシード", + "ferrothorn": "ナットレイ", + "klink": "ギアル", + "klang": "ギギアル", + "klinklang": "ギギギアル", + "tynamo": "シビシラス", + "eelektrik": "シビビール", + "eelektross": "シビルドン", + "elgyem": "リグレー", + "beheeyem": "オーベム", + "litwick": "ヒトモシ", + "lampent": "ランプラー", + "chandelure": "シャンデラ", + "axew": "キバゴ", + "fraxure": "オノンド", + "haxorus": "オノノクス", + "cubchoo": "クマシュン", + "beartic": "ツンベアー", + "cryogonal": "フリージオ", + "shelmet": "チョボマキ", + "accelgor": "アギルダー", + "stunfisk": "マッギョ", + "mienfoo": "コジョフー", + "mienshao": "コジョンド", + "druddigon": "クリムガン", + "golett": "ゴビット", + "golurk": "ゴルーグ", + "pawniard": "コマタナ", + "bisharp": "キリキザン", + "bouffalant": "バッフロン", + "rufflet": "ワシボン", + "braviary": "ウォーグル", + "vullaby": "バルチャイ", + "mandibuzz": "バルジーナ", + "heatmor": "クイタラン", + "durant": "アイアント", + "deino": "モノズ", + "zweilous": "ジヘッド", + "hydreigon": "サザンドラ", + "larvesta": "メラルバ", + "volcarona": "ウルガモス", + "cobalion": "コバルオン", + "terrakion": "テラキオン", + "virizion": "ビリジオン", + "tornadus": "トルネロス", + "thundurus": "ボルトロス", + "reshiram": "レシラム", + "zekrom": "ゼクロム", + "landorus": "ランドロス", + "kyurem": "キュレム", + "keldeo": "ケルディオ", + "meloetta": "メロエッタ", + "genesect": "ゲノセクト", + "chespin": "ハリマロン", + "quilladin": "ハリボーグ", + "chesnaught": "ブリガロン", + "fennekin": "フォッコ", + "braixen": "テールナー", + "delphox": "マフォクシー", + "froakie": "ケロマツ", + "frogadier": "ゲコガシラ", + "greninja": "ゲッコウガ", + "bunnelby": "ホルビー", + "diggersby": "ホルード", + "fletchling": "ヤヤコマ", + "fletchinder": "ヒノヤコマ", + "talonflame": "ファイアロー", + "scatterbug": "コフキムシ", + "spewpa": "コフーライ", + "vivillon": "ビビヨン", + "litleo": "シシコ", + "pyroar": "カエンジシ", + "flabebe": "フラベベ", + "floette": "フラエッテ", + "florges": "フラージェス", + "skiddo": "メェークル", + "gogoat": "ゴーゴート", + "pancham": "ヤンチャム", + "pangoro": "ゴロンダ", + "furfrou": "トリミアン", + "espurr": "ニャスパー", + "meowstic": "ニャオニクス", + "honedge": "ヒトツキ", + "doublade": "ニダンギル", + "aegislash": "ギルガルド", + "spritzee": "シュシュプ", + "aromatisse": "フレフワン", + "swirlix": "ペロッパフ", + "slurpuff": "ペロリーム", + "inkay": "マーイーカ", + "malamar": "カラマネロ", + "binacle": "カメテテ", + "barbaracle": "ガメノデス", + "skrelp": "クズモー", + "dragalge": "ドラミドロ", + "clauncher": "ウデッポウ", + "clawitzer": "ブロスター", + "helioptile": "エリキテル", + "heliolisk": "エレザード", + "tyrunt": "チゴラス", + "tyrantrum": "ガチゴラス", + "amaura": "アマルス", + "aurorus": "アマルルガ", + "sylveon": "ニンフィア", + "hawlucha": "ルチャブル", + "dedenne": "デデンネ", + "carbink": "メレシー", + "goomy": "ヌメラ", + "sliggoo": "ヌメイル", + "goodra": "ヌメルゴン", + "klefki": "クレッフィ", + "phantump": "ボクレー", + "trevenant": "オーロット", + "pumpkaboo": "バケッチャ", + "gourgeist": "パンプジン", + "bergmite": "カチコール", + "avalugg": "クレベース", + "noibat": "オンバット", + "noivern": "オンバーン", + "xerneas": "ゼルネアス", + "yveltal": "イベルタル", + "zygarde": "ジガルデ", + "diancie": "ディアンシー", + "hoopa": "フーパ", + "volcanion": "ボルケニオン", + "rowlet": "モクロー", + "dartrix": "フクスロー", + "decidueye": "ジュナイパー", + "litten": "ニャビー", + "torracat": "ニャヒート", + "incineroar": "ガオガエン", + "popplio": "アシマリ", + "brionne": "オシャマリ", + "primarina": "アシレーヌ", + "pikipek": "ツツケラ", + "trumbeak": "ケララッパ", + "toucannon": "ドデカバシ", + "yungoos": "ヤングース", + "gumshoos": "デカグース", + "grubbin": "アゴジムシ", + "charjabug": "デンヂムシ", + "vikavolt": "クワガノン", + "crabrawler": "マケンカニ", + "crabominable": "ケケンカニ", + "oricorio": "オドリドリ", + "cutiefly": "アブリー", + "ribombee": "アブリボン", + "rockruff": "イワンコ", + "lycanroc": "ルガルガン", + "wishiwashi": "ヨワシ", + "mareanie": "ヒドイデ", + "toxapex": "ドヒドイデ", + "mudbray": "ドロバンコ", + "mudsdale": "バンバドロ", + "dewpider": "シズクモ", + "araquanid": "オニシズクモ", + "fomantis": "カリキリ", + "lurantis": "ラランテス", + "morelull": "ネマシュ", + "shiinotic": "マシェード", + "salandit": "ヤトウモリ", + "salazzle": "エンニュート", + "stufful": "ヌイコグマ", + "bewear": "キテルグマ", + "bounsweet": "アマカジ", + "steenee": "アママイコ", + "tsareena": "アマージョ", + "comfey": "キュワワー", + "oranguru": "ヤレユータン", + "passimian": "ナゲツケサル", + "wimpod": "コソクムシ", + "golisopod": "グソクムシャ", + "sandygast": "スナバァ", + "palossand": "シロデスナ", + "pyukumuku": "ナマコブシ", + "type:_null": "タイプ:ヌル", + "silvally": "シルヴァディ", + "minior": "メテノ", + "komala": "ネッコアラ", + "turtonator": "バクガメス", + "togedemaru": "トゲデマル", + "mimikyu": "ミミッキュ", + "bruxish": "ハギギシリ", + "drampa": "ジジーロン", + "dhelmise": "ダダリン", + "jangmo_o": "ジャラコ", + "hakamo_o": "ジャランゴ", + "kommo_o": "ジャラランガ", + "tapu_koko": "カプ・コケコ", + "tapu_lele": "カプ・テテフ", + "tapu_bulu": "カプ・ブルル", + "tapu_fini": "カプ・レヒレ", + "cosmog": "コスモッグ", + "cosmoem": "コスモウム", + "solgaleo": "ソルガレオ", + "lunala": "ルナアーラ", + "nihilego": "ウツロイド", + "buzzwole": "マッシブーン", + "pheromosa": "フェローチェ", + "xurkitree": "デンジュモク", + "celesteela": "テッカグヤ", + "kartana": "カミツルギ", + "guzzlord": "アクジキング", + "necrozma": "ネクロズマ", + "magearna": "マギアナ", + "marshadow": "マーシャドー", + "poipole": "ベベノム", + "naganadel": "アーゴヨン", + "stakataka": "ツンデツンデ", + "blacephalon": "ズガドーン", + "zeraora": "ゼラオラ", + "meltan": "メルタン", + "melmetal": "メルメタル", + "grookey": "サルノリ", + "thwackey": "バチンキー", + "rillaboom": "ゴリランダー", + "scorbunny": "ヒバニー", + "raboot": "ラビフット", + "cinderace": "エースバーン", + "sobble": "メッソン", + "drizzile": "ジメレオン", + "inteleon": "インテレオン", + "skwovet": "ホシガリス", + "greedent": "ヨクバリス", + "rookidee": "ココガラ", + "corvisquire": "アオガラス", + "corviknight": "アーマーガア", + "blipbug": "サッチムシ", + "dottler": "レドームシ", + "orbeetle": "イオルブ", + "nickit": "クスネ", + "thievul": "フォクスライ", + "gossifleur": "ヒメンカ", + "eldegoss": "ワタシラガ", + "wooloo": "ウールー", + "dubwool": "バイウールー", + "chewtle": "カムカメ", + "drednaw": "カジリガメ", + "yamper": "ワンパチ", + "boltund": "パルスワン", + "rolycoly": "タンドン", + "carkol": "トロッゴン", + "coalossal": "セキタンザン", + "applin": "カジッチュ", + "flapple": "アップリュー", + "appletun": "タルップル", + "silicobra": "スナヘビ", + "sandaconda": "サダイジャ", + "cramorant": "ウッウ", + "arrokuda": "サシカマス", + "barraskewda": "カマスジョー", + "toxel": "エレズン", + "toxtricity": "ストリンダー", + "sizzlipede": "ヤクデ", + "centiskorch": "マルヤクデ", + "clobbopus": "タタッコ", + "grapploct": "オトスパス", + "sinistea": "ヤバチャ", + "polteageist": "ポットデス", + "hatenna": "ミブリム", + "hattrem": "テブリム", + "hatterene": "ブリムオン", + "impidimp": "ベロバー", + "morgrem": "ギモー", + "grimmsnarl": "オーロンゲ", + "obstagoon": "タチフサグマ", + "perrserker": "ニャイキング", + "cursola": "サニゴーン", + "sirfetch_d": "ネギガナイト", + "mr_rime": "バリコオル", + "runerigus": "デスバーン", + "milcery": "マホミル", + "alcremie": "マホイップ", + "falinks": "タイレーツ", + "pincurchin": "バチンウニ", + "snom": "ユキハミ", + "frosmoth": "モスノウ", + "stonjourner": "イシヘンジン", + "eiscue": "コオリッポ", + "indeedee": "イエッサン", + "morpeko": "モルペコ", + "cufant": "ゾウドウ", + "copperajah": "ダイオウドウ", + "dracozolt": "パッチラゴン", + "arctozolt": "パッチルドン", + "dracovish": "ウオノラゴン", + "arctovish": "ウオチルドン", + "duraludon": "ジュラルドン", + "dreepy": "ドラメシヤ", + "drakloak": "ドロンチ", + "dragapult": "ドラパルト", + "zacian": "ザシアン", + "zamazenta": "ザマゼンタ", + "eternatus": "ムゲンダイナ", + "kubfu": "ダクマ", + "urshifu": "ウーラオス", + "zarude": "ザルード", + "regieleki": "レジエレキ", + "regidrago": "レジドラゴ", + "glastrier": "ブリザポス", + "spectrier": "レイスポス", + "calyrex": "バドレックス", + "wyrdeer": "アヤシシ", + "kleavor": "バサギリ", + "ursaluna": "ガチグマ", + "basculegion": "イダイトウ", + "sneasler": "オオニューラ", + "overqwil": "ハリーマン", + "enamorus": "ラブトロス", + "sprigatito": "ニャオハ", + "floragato": "ニャローテ", + "meowscarada": "マスカーニャ", + "fuecoco": "ホゲータ", + "crocalor": "アチゲータ", + "skeledirge": "ラウドボーン", + "quaxly": "クワッス", + "quaxwell": "ウェルカモ", + "quaquaval": "ウェーニバル", + "lechonk": "グルトン", + "oinkologne": "パフュートン", + "tarountula": "タマンチュラ", + "spidops": "ワナイダー", + "nymble": "マメバッタ", + "lokix": "エクスレッグ", + "pawmi": "パモ", + "pawmo": "パモット", + "pawmot": "パーモット", + "tandemaus": "ワッカネズミ", + "maushold": "イッカネズミ", + "fidough": "パピモッチ", + "dachsbun": "バウッツェル", + "smoliv": "ミニーブ", + "dolliv": "オリーニョ", + "arboliva": "オリーヴァ", + "squawkabilly": "イキリンコ", + "nacli": "コジオ", + "naclstack": "ジオヅム", + "garganacl": "キョジオーン", + "charcadet": "カルボウ", + "armarouge": "グレンアルマ", + "ceruledge": "ソウブレイズ", + "tadbulb": "ズピカ", + "bellibolt": "ハラバリー", + "wattrel": "カイデン", + "kilowattrel": "タイカイデン", + "maschiff": "オラチフ", + "mabosstiff": "マフィティフ", + "shroodle": "シルシュルー", + "grafaiai": "タギングル", + "bramblin": "アノクサ", + "brambleghast": "アノホラグサ", + "toedscool": "ノノクラゲ", + "toedscruel": "リククラゲ", + "klawf": "ガケガニ", + "capsakid": "カプサイジ", + "scovillain": "スコヴィラン", + "rellor": "シガロコ", + "rabsca": "ベラカス", + "flittle": "ヒラヒナ", + "espathra": "クエスパトラ", + "tinkatink": "カヌチャン", + "tinkatuff": "ナカヌチャン", + "tinkaton": "デカヌチャン", + "wiglett": "ウミディグダ", + "wugtrio": "ウミトリオ", + "bombirdier": "オトシドリ", + "finizen": "ナミイルカ", + "palafin": "イルカマン", + "varoom": "ブロロン", + "revavroom": "ブロロローム", + "cyclizar": "モトトカゲ", + "orthworm": "ミミズズ", + "glimmet": "キラーメ", + "glimmora": "キラフロル", + "greavard": "ボチ", + "houndstone": "ハカドッグ", + "flamigo": "カラミンゴ", + "cetoddle": "アルクジラ", + "cetitan": "ハルクジラ", + "veluza": "ミガルーサ", + "dondozo": "ヘイラッシャ", + "tatsugiri": "シャリタツ", + "annihilape": "コノヨザル", + "clodsire": "ドオー", + "farigiraf": "リキキリン", + "dudunsparce": "ノココッチ", + "kingambit": "ドドゲザン", + "great_tusk": "イダイナキバ", + "scream_tail": "サケブシッポ", + "brute_bonnet": "アラブルタケ", + "flutter_mane": "ハバタクカミ", + "slither_wing": "チヲハウハネ", + "sandy_shocks": "スナノケガワ", + "iron_treads": "テツノワダチ", + "iron_bundle": "テツノツツミ", + "iron_hands": "テツノカイナ", + "iron_jugulis": "テツノコウベ", + "iron_moth": "テツノドクガ", + "iron_thorns": "テツノイバラ", + "frigibax": "セビエ", + "arctibax": "セゴール", + "baxcalibur": "セグレイブ", + "gimmighoul": "コレクレー", + "gholdengo": "サーフゴー", + "wo_chien": "チオンジェン", + "chien_pao": "パオジアン", + "ting_lu": "ディンルー", + "chi_yu": "イーユイ", + "roaring_moon": "トドロクツキ", + "iron_valiant": "テツノブジン", + "koraidon": "コライドン", + "miraidon": "ミライドン", + "walking_wake": "ウネルミナモ", + "iron_leaves": "テツノイサハ ", + "dipplin": "カミッチュ", + "poltchageist": "チャデス", + "sinistcha": "ヤバソチャ", + "okidogi": "イイネイヌ", + "munkidori": "マシマシラ", + "fezandipiti": "キチキギス", + "ogerpon": "オーガポン", + "archaludon": "ブリジュラス", + "hydrapple": "カミツオロチ", + "gouging_fire": "ウガツホムラ", + "raging_bolt": "タケルライコ", + "iron_boulder": "テツノイワオ", + "iron_crown": "テツノカシラ", + "terapagos": "テラパゴス", + "pecharunt": "モモワロウ", + "alola_rattata": "コラッタ", + "alola_raticate": "ラッタ", + "alola_raichu": "ライチュウ", + "alola_sandshrew": "サンド", + "alola_sandslash": "サンドパン", + "alola_vulpix": "ロコン", + "alola_ninetales": "キュウコン", + "alola_diglett": "ディグダ", + "alola_dugtrio": "ダグトリオ", + "alola_meowth": "ニャース", + "alola_persian": "ペルシアン", + "alola_geodude": "イシツブテ", + "alola_graveler": "ゴローン", + "alola_golem": "ゴローニャ", + "alola_grimer": "ベトベター", + "alola_muk": "ベトベトン", + "alola_exeggutor": "ナッシー", + "alola_marowak": "ガラガラ", + "eternal_floette": "フラエッテ", + "galar_meowth": "ニャース", + "galar_ponyta": "ポニータ", + "galar_rapidash": "ギャロップ", + "galar_slowpoke": "ヤドン", + "galar_slowbro": "ヤドラン", + "galar_farfetchd": "カモネギ", + "galar_weezing": "マタドガス", + "galar_mr_mime": "バリヤード", + "galar_articuno": "フリーザー", + "galar_zapdos": "サンダー", + "galar_moltres": "ファイヤー", + "galar_slowking": "ヤドキング", + "galar_corsola": "サニーゴ", + "galar_zigzagoon": "ジグザグマ", + "galar_linoone": "マッスグマ", + "galar_darumaka": "ダルマッカ", + "galar_darmanitan": "ヒヒダルマ", + "galar_yamask": "デスマス", + "galar_stunfisk": "マッギョ", + "hisui_growlithe": "ガーディ", + "hisui_arcanine": "ウインディ", + "hisui_voltorb": "ビリリダマ", + "hisui_electrode": "マルマイン", + "hisui_typhlosion": "バクフーン", + "hisui_qwilfish": "ハリーセン", + "hisui_sneasel": "ニューラ", + "hisui_samurott": "ダイケンキ", + "hisui_lilligant": "ドレディア", + "hisui_zorua": "ゾロア", + "hisui_zoroark": "ゾロアーク", + "hisui_braviary": "ウォーグル", + "hisui_sliggoo": "ヌメイル", + "hisui_goodra": "ヌメルゴン", + "hisui_avalugg": "クレベース", + "hisui_decidueye": "ジュナイパー", + "paldea_tauros": "ケンタロス", + "paldea_wooper": "ウパー", + "bloodmoon_ursaluna": "ガチグマ", +} as const; diff --git a/src/locales/ja/save-slot-select-ui-handler.ts b/src/locales/ja/save-slot-select-ui-handler.ts new file mode 100644 index 00000000000..115cd9987b8 --- /dev/null +++ b/src/locales/ja/save-slot-select-ui-handler.ts @@ -0,0 +1,9 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const saveSlotSelectUiHandler: SimpleTranslationEntries = { + "overwriteData": "せんたくしたスロットにデータをうわがきしますか?", + "loading": "ローディング...", + "wave": "Wave", + "lv": "Lv", + "empty": "なし", +} as const; diff --git a/src/locales/ja/settings.ts b/src/locales/ja/settings.ts new file mode 100644 index 00000000000..ef20d071d2d --- /dev/null +++ b/src/locales/ja/settings.ts @@ -0,0 +1,101 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales.js"; + +export const settings: SimpleTranslationEntries = { + "boy": "男の子", + "girl": "女の子", + "general": "全般", + "display": "ディスプレイ", + "audio": "音声", + "gamepad": "コントローラー", + "keyboard": "キーボード", + "gameSpeed": "ゲームスピード", + "hpBarSpeed": "HPバーの増減スピード", + "expGainsSpeed": "EXPバーの増加スピード", + "expPartyDisplay": "パーティの経験値取得表示", + "skipSeenDialogues": "Skip Seen Dialogues", + "battleStyle": "試合のルール", + "enableRetries": "リトライを有効にする", + "hideIvs": "Hide IV scanner", + "tutorials": "チュートリアル", + "touchControls": "タッチ操作", + "vibrations": "振動", + "normal": "普通", + "fast": "早い", + "faster": "とても早い", + "skip": "スキップ", + "levelUpNotifications": "レベルアップ時のみ", + "on": "オン", + "off": "オフ", + "switch": "入れ替え", + "set": "勝ち抜き", + "auto": "自動", + "disabled": "無効", + "language": "言語", + "change": "変更", + "uiTheme": "UIテーマ", + "default": "デフォルト", + "legacy": "レガシー", + "windowType": "ウィンドウタイプ", + "moneyFormat": "お金の表示形式", + "damageNumbers": "ダメージ表示", + "simple": "シンプル", + "fancy": "Fancy", + "abbreviated": "省略", + "moveAnimations": "戦闘アニメ", + "showStatsOnLevelUp": "レベルアップ時のステータス表示", + "candyUpgradeNotification": "Candy Upgrade Notification", + "passivesOnly": "パッシブのみ", + "candyUpgradeDisplay": "Candy Upgrade Display", + "icon": "アイコン", + "animation": "アニメーション", + "moveInfo": "技の情報表示", + "showMovesetFlyout": "Show Moveset Flyout", + "showArenaFlyout": "Show Arena Flyout", + "showTimeOfDayWidget": "Show Time of Day Widget", + "timeOfDayAnimation": "Time of Day Animation", + "bounce": "Bounce", + "timeOfDay_back": "Back", + "spriteSet": "Sprite Set", + "consistent": "Consistent", + "mixedAnimated": "Mixed Animated", + "fusionPaletteSwaps": "Fusion Palette Swaps", + "playerGender": "プレイヤーの性別", + "typeHints": "相性のヒント", + "masterVolume": "マスターボリューム", + "bgmVolume": "BGMのボリューム", + "seVolume": "SEのボリューム", + "musicPreference": "Music Preference", + "mixed": "Mixed", + "gamepadPleasePlug": "Please Plug in a Gamepad or Press a Button", + "delete": "Delete", + "keyboardPleasePress": "Please Press a Key on Your Keyboard", + "reset": "リセット", + "requireReload": "再読み込みが必要", + "action": "決定", + "back": "戻る", + "pressToBind": "Press to Bind", + "pressButton": "Press a Button...", + "buttonUp": "上", + "buttonDown": "下", + "buttonLeft": "左", + "buttonRight": "右", + "buttonAction": "決定", + "buttonMenu": "メニュー", + "buttonSubmit": "Submit", + "buttonCancel": "キャンセル", + "buttonStats": "Stats", + "buttonCycleForm": "Cycle Form", + "buttonCycleShiny": "Cycle Shiny", + "buttonCycleGender": "Cycle Gender", + "buttonCycleAbility": "Cycle Ability", + "buttonCycleNature": "Cycle Nature", + "buttonCycleVariant": "Cycle Variant", + "buttonSpeedUp": "Speed Up", + "buttonSlowDown": "Slow Down", + "alt": " (代替)", + "mute": "ミュート", + "controller": "コントローラー", + "gamepadSupport": "コントローラーサポート", + "showBgmBar": "Show Music Names", + "shopOverlayOpacity": "Shop Overlay Opacity", +} as const; diff --git a/src/locales/ja/splash-messages.ts b/src/locales/ja/splash-messages.ts new file mode 100644 index 00000000000..e549bc24f19 --- /dev/null +++ b/src/locales/ja/splash-messages.ts @@ -0,0 +1,38 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const splashMessages: SimpleTranslationEntries = { + "battlesWon": "Battles Won!", + "joinTheDiscord": "Join the Discord!", + "infiniteLevels": "Infinite Levels!", + "everythingStacks": "Everything Stacks!", + "optionalSaveScumming": "Optional Save Scumming!", + "biomes": "35 Biomes!", + "openSource": "Open Source!", + "playWithSpeed": "Play with 5x Speed!", + "liveBugTesting": "Live Bug Testing!", + "heavyInfluence": "Heavy RoR2 Influence!", + "pokemonRiskAndPokemonRain": "Pokémon Risk and Pokémon Rain!", + "nowWithMoreSalt": "Now with 33% More Salt!", + "infiniteFusionAtHome": "Infinite Fusion at Home!", + "brokenEggMoves": "Broken Egg Moves!", + "magnificent": "Magnificent!", + "mubstitute": "Mubstitute!", + "thatsCrazy": "That's Crazy!", + "oranceJuice": "Orance Juice!", + "questionableBalancing": "Questionable Balancing!", + "coolShaders": "Cool Shaders!", + "aiFree": "AI-Free!", + "suddenDifficultySpikes": "Sudden Difficulty Spikes!", + "basedOnAnUnfinishedFlashGame": "Based on an Unfinished Flash Game!", + "moreAddictiveThanIntended": "More Addictive than Intended!", + "mostlyConsistentSeeds": "Mostly Consistent Seeds!", + "achievementPointsDontDoAnything": "Achievement Points Don't Do Anything!", + "youDoNotStartAtLevel": "You Do Not Start at Level 2000!", + "dontTalkAboutTheManaphyEggIncident": "Don't Talk About the Manaphy Egg Incident!", + "alsoTryPokengine": "Also Try Pokéngine!", + "alsoTryEmeraldRogue": "Also Try Emerald Rogue!", + "alsoTryRadicalRed": "Also Try Radical Red!", + "eeveeExpo": "Eevee Expo!", + "ynoproject": "YNOproject!", + "breedersInSpace": "Breeders in space!", +} as const; diff --git a/src/locales/ja/starter-select-ui-handler.ts b/src/locales/ja/starter-select-ui-handler.ts new file mode 100644 index 00000000000..095ceeb0ca1 --- /dev/null +++ b/src/locales/ja/starter-select-ui-handler.ts @@ -0,0 +1,49 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +/** + * The menu namespace holds most miscellaneous text that isn't directly part of the game's + * contents or directly related to Pokemon data. This includes menu navigation, settings, + * account interactions, descriptive text, etc. + */ +export const starterSelectUiHandler: SimpleTranslationEntries = { + "confirmStartTeam": "この じょうけんで チャレンジを しんこうしますか?", + "confirmExit": "しゅうりょう しますか?", + "invalidParty": "This is not a valid starting party!", + "gen1": "1せだい", + "gen2": "2せだい", + "gen3": "3せだい", + "gen4": "4せだい", + "gen5": "5せだい", + "gen6": "6せだい", + "gen7": "7せだい", + "gen8": "8せだい", + "gen9": "9せだい", + "growthRate": "EXPタイプ:", + "ability": "とくせい:", + "passive": "パッシブ:", + "nature": "せいかく:", + "eggMoves": "タマゴわざ", + "start": "始める", + "addToParty": "えらぶ", + "toggleIVs": "個体値を ひょうじ", + "manageMoves": "わざを ならびかえ", + "manageNature": "せいかくを ならびかえ", + "useCandies": "アメを つかう", + "selectNature": "せいかくをえらんでください", + "selectMoveSwapOut": "交換する技を選択してください", + "selectMoveSwapWith": "交換先の技を選択してください", + "unlockPassive": "パッシブを かいほうする", + "reduceCost": "ポケモンの 必要ポイントを へらす", + "sameSpeciesEgg": "Buy an Egg", + "cycleShiny": ": 色違い変更", + "cycleForm": ": フォルム変更", + "cycleGender": ": 性別変更", + "cycleAbility": ": 特性変更", + "cycleNature": ": 性格変更", + "cycleVariant": ": 色変更", + "enablePassive": "パッシブ ゆうこう", + "disablePassive": "パッシブ むこう", + "locked": "Locked", + "disabled": "Disabled", + "uncaught": "Uncaught" +}; diff --git a/src/locales/ja/status-effect.ts b/src/locales/ja/status-effect.ts new file mode 100644 index 00000000000..5914fc27298 --- /dev/null +++ b/src/locales/ja/status-effect.ts @@ -0,0 +1,67 @@ +import { StatusEffectTranslationEntries } from "#app/interfaces/locales.js"; + +export const statusEffect: StatusEffectTranslationEntries = { + none: { + name: "None", + description: "", + obtain: "", + obtainSource: "", + activation: "", + overlap: "", + heal: "" + }, + poison: { + name: "Poison", + description: "poisoning", + obtain: "{{pokemonNameWithAffix}}\nwas poisoned!", + obtainSource: "{{pokemonNameWithAffix}}\nwas poisoned by the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is hurt\nby poison!", + overlap: "{{pokemonNameWithAffix}} is\nalready poisoned!", + heal: "{{pokemonNameWithAffix}} was\ncured of its poison!" + }, + toxic: { + name: "Toxic", + description: "poisoning", + obtain: "{{pokemonNameWithAffix}}\nwas badly poisoned!", + obtainSource: "{{pokemonNameWithAffix}}\nwas badly poisoned by the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is hurt\nby poison!", + overlap: "{{pokemonNameWithAffix}} is\nalready poisoned!", + heal: "{{pokemonNameWithAffix}} was\ncured of its poison!" + }, + paralysis: { + name: "Paralysis", + description: "paralysis", + obtain: "{{pokemonNameWithAffix}} was paralyzed,\nIt may be unable to move!", + obtainSource: "{{pokemonNameWithAffix}} was paralyzed by the {{sourceText}}!\nIt may be unable to move!", + activation: "{{pokemonNameWithAffix}} is paralyzed!\nIt can't move!", + overlap: "{{pokemonNameWithAffix}} is\nalready paralyzed!", + heal: "{{pokemonNameWithAffix}} was\nhealed of paralysis!" + }, + sleep: { + name: "Sleep", + description: "sleep", + obtain: "{{pokemonNameWithAffix}}\nfell asleep!", + obtainSource: "{{pokemonNameWithAffix}}\nfell asleep from the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is fast asleep.", + overlap: "{{pokemonNameWithAffix}} is\nalready asleep!", + heal: "{{pokemonNameWithAffix}} woke up!" + }, + freeze: { + name: "Freeze", + description: "freezing", + obtain: "{{pokemonNameWithAffix}}\nwas frozen solid!", + obtainSource: "{{pokemonNameWithAffix}}\nwas frozen solid by the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is\nfrozen solid!", + overlap: "{{pokemonNameWithAffix}} is\nalready frozen!", + heal: "{{pokemonNameWithAffix}} was\ndefrosted!" + }, + burn: { + name: "Burn", + description: "burn", + obtain: "{{pokemonNameWithAffix}}\nwas burned!", + obtainSource: "{{pokemonNameWithAffix}}\nwas burned by the {{sourceText}}!", + activation: "{{pokemonNameWithAffix}} is hurt\nby its burn!", + overlap: "{{pokemonNameWithAffix}} is\nalready burned!", + heal: "{{pokemonNameWithAffix}} was\nhealed of its burn!" + }, +} as const; diff --git a/src/locales/ja/trainers.ts b/src/locales/ja/trainers.ts new file mode 100644 index 00000000000..a40fabaeacc --- /dev/null +++ b/src/locales/ja/trainers.ts @@ -0,0 +1,322 @@ +import {SimpleTranslationEntries} from "#app/interfaces/locales"; + +// Titles of special trainers like gym leaders, elite four, and the champion +export const titles: SimpleTranslationEntries = { + "elite_four": "Elite Four", + "elite_four_female": "Elite Four", + "gym_leader": "Gym Leader", + "gym_leader_female": "Gym Leader", + "gym_leader_double": "Gym Leader Duo", + "champion": "Champion", + "champion_female": "Champion", + "champion_double": "Champion Duo", + "rival": "Rival", + "professor": "Professor", + "frontier_brain": "Frontier Brain", + "rocket_boss": "Team Rocket Boss", + "magma_boss": "Team Magma Boss", + "aqua_boss": "Team Aqua Boss", + "galactic_boss": "Team Galactic Boss", + "plasma_boss": "Team Plasma Boss", + "flare_boss": "Team Flare Boss", + + "rocket_admin": "Team Rocket Admin", + "rocket_admin_female": "Team Rocket Admin", + "magma_admin": "Team Magma Admin", + "magma_admin_female": "Team Magma Admin", + "aqua_admin": "Team Aqua Admin", + "aqua_admin_female": "Team Aqua Admin", + "galactic_commander": "Team Galactic Commander", + "galactic_commander_female": "Team Galactic Commander", + "plasma_sage": "Team Plasma Sage", + "plasma_admin": "Team Plasma Admin", + "flare_admin": "Team Flare Admin", + "flare_admin_female": "Team Flare Admin", + // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. +} as const; + +// Titles of trainers like "Youngster" or "Lass" +export const trainerClasses: SimpleTranslationEntries = { + "ace_trainer": "Ace Trainer", + "ace_trainer_female": "Ace Trainer", + "ace_duo": "Ace Duo", + "artist": "Artist", + "artist_female": "Artist", + "backers": "Backers", + "backpacker": "Backpacker", + "backpacker_female": "Backpacker", + "backpackers": "Backpackers", + "baker": "Baker", + "battle_girl": "Battle Girl", + "beauty": "Beauty", + "beginners": "Beginners", + "biker": "Biker", + "black_belt": "Black Belt", + "breeder": "Breeder", + "breeder_female": "Breeder", + "breeders": "Breeders", + "clerk": "Clerk", + "clerk_female": "Clerk", + "colleagues": "Colleagues", + "crush_kin": "Crush Kin", + "cyclist": "Cyclist", + "cyclist_female": "Cyclist", + "cyclists": "Cyclists", + "dancer": "Dancer", + "dancer_female": "Dancer", + "depot_agent": "Depot Agent", + "doctor": "Doctor", + "doctor_female": "Doctor", + "firebreather": "Firebreather", + "fisherman": "Fisherman", + "fisherman_female": "Fisherman", + "gentleman": "Gentleman", + "guitarist": "Guitarist", + "guitarist_female": "Guitarist", + "harlequin": "Harlequin", + "hiker": "Hiker", + "hooligans": "Hooligans", + "hoopster": "Hoopster", + "infielder": "Infielder", + "janitor": "Janitor", + "lady": "Lady", + "lass": "Lass", + "linebacker": "Linebacker", + "maid": "Maid", + "madame": "Madame", + "medical_team": "Medical Team", + "musician": "Musician", + "hex_maniac": "Hex Maniac", + "nurse": "Nurse", + "nursery_aide": "Nursery Aide", + "officer": "Officer", + "parasol_lady": "Parasol Lady", + "pilot": "Pilot", + "pokéfan": "Poké Fan", + "pokéfan_female": "Poké Fan", + "pokéfan_family": "Poké Fan Family", + "preschooler": "Preschooler", + "preschooler_female": "Preschooler", + "preschoolers": "Preschoolers", + "psychic": "Psychic", + "psychic_female": "Psychic", + "psychics": "Psychics", + "pokémon_ranger": "Pokémon Ranger", + "pokémon_ranger_female": "Pokémon Ranger", + "pokémon_rangers": "Pokémon Ranger", + "ranger": "Ranger", + "restaurant_staff": "Restaurant Staff", + "rich": "Rich", + "rich_female": "Rich", + "rich_boy": "Rich Boy", + "rich_couple": "Rich Couple", + "rich_kid": "Rich Kid", + "rich_kid_female": "Rich Kid", + "rich_kids": "Rich Kids", + "roughneck": "Roughneck", + "sailor": "Sailor", + "scientist": "Scientist", + "scientist_female": "Scientist", + "scientists": "Scientists", + "smasher": "Smasher", + "snow_worker": "Snow Worker", + "snow_worker_female": "Snow Worker", + "striker": "Striker", + "school_kid": "School Kid", + "school_kid_female": "School Kid", + "school_kids": "School Kids", + "swimmer": "Swimmer", + "swimmer_female": "Swimmer", + "swimmers": "Swimmers", + "twins": "Twins", + "veteran": "Veteran", + "veteran_female": "Veteran", + "veteran_duo": "Veteran Duo", + "waiter": "Waiter", + "waitress": "Waitress", + "worker": "Worker", + "worker_female": "Worker", + "workers": "Workers", + "youngster": "Youngster", + "rocket_grunt": "Rocket Grunt", + "rocket_grunts": "Rocket Grunts", + "rocket_grunt_female": "Rocket Grunt", + "magma_grunt": "Magma Grunt", + "magma_grunt_female": "Magma Grunt", + "magma_grunts": "Magma Grunts", + "aqua_grunt": "Aqua Grunt", + "aqua_grunt_female": "Aqua Grunt", + "aqua_grunts": "Aqua Grunts", + "galactic_grunt": "Galactic Grunt", + "galactic_grunt_female": "Galactic Grunt", + "galactic_grunts": "Galactic Grunts", + "plasma_grunt": "Plasma Grunt", + "plasma_grunt_female": "Plasma Grunt", + "plasma_grunts": "Plasma Grunts", + "flare_grunt": "Flare Grunt", + "flare_grunt_female": "Flare Grunt", + "flare_grunts": "Flare Grunts", +} as const; + +// Names of special trainers like gym leaders, elite four, and the champion +export const trainerNames: SimpleTranslationEntries = { + "brock": "Brock", + "misty": "Misty", + "lt_surge": "Lt Surge", + "erika": "Erika", + "janine": "Janine", + "sabrina": "Sabrina", + "blaine": "Blaine", + "giovanni": "Giovanni", + "falkner": "Falkner", + "bugsy": "Bugsy", + "whitney": "Whitney", + "morty": "Morty", + "chuck": "Chuck", + "jasmine": "Jasmine", + "pryce": "Pryce", + "clair": "Clair", + "roxanne": "Roxanne", + "brawly": "Brawly", + "wattson": "Wattson", + "flannery": "Flannery", + "norman": "Norman", + "winona": "Winona", + "tate": "Tate", + "liza": "Liza", + "juan": "Juan", + "roark": "Roark", + "gardenia": "Gardenia", + "maylene": "Maylene", + "crasher_wake": "Crasher Wake", + "fantina": "Fantina", + "byron": "Byron", + "candice": "Candice", + "volkner": "Volkner", + "cilan": "Cilan", + "chili": "Chili", + "cress": "Cress", + "cheren": "Cheren", + "lenora": "Lenora", + "roxie": "Roxie", + "burgh": "Burgh", + "elesa": "Elesa", + "clay": "Clay", + "skyla": "Skyla", + "brycen": "Brycen", + "drayden": "Drayden", + "marlon": "Marlon", + "viola": "Viola", + "grant": "Grant", + "korrina": "Korrina", + "ramos": "Ramos", + "clemont": "Clemont", + "valerie": "Valerie", + "olympia": "Olympia", + "wulfric": "Wulfric", + "milo": "Milo", + "nessa": "Nessa", + "kabu": "Kabu", + "bea": "Bea", + "allister": "Allister", + "opal": "Opal", + "bede": "Bede", + "gordie": "Gordie", + "melony": "Melony", + "piers": "Piers", + "marnie": "Marnie", + "raihan": "Raihan", + "katy": "Katy", + "brassius": "Brassius", + "iono": "Iono", + "kofu": "Kofu", + "larry": "Larry", + "ryme": "Ryme", + "tulip": "Tulip", + "grusha": "Grusha", + "lorelei": "Lorelei", + "bruno": "Bruno", + "agatha": "Agatha", + "lance": "Lance", + "will": "Will", + "koga": "Koga", + "karen": "Karen", + "sidney": "Sidney", + "phoebe": "Phoebe", + "glacia": "Glacia", + "drake": "Drake", + "aaron": "Aaron", + "bertha": "Bertha", + "flint": "Flint", + "lucian": "Lucian", + "shauntal": "Shauntal", + "marshal": "Marshal", + "grimsley": "Grimsley", + "caitlin": "Caitlin", + "malva": "Malva", + "siebold": "Siebold", + "wikstrom": "Wikstrom", + "drasna": "Drasna", + "hala": "Hala", + "molayne": "Molayne", + "olivia": "Olivia", + "acerola": "Acerola", + "kahili": "Kahili", + "rika": "Rika", + "poppy": "Poppy", + "hassel": "Hassel", + "crispin": "Crispin", + "amarys": "Amarys", + "lacey": "Lacey", + "drayton": "Drayton", + "blue": "Blue", + "red": "Red", + "steven": "Steven", + "wallace": "Wallace", + "cynthia": "Cynthia", + "alder": "Alder", + "iris": "Iris", + "diantha": "Diantha", + "hau": "Hau", + "geeta": "Geeta", + "nemona": "Nemona", + "kieran": "Kieran", + "leon": "Leon", + "rival": "Finn", + "rival_female": "Ivy", + + // Evil Team Admins + "archer": "Archer", + "ariana": "Ariana", + "proton": "Proton", + "petrel": "Petrel", + "tabitha": "Tabitha", + "courtney": "Courtney", + "shelly": "Shelly", + "matt": "Matt", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturn", + "zinzolin": "Zinzolin", + "rood": "Rood", + "xerosic": "Xerosic", + "bryony": "Bryony", + + "maxie": "Maxie", + "archie": "Archie", + "cyrus": "Cyrus", + "ghetsis": "Ghetsis", + "lysandre": "Lysandre", + + // Double Names + "blue_red_double": "Blue & Red", + "red_blue_double": "Red & Blue", + "tate_liza_double": "Tate & Liza", + "liza_tate_double": "Liza & Tate", + "steven_wallace_double": "Steven & Wallace", + "wallace_steven_double": "Wallace & Steven", + "alder_iris_double": "Alder & Iris", + "iris_alder_double": "Iris & Alder", + "marnie_piers_double": "Marnie & Piers", + "piers_marnie_double": "Piers & Marnie", +} as const; diff --git a/src/locales/ja/tutorial.ts b/src/locales/ja/tutorial.ts new file mode 100644 index 00000000000..fa57037a052 --- /dev/null +++ b/src/locales/ja/tutorial.ts @@ -0,0 +1,44 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const tutorial: SimpleTranslationEntries = { + intro: `PokéRogueへようこそ!ログライク要素が\n加わったバトル中心のポケモンファンゲームです。 + $このゲームは収益を上げず、Pokémonおよび使用される\n著作権資産に対する所有権を主張しません。 + $ゲームはまだ作業中ですが、完全にプレイすることができます。\nバグ報告はディスコードコミュニティをご利用ください。 + $ゲームが遅い場合は、ブラウザ設定で「ハードウェア\nアクセラレーション」がオンになっていることを確認してください`, + + accessMenu: "メニューにアクセスするには、入力待ちの間にMキーまたはEscを押してください。\nメニューには設定やさまざまな機能が含まれています。", + + menu: `このメニューから設定にアクセスできます。 + $設定ではゲームスピード、ウィンドウスタイル、\nおよびその他のオプションを変更できます。 + $ここにはさまざまな他の機能もありますので、\nぜひ確認してみてください!`, + + starterSelect: `この画面でZキーやSpaceを押してポケモンを選択できます。\n選んだポケモンは自分の最初のパーティーになります。 + $最大6匹のパーティーで始めることができますが\nポケモンによってポイントがあり、合計10を超えてはなりません。 + $捕まえたりふかさせたりすることで\n選択できる性別、特性、フォルムなどの幅を広げることができます。 + $個体値も徐々に累積して高くなるので、\n同じポケモンをたくさん捕まえてみてください!`, + + pokerus: `毎日ランダムでスターターの\n3種類に紫色の枠が表示されます。 + $登録されたスターターの中にあれば、\nパーティに追加してつよさを確認してみましょう!`, + + statChange: `ポケモンは交代しない限り、\n次のバトルでも能力変化が維持されます。 + $その代わりに、トレーナーバトルや新しいバイオームに\n入る直前に自動的にリセットされます。 + $CキーまたはShiftキーを押し続けると、\n現在のポケモンの能力変化を確認できます。 + $Vキーを押すと、\n相手が使用した技も確認できます。 + $ただし、今のバトルで相手ポケモンがすでに\n使った技のみが表示されます。`, + + selectItem: `バトルが終わるたびに、\nランダムなアイテム3つの中から1つを選んで獲得します。 + $種類は消耗品、ポケモンの持ち物、\n永続的なパッシブアイテムなど様々です。 + $ほとんどの消耗しない道具は\n効果が累積されます。 + $進化用など一部のアイテムは\n使用できる場合にのみ登場します。 + $持ち物を渡す機能を使用して\nポケモン同士で道具を持たせることもできます。 + $持ち物があれば、アイテム選択画面の\n右下に渡す機能が表示されます。 + $お金で消耗品を購入することもでき、\nウェーブが進むにつれて購入可能な種類が増えます。 + $アイテムを選択すると次のウェーブに進むため、\nまず消耗品の購入を行ってください。`, + + eggGacha: `この画面でポケモンのたまごクーポンを\nガチャができます。 + $卵は戦闘を繰り返すうちにふかします。\n珍しいほどもっと長くかかります。 + $ふかさせたポケモンはパーティーに追加されず、\nスターティングに登録されます。 + $卵からふかしたポケモンは一般的に\n野生で捕まえたポケモンよりも高い個体値を持ちます。 + $一部のポケモンは卵からしか手に入りません。 + $各ガチャマシンがそれぞれ異なるボーナスを持っているため、\n好きな方を使ってみてください!,`, +} as const; diff --git a/src/locales/ja/voucher.ts b/src/locales/ja/voucher.ts new file mode 100644 index 00000000000..3d9730ece64 --- /dev/null +++ b/src/locales/ja/voucher.ts @@ -0,0 +1,11 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const voucher: SimpleTranslationEntries = { + "vouchers": "クーポン", + "eggVoucher": "たまごクーポン", + "eggVoucherPlus": "たまごクーポンプラス", + "eggVoucherPremium": "たまごクーポンプレミアム", + "eggVoucherGold": "たまごクーポンゴールド", + "locked": "Locked", + "defeatTrainer": "Defeat {{trainerName}}" +} as const; diff --git a/src/locales/ja/weather.ts b/src/locales/ja/weather.ts new file mode 100644 index 00000000000..a346983d8ec --- /dev/null +++ b/src/locales/ja/weather.ts @@ -0,0 +1,66 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +/** + * The weather namespace holds text displayed when weather is active during a battle + */ +export const weather: SimpleTranslationEntries = { + "sunnyStartMessage": "日差しが 強くなった!", + "sunnyLapseMessage": "日差しが 強い!", + "sunnyClearMessage": "日差しが 元に戻った!", + + "rainStartMessage": "雨が 降り始めた!", + "rainLapseMessage": "雨が 降っている!", + "rainClearMessage": "雨が あがった!", + + "sandstormStartMessage": "砂あらしが 吹き始めた!", + "sandstormLapseMessage": "砂あらしが 吹きあれる!", + "sandstormClearMessage": "砂あらしが おさまった!", + "sandstormDamageMessage": "砂あらしが\n{{pokemonNameWithAffix}}を 襲う!", + + "hailStartMessage": "あられが 降り始めた!", + "hailLapseMessage": "あられが 降っている!", + "hailClearMessage": "あられが 止んだ!", + "hailDamageMessage": "あられが\n{{pokemonNameWithAffix}}を 襲う!", + + "snowStartMessage": "雪が 降り始めた!", + "snowLapseMessage": "雪が 降っている!", + "snowClearMessage": "雪が 止んだ!", + + "fogStartMessage": "足下に 霧(きり)が立ち込めた!", + "fogLapseMessage": "足下に 霧(きり)が 立ち込めている!", + "fogClearMessage": "足下の 霧(きり)が消え去った!", + + "heavyRainStartMessage": "強い雨が 降り始めた!", + "heavyRainLapseMessage": "強い雨が 降っている!", + "heavyRainClearMessage": "強い雨が あがった!", + + "harshSunStartMessage": "日差しが とても強くなった!", + "harshSunLapseMessage": "日差しが とても強い!", + "harshSunClearMessage": "日差しが 元に戻った!", + + "strongWindsStartMessage": "謎(なぞ)の 乱気流(らんきりゅう)が\nひこうポケモンを 護(まも)る!", + "strongWindsLapseMessage": "謎(なぞ)の 乱気流(らんきりゅう)の 勢(いきお)いは 止まらない!", + "strongWindsEffectMessage": "謎(なぞ)の 乱気流(らんきりゅう)が 攻撃(こうげき)を 弱(よわ)めた!", + "strongWindsClearMessage": "謎(なぞ)の 乱気流(らんきりゅう)が おさまった!" +}; + +export const terrain: SimpleTranslationEntries = { + "misty": "ミストフィールド", + "mistyStartMessage": "足下に 霧(きり)が立ち込めた!", + "mistyClearMessage": "足下の 霧(きり)が消え去った!", + "mistyBlockMessage": "{{pokemonNameWithAffix}}は\nミストフィールドに 守られている!", + + "electric": "エレキフィールド", + "electricStartMessage": "足下に 電気が かけめぐる!", + "electricClearMessage": "足下の 電気が 消え去った!", + + "grassy": "グラスフィールド", + "grassyStartMessage": "足下に 草がおいしげった!", + "grassyClearMessage": "足下の 草が消え去った!", + + "psychic": "サイコフィールド", + "psychicStartMessage": "足元が 不思議な感じに なった!", + "psychicClearMessage": "足元の 不思議感が 消え去った!", + + "defaultBlockMessage": "{{pokemonNameWithAffix}}は\n{{terrainName}}に 守られている!" +}; diff --git a/src/locales/ko/achv.ts b/src/locales/ko/achv.ts index abc0f186fd4..431cb7d3296 100644 --- a/src/locales/ko/achv.ts +++ b/src/locales/ko/achv.ts @@ -169,6 +169,10 @@ export const PGMachv: AchievementTranslationEntries = { name: "무패", description: "클래식 모드 클리어", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "우리집 꿈나무", + description: "최종 진화형이 아닌 포켓몬을 데리고 클래식 모드 클리어." + }, "MONO_GEN_ONE": { name: "근본 라이벌", diff --git a/src/locales/ko/arena-tag.ts b/src/locales/ko/arena-tag.ts index ca1039e2bc0..2211ced6c4c 100644 --- a/src/locales/ko/arena-tag.ts +++ b/src/locales/ko/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "상대 주변을\n{{moveName}}[[가]] 보호하고 있다!", "conditionalProtectApply": "{{pokemonNameWithAffix}}[[를]]\n{{moveName}}[[가]] 지켜주고 있다!", "matBlockOnAdd": "{{pokemonNameWithAffix}}[[는]]\n마룻바닥세워막기를 노리고 있다!", + "noCritOnAddPlayer": "{{moveName}}의 힘으로\n우리 편의 급소가 숨겨졌다!", + "noCritOnAddEnemy": "{{moveName}}의 힘으로\n상대의 급소가 숨겨졌다!", + "noCritOnRemove": "{{pokemonNameWithAffix}}의 {{moveName}}[[가]] 풀렸다!", "wishTagOnAdd": "{{pokemonNameWithAffix}}의\n희망사항이 이루어졌다!", "mudSportOnAdd": "전기의 위력이 약해졌다!", "mudSportOnRemove": "흙놀이의 효과가\n없어졌다!", diff --git a/src/locales/ko/bgm-name.ts b/src/locales/ko/bgm-name.ts index 69697b07774..81052560fc4 100644 --- a/src/locales/ko/bgm-name.ts +++ b/src/locales/ko/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries" : "{{name}}", "battle_kanto_champion": "BW2 관동 챔피언 배틀", "battle_johto_champion": "BW2 성도 챔피언 배틀", - "battle_hoenn_champion": "BW2 호연 챔피언 배틀", + "battle_hoenn_champion_g5": "BW2 호연 챔피언 배틀", + "battle_hoenn_champion_g6": "ORAS 호연 챔피언 배틀", "battle_sinnoh_champion": "BW2 신오 챔피언 배틀", "battle_champion_alder": "BW 하나 챔피언 배틀", "battle_champion_iris": "BW2 하나 챔피언 배틀", diff --git a/src/locales/ko/dialogue.ts b/src/locales/ko/dialogue.ts index 8e7f09ff373..ed0b498abbc 100644 --- a/src/locales/ko/dialogue.ts +++ b/src/locales/ko/dialogue.ts @@ -383,124 +383,297 @@ export const PGMdialogue: DialogueTranslationEntries = { 3: "내가 뱃멀미가 나는 것 같군…" }, }, - "rocket_grunt": { + "archer": { "encounter": { - 1: "트러블에 대비하도록!" + 1: "더 나아가기 전에 우리 로켓단과 맞설 만한지 한 번 봅시다!", + 2: "당신의 실력이 예사롭지 않다는 소식을 들었습니다. 정말인지 한 번 보지요.", + 3: "…저는 로켓단의 간부 아폴로. 우리 조직의 적에게는 봐 주지 않습니다!" }, "victory": { - 1: "로켓단은 다시 떠오를 거니까!" + 1: "…이런 실수를 하다니!", + 2: "역시 지금의 저는 무리였군요…", + 3: "비, 비주기님, 용서해주십시오…제가 일개 트레이너한테 당하다니…" }, }, - "rocket_admin": { + "ariana": { "encounter": { - 1: "어라 어라… 결국 여기까지 오셨습니까? 꽤 우수한 트레이너인가 보군요.", - 2: "영웅 놀이는 여기까지랍니다, 꼬마야.", - 3: "어른이 화를 내면 무섭다는 걸 보여 드리죠!" + 1: `거기까지다~!! 너 같은 놈을 언제까지고 설치게 두었다가는 + $로켓단의 프라이드는 상처 입고 상처 입어서 상처투성이가 돼 버린다고-!`, + 2: `내가 하는 일이 옳은지 그른지는 상관 없어… + $그저 비주기님을 믿고 따르는 것 뿐이니까-!`, + 3: "네 여정은 여기서 끝이야. 내가 널 이길 테니까-!" }, "victory": { - 1: "크으… 비주기님 용서해 주세요…!", - 2: "어떻게 이런 일이…", - 3: "아아… 넌 너무 강하다…" + 1: `어머, 강하군. 안타깝네. + $네가 로켓단에 있었다면 간부가 될 수 있었을 텐데.`, + 2: "사…산산조각났네…", + 3: "으이이이익! 온 힘을 다해서 싸웠는데…이래도 이길 수 없다니!" }, }, - "magma_grunt": { + "proton": { "encounter": { - 1: " 마그마단을 방해한다면, 자비는 없닷!" + 1: "뭐죠? 우리 일에 끼어든다면 자비를 바라지 마십시오!", + 2: `뭐죠? 나는 로켓단에서 제일 냉혹하다고 불리는 남자… + $우리 일을 방해하도록 그냥 놔두지 않겠습니다!`, + 3: "나는 로켓단의 간부 랜스. 당신의 참견도 여기까지입니다!" }, "victory": { - 1: "하? 내가 졌어?!" + 1: "요새가 무너져내렸네요…", + 2: "나한테 이긴 건 결국 로켓단의 분노를 강하게 했을 뿐이예요…", + 3: "나는 졌지만, 결코 이 일을 잊지 않을 겁니다!" }, }, - "magma_admin": { + + "petrel": { "encounter": { - 1: "……아하… ……역시 왔네…그치만 안타깝게 됐어……다 끝났거든", - 2: "……남은……내 일은……너를……막는 것", - 3: "……너랑……인게이지……하고 싶어……아하하하" + 1: `후후훗, 잘 왔구나. 오잉? 내가 누군지 알아? 비주기야. + $비주기님이라고, 우-하하! …엥? 전혀 안 닮았다고? + $비주기님으로는 안 보인다고? 제길- 열심히 연습했는데!`, + 2: "나는 로켓단의 간부 람다. 우리 계획을 방해하는 건 용납할 수 없다!", + 3: "이 로켓단 간부 람다가 네놈 불청객을 처리해 주지!" }, "victory": { - 1: "……룰루리", - 2: "……재밌쪄", - 3: "…하아하아……으…하아하아…" + 1: "조…좋아. 비주기님이 어디 계신지 알려주지.", + 2: "크으으… 이 내가 당해낼 수 없다니… 비주기님, 용서해주십시오…", + 3: "안돼, 이런다고 나한테 어쩔 수는 없어. 다른 사람들에게 알려야겠어…" }, }, - "aqua_grunt": { + "tabitha": { "encounter": { - 1: "아쿠아단을 넘본 사람에게는 자비는 없다, 꼬마도 마찬가지야!" + 1: "우효효효! 그래 당신 여기까지 왔구나! 그치만 늦었어요!", + 2: `우효효… 벌써 여기까지 왔네요? 우리가 당신을 과소평가했어요. 하지만 이제 시작입니다! + $이 호걸님은 이제까지 본 조무래기들과는 차원이 다르답니다! 우물쭈물 시간을 끌지 않아요. + $확실하게 보내주마! 우효효효효효효!!`, + 3: "여기서 어른의 무서움을 제대로 알려주지! 받아랏-!" }, "victory": { - 1: "말도 안돼!" + 1: `우효효! 이 호걸은 이겼을지 몰라도 마적님한테는 안 될 겁니다! + $차라리 여기서 졌다면 무자비한 채찍질은 피했을텐데 말이죠!`, + 2: "우효~! 이야 이건 예상 밖의 전개인데!?", + 3: "우효! 어떻게?! 이 호걸님이 이런 일개 트레이너에게…" }, }, - "aqua_admin": { + "courtney": { "encounter": { - 1: "각오하는 게 좋을 거야! 네 얼굴이 눈물로 범벅이 되게 해주겠어!", + 1: "…그 …네가 가진 …우리…마그마단이 원하는 것", + 2: "…………그럼 …………삭제하겠습니다", + 3: "……애널라이즈 ……하고 싶어 ……아하하하♪" + }, + "victory": { + 1: "……바꿔줘 ……세계를", + 2: `………예상대로 ………예상외 ………너 …………타깃 록 ………했으니까 + $……엑스페리먼트 ……할 테니까 ………너를………계속………아핫…♪`, + 3: "……또 ……예상외 ………… ……역시 ……너…재미있어…! ………아하하…♪" + }, + }, + "shelly": { + "encounter": { + 1: `엥? 우리 아쿠아단의 일에 끼어들겠다고? + $…좋아! 기본적인 예의도 모르는 애송이한테는 제대로 그 버릇을 고쳐줘야지… + $겁먹고 도망쳐도 용서 따위 없을 줄 알아! 우하하하!`, 2: "아앙? 뭐야? 이 건방진 꼬맹이는…", - 3: "…아니 넌!? 일부러 여기까지 쫓아온 거야?" + 3: "침착해. 조급해 하지 말라고… 금방 박살내 줄 테니까." }, "victory": { - 1: "하아… 하아…완전 지쳤어", + 1: `아아아아앙!? 예상치 못하게 방해받았잖아! 어쩔 수 없네. + $여기선 물러나야겠네. 그렇지만 네가 아쿠아단을 만나는 게 이게 마지막은 아닐 거야. + $우리는 다른 계획도 있거든! 잊지 마!`, 2: "크윽…!? 너무 봐줬나…!", - 3: "뭐…뭐라고!?" + 3: `…으윽 …싸우면서 더욱 실력이 좋아졌다고…!? + $장래가 무서워지는 애송이네… …나와 내 포켓몬들은 더 이상 싸울 힘이 남아 있지 않아. + $…가 …가서 아강님한테 아주 혼쭐이나 나 버려.` }, }, - "galactic_grunt": { + "matt": { "encounter": { - 1: "갤럭시단을 방해하지 마!" + 1: "후하하하하하! 뭐라 떠들고 있는 거야! 너 생긴 게 마크탕이랑 똑같네!", + 2: "음음! 네 녀석은! 재미있는 녀석!", + 3: "뭐야?! 우릴 따라온 거냐!" }, "victory": { - 1: "사격 중지…… " + 1: "…그래서 말이지, 리더님이 시간이 나실 때까진 내가 상대해 주마!", + 2: `확 확 느껴지는데! 네놈들의 강함이 말이야! + $제대로 붙었다고 하기엔 조금 모자라지만 이제 타임오버 같네…`, + 3: "재밌는데!! 역시 재미있어! 넌! 또 붙게 될 때를 기대하고 있겠어!" }, }, - "galactic_admin": { + "mars": { "encounter": { - 1: "나는 갤럭시단에 있는 간부 중의 한 명.", - 2: "갤럭시단을 방해한다면 일말의 가능성도 모두 제거한다!!", - 3: "왜 그래? 설마 떨고 있는 거야?" + 1: "난 갤럭시단 간부인 마스! 강하고 아름답지!", + 2: "갤럭시단의 미래에 대한 비전은 흔들림 없지. 방해한다면 무자비하게 짓밟아 주마!", + 3: "두렵지 않아? 넌 그래야만 할 걸!" }, "victory": { - 1: "설마! 내가 졌다고!? 건방진 아이로구나!!", - 2: "…역시 강해!", - 3: "어린아이에게 지다니… 방심이란 무섭구나." - }, + 1: "갤럭시단의 간부로서… 이런 일은 있을 수 없어!!", + 2: "넌 능력 있구나. 그건 인정하지.", + 3: "아-이런 이런! 졌잖아!" + } }, - "plasma_grunt": { + "jupiter": { "encounter": { - 1: "다른 생각을 가진사람들은 용납하지 않겠다!" + 1: "무슨 볼일이라도? 좋아! 갤럭시단 간부인 나 주피터가 상대해주지.", + 2: "발버둥쳐 봐야 소용 없어. 갤럭시단이 승리할 거니까!", + 3: "너 떨고 있네… 무서운 거지?" }, "victory": { - 1: "플라-스마-!" - }, + 1: "일개 트레이너에게 지다니 방심이란 무섭구나.", + 2: "다음에는 내가 울려 주고 말겠어!", + 3: "흥! 강하네. 하지만 보스는 당할 수 없어" + } }, - "plasma_sage": { + "saturn": { + "encounter": { + 1: "나는 갤럭시단의 간부 새턴. 모든 것은 모두를 위해 그리고 갤럭시단을 위해!", + 2: "갤럭시단을 방해한다면 일말의 가능성도 모두 제거한다!", + 3: "여기까지 왔으니 갤럭시단 나름의 접대를 해 주지." + }, + "victory": { + 1: "이럴 수가… 너한테 졌다고?!", + 2: "…역시 강해! 갤럭시단에 맞설 만하군.", + 3: "강하다! 하지만 불쌍하군." + }}, + "zinzolin": { "encounter": { 1: "너는 플라스마단에게 있어 불안요소가 될 것이다. 여기서 제거하겠다!", - 2: "이런 이런… 내가 싸워야만 하다니.", - 3: "여기까지 오다니 대단한 트레이너군." + 2: "이런 이런… 내가 이런 곳에서 싸워야만 하다니!", + 3: "여기까지 오다니 대단한 트레이너군. 그러나 여기가 끝이다." }, "victory": { - 1: "게치스…", - 2: "그건 그렇고 춥구먼. 나는 떨고 있다. 괴롭지만 살아 있다.", - 3: "흐음. 의외로 똑똑한 트레이너군." - }, + 1: "게치스님… 제가 실패했습니다…", + 2: "그건 그렇고 힘들구먼. 나는 떨고 있다. 괴롭지만 살아 있다. 그것이야말로 살아 있다는 실감!", + 3: "흐음. 의외로 똑똑한 트레이너군. 하지만 생각만큼은 아니야." + } }, - "flare_grunt": { + "rood": { "encounter": { - 1: "패션이 우리한텐 가장 중요하다고!" + 1: "너는 플라스마단에 위협이 되는구나. 너라는 트레이너가 어떤 인물인지 승부로 알아봐야겠다.", + 2: "오호! 싸늘하구먼… 이런 곳에서 싸울 줄이야!", + 3: "너는 여기까지 온 것으로 보니 뛰어난 트레이너구나. 그렇다만 이젠 끝이다." }, "victory": { - 1: "미래가 밝아 보이질 않네." - }, + 1: "게치스님… 임무를 실패했습니다…", + 2: "나는 떨고 있다. 나는 괴롭지만 이겨냈다.", + 3: "음… 너는 재능이 있는 트레이너구나. 하지만 충분하지는 않다." + } }, - "flare_admin": { + "xerosic": { "encounter": { - 1: "왔다! 왔구나! 자! 자! 아직 끝나지 않았다!", + 1: "오오- 네 소문은 많이 들었다. 자, 이리 와 보거라!", 2: "너 강하구나. 에너지를 얼마나 갖고 있지?", 3: "기다리고 있었어! 너를 조사하겠다. 자 시작한다!" }, "victory": { 1: "강하구나, 너는. 응, 정말 강해, 너는.", - 2: "그렇지만 보스의 꿈이 이루어져 아름다운 세상이 태어날 것이다!", - 3: "굉장하구나 너! 아주 굉장해! 나는 너를 인정하겠다" + 2: "뭣이라! 넌 굉장하군! 너의 포켓몬도 대단하군!", + 3: "굉장하구나 너! 아주 굉장해! 나는 너를 인정하겠다!" + } + }, + "bryony": { + "encounter": { + 1: "나는 바라. 당신과 싸울 수 있어 기쁘군요. 한 번 보여주시죠.", + 2: "인상적이군요… 보기보다 강해요. 에너지가 어디까지 뻗어나가는지 봅시다.", + 3: "도착할 줄 알았습니다. 시작할까요?" + }, + "victory": { + 1: "어라? 이길 확률은 어디까지나 확률. 절대적이진 않네.", + 2: "확률을 무시하는 트레이너, 네 파워의 원천은 뭐지?", + 3: "놀랍군! 칭찬할 만 해." + } + }, + "rocket_grunt": { + "encounter": { + 1: "트러블에 대비하도록!", + 2: "우리는 위업을 이루는 중이라고! 저리 비켜, 꼬마 녀석!", + 3: "포켓몬을 넘기지 않겠다면, 로켓단이 징벌하겠다!", + 4: "로켓단의 진정한 공포를 보여주마!", + 5: "헤이, 키드! 미는 로켓단의 멤버라구요!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "로켓단은 다시 떠오를 거니까!", + 2: "이런! 엘리베이터 키를 떨어뜨렸어!", + 3: "실패했군!", + 4: "내 동료들이 가만히 있지 않을 거다!", + 5: "유 쎄이 왓? 로켓단이 바이바이? 유한테 브레이킹?" //Use of wrong grammar is deliberate + }, + }, + "magma_grunt": { + "encounter": { + 1: " 마그마단을 방해한다면, 자비는 없닷!", + 2: "계획을 방해하지 않는 게 좋을 거다! 우리는 세상을 더 나은 곳으로 만들고 있거든!", + 3: "방해꾼 녀석! 마그단은 너 같은 꼬마들을 상대할 시간이 없어!", + 4: "마시멜로를 갖고 있었으면 좋겠네. 왜냐하면… 곧 뜨거워질 테니까!", + 5: "화산의 힘을 사용할 거야! 그건 정말로… 폭발적일 테니까! 알아들었지? 헤헷!" + }, + "victory": { + 1: "하? 내가 졌어?!", + 2: "내가 지다니! 이것 때문에 점심도 거르고 왔는데.", + 3: "말도 안돼! 넌 아직 어린애잖아!", + 4: "으윽… 당장 은신처로 숨을 걸 그랬나…", + 5: "네가 이겼어… 이것 때문에 보스가, 내 월급 깎으려나?" + }, + }, + "aqua_grunt": { + "encounter": { + 1: "아쿠아단을 넘본 사람에게는 자비는 없다, 꼬마도 마찬가지야!", + 2: "쯧… 아쿠아단에 참견하다니 오지랖이 넓군!", + 3: "흠뻑 물을 뒤집어쓰게 될 거다! 내 물 포켓몬의 공격 뿐만이 아니야!", + 4: "우리, 아쿠아단은, 모두를 위해 존재한다!", + 5: "내가 떠밀… 아니, 파도에 떠내려갈 준비나 하라고! 내 포켓몬이 그렇게 만들 테니까 " + }, + "victory": { + 1: "말도 안 돼!", + 2: "크윽, 참견쟁이 꼬마에게 당하다니!", + 3: "내가 졌다고?! 헤엄쳐서 은신처로 돌아가야겠군…", + 4: "이런, 완전 망했군… 보스가 화를 내실텐데…", + 5: "네가 이겼어… 이것 때문에 보스가, 나를 판자 위로 보내는 거 아냐?" + }, + }, + "galactic_grunt": { + "encounter": { + 1: "갤럭시단을 방해하지 마!", + 2: "기술의 힘과 우리가 꿈꾸는 미래를 목격하라!", + 3: "갤럭시단으로서, 우리를 방해하는 자는 누구든 제거하겠다!", + 4: "질 준비나 하라고!", + 5: "우주에서 싸울 준비는 됐겠지!" + }, + "victory": { + 1: "사격 중지…… ", + 2: "이런 좌절이라도, 우리의 큰 계획 앞엔 아무 의미도 못 돼.", + 3: "우리의 계획은 이번 패배보다 크거든.", + 4: "어떻게 한 거지?!", + 5: "메모해야겠군. 최대한 포켓몬 배틀을 ASAP으로 연습할 것." + }, + }, + "plasma_grunt": { + "encounter": { + 1: "다른 생각을 가진사람들은 용납하지 않겠다!", + 2: "내가 이기면, 네 포켓몬들을 놓아주도록 해!", + 3: "플라즈마단을 방해한다면, 내가 처리해주지!", + 4: "플라즈마단은 너 같은 이기적인 인간에게서 포켓몬을 해방할 것이다!", + 5: "우리 스타일링은 정말 대단하지… 배틀 실력은 어떻냐고? 곧 알게 될거야." + }, + "victory": { + 1: "플라-스마-!", + 2: "내가 지다니…", + 3: "…포켓몬이 너무 약해, 더 좋은 포켓몬을 훔치러 가야겠군!", + 4: "훌륭한 계획은 항상 방해를 받는다니깐.", + 5: "이건 나빠… 나빠나빠나빠나빠나빠! 플라스마단에 나빠! 줄여서, 플라나빠!" + }, + }, + "flare_grunt": { + "encounter": { + 1: "네 포켓몬, 플레어단의 우아함에 상대가 되지 않는다고.", + 2: "선글라스를 가져왔길 바랄게. 곧 밝게 빛날 테니까!", + 3: "플레어단이 세상의 불완전함을 정화할 거야!", + 4: "플레어단의 광채를 마주칠 준비는 됐겠지!", + 5: "패션이 우리한텐 가장 중요하다고!" + }, + "victory": { + 1: "미래가 밝아 보이질 않네.", + 2: "생각했던 것보다 전투에는 많은 요소가 있는 것 같군. 다시 계획을 짜야겠어.", + 3: "아앗?! 내가 졌어?!", + 4: "패배 속에서도, 플레어단의 우아함은 빛나고 있다고.", + 5: "네가 이긴 것 같네. 그렇지만 졌어도, 난 우아하게 퇴장할 거니까!" }, }, "rocket_boss_giovanni_1": { @@ -2582,7 +2755,7 @@ export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `드디어 때가 다시 도래했다.\n당도한 연유를 아는가? $이미 도달한 적이 있기에 이 자리에 있다.\n셀 수도 없이 많이. - $아니, 사실 셀 수는 있지.\n정확히 너의 5,643,853번째다. + $아니, 사실 셀 수는 있지.\n정확히 너의 {{cycleCount}}번째다. $매 번 태초의 정신으로 되돌아갔을 뿐.\n하지만 어떻게든, 흔적은 남는다. $실패만을 반복했을 뿐이지만,\n지금은 네 안에 무언가가 있구나.\n $홀로 선 것처럼 보이나, 무언가 이질적인… diff --git a/src/locales/ko/filter-bar.ts b/src/locales/ko/filter-bar.ts index 821a80d78ee..b4dcb48b581 100644 --- a/src/locales/ko/filter-bar.ts +++ b/src/locales/ko/filter-bar.ts @@ -3,7 +3,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "세대", "typeFilter": "타입", - "dexFilter": "도감", + "caughtFilter": "포획", "unlocksFilter": "해금", "miscFilter": "기타", "sortFilter": "정렬", @@ -13,9 +13,18 @@ export const filterBar: SimpleTranslationEntries = { "passive": "패시브", "passiveUnlocked": "패시브 해금", "passiveLocked": "패시브 잠김", + "costReduction": "코스트 줄이기", + "costReductionUnlocked": "코스트 절감됨", + "costReductionLocked": "코스트 절감 없음", "ribbon": "클리어 여부", - "hasWon": "클리어 함", - "hasNotwon": "클리어 안함", + "hasWon": "클리어 완료", + "hasNotWon": "클리어 안함", + "hiddenAbility": "숨겨진 특성", + "hasHiddenAbility": "숨겨진 특성 보유", + "noHiddenAbility": "숨겨진 특성 없음", + "pokerus": "포켓러스", + "hasPokerus": "포켓러스 감염", + "noPokerus": "포켓러스 없음", "sortByNumber": "도감번호", "sortByCost": "코스트", "sortByCandies": "사탕 수", diff --git a/src/locales/ko/move-trigger.ts b/src/locales/ko/move-trigger.ts index 9ebf08b1017..4c32e5b5b9f 100644 --- a/src/locales/ko/move-trigger.ts +++ b/src/locales/ko/move-trigger.ts @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "{{pokemonName}}를(을)\n강렬한 빛이 감쌌다!", "bellChimed": "방울소리가 울려 퍼졌다!", "foresawAnAttack": "{{pokemonName}}는(은)\n미래의 공격을 예지했다!", + "isTighteningFocus": "{{pokemonName}}[[는]]\n집중력을 높이고 있다!", "hidUnderwater": "{{pokemonName}}는(은)\n물속에 몸을 숨겼다!", "soothingAromaWaftedThroughArea": "기분 좋은 향기가 퍼졌다!", "sprangUp": "{{pokemonName}}는(은)\n높이 뛰어올랐다!", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "{{pokemonName}}[[는]]\n{{targetPokemonName}}[[와]] 같은 타입이 되었다!", "suppressAbilities": "{{pokemonName}}의\n특성이 효과를 발휘하지 못하게 되었다!", "swapArenaTags": "{{pokemonName}}[[는]]\n서로의 필드 효과를 교체했다!", + "exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!", } as const; diff --git a/src/locales/ko/settings.ts b/src/locales/ko/settings.ts index 40e9676b6da..3dcdc3b07c1 100644 --- a/src/locales/ko/settings.ts +++ b/src/locales/ko/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "본 대화 생략", "battleStyle": "시합 룰", "enableRetries": "재도전 허용", + "hideIvs": "개체값탐지기 효과 끄기", "tutorials": "튜토리얼", "touchControls": "터치 컨트롤", "vibrations": "진동", diff --git a/src/locales/ko/starter-select-ui-handler.ts b/src/locales/ko/starter-select-ui-handler.ts index c57a37788fa..25bb006059e 100644 --- a/src/locales/ko/starter-select-ui-handler.ts +++ b/src/locales/ko/starter-select-ui-handler.ts @@ -7,7 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "이 포켓몬들로 시작하시겠습니까?", - "confirmExit": "Do you want to exit?", + "confirmExit": "나가시겠습니까?", "invalidParty": "스타팅 포켓몬 파티에 적합하지 않습니다!", "gen1": "1세대", "gen2": "2세대", diff --git a/src/locales/ko/trainers.ts b/src/locales/ko/trainers.ts index 3a139026448..b34530eb24c 100644 --- a/src/locales/ko/trainers.ts +++ b/src/locales/ko/trainers.ts @@ -19,6 +19,19 @@ export const titles: SimpleTranslationEntries = { "galactic_boss": "갤럭시단 보스", "plasma_boss": "플라스마단 보스", "flare_boss": "플레어단 보스", + + "rocket_admin": "로켓단 간부", + "rocket_admin_female": "로켓단 간부", + "magma_admin": "마그마단 간부", + "magma_admin_female": "마그마단 간부", + "aqua_admin": "아쿠아단 간부", + "aqua_admin_female": "아쿠아단 간부", + "galactic_commander": "갤럭시단 간부", + "galactic_commander_female": "갤럭시단 간부", + "plasma_sage": "플라스마단 현인", + "plasma_admin": "플라스마단 간부", + "flare_admin": "플레어단 간부", + "flare_admin_female": "플레어단 간부", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -36,7 +49,7 @@ export const trainerClasses: SimpleTranslationEntries = { "baker": "제빵사", "battle_girl": "배틀걸", "beauty": "아가씨", - "beginners": "반바지 꼬마 & 짧은 치마", // 확인 필요 + "beginners": "반바지 꼬마 & 짧은 치마", "biker": "폭주족", "black_belt": "태권왕", "breeder": "포켓몬 브리더", @@ -62,7 +75,7 @@ export const trainerClasses: SimpleTranslationEntries = { "guitarist_female": "기타리스트", "harlequin": "어릿광대", "hiker": "등산가", - "hooligans": "폭주족 & 빡빡이", // 확인 필요 + "hooligans": "폭주족 & 빡빡이", "hoopster": "농구선수", "infielder": "야구선수", "janitor": "청소부", @@ -92,7 +105,7 @@ export const trainerClasses: SimpleTranslationEntries = { "pokémon_ranger_female": "포켓몬 레인저", "pokémon_rangers": "포켓몬 레인저", "ranger": "포켓몬 레인저", - "restaurant_staff": "요리사", // 혹은 오너로 추정 + "restaurant_staff": "레스토랑 직원", "rich": "신사", "rich_female": "마담", "rich_boy": "도련님", @@ -114,7 +127,7 @@ export const trainerClasses: SimpleTranslationEntries = { "school_kids": "학원끝난 아이", "swimmer": "수영팬티 소년", "swimmer_female": "비키니 아가씨", - "swimmers": "수영팬티 소년 & 비키니 아가씨", // 확인 필요 + "swimmers": "수영팬티 소년 & 비키니 아가씨", "twins": "쌍둥이", "veteran": "베테랑 트레이너", "veteran_female": "베테랑 트레이너", @@ -128,32 +141,22 @@ export const trainerClasses: SimpleTranslationEntries = { "rocket_grunt": "로켓단 조무래기", "rocket_grunt_female": "로켓단 조무래기", "rocket_grunts": "로켓단 조무래기들", - "rocket_admin": "로켓단 간부", - "rocket_admin_female": "로켓단 간부", "magma_grunt": "마그마단 조무래기", "magma_grunt_female": "마그마단 조무래기", "magma_grunts": "마그마단 조무래기들", - "magma_admin": "마그마단 간부", - "magma_admin_female": "마그마단 간부", "aqua_grunt": "아쿠아단 조무래기", "aqua_grunt_female": "아쿠아단 조무래기", "aqua_grunts": "아쿠아단 조무래기들", - "aqua_admin": "아쿠아단 간부", - "aqua_admin_female": "아쿠아단 간부", "galactic_grunt": "갤럭시단 조무래기", "galactic_grunt_female": "갤럭시단 조무래기", "galactic_grunts": "갤럭시단 조무래기들", - "galactic_admin": "갤럭시단 간부", - "galactic_admin_female": "갤럭시단 간부", "plasma_grunt": "플라스마단 조무래기", "plasma_grunt_female": "플라스마단 조무래기", "plasma_grunts": "플라스마단 조무래기들", - "plasma_sage": "플라스마단 현인", "flare_grunt": "플레어단 조무래기", "flare_grunt_female": "플레어단 조무래기", "flare_grunts": "플레어단 조무래기들", - "flare_admin": "플레어단 간부", - "flare_admin_female": "플레어단 간부", + } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -282,6 +285,25 @@ export const trainerNames: SimpleTranslationEntries = { "leon": "단델", "rival": "핀", "rival_female": "아이비", + + // Evil Team Admins + "archer": "아폴로", + "ariana": "아테나", + "proton": "랜스", + "petrel": "람다", + "tabitha": "호걸", + "courtney": "구열", + "shelly": "이연", + "matt": "해조", + "mars": "마스", + "jupiter": "주피터", + "saturn": "새턴", + "zinzolin": "비오", + "rood": "로트", + "xerosic": "크세로시키", + "bryony": "바라", + + "maxie": "마적", "archie": "아강", "cyrus": "태홍", diff --git a/src/locales/pt_BR/achv.ts b/src/locales/pt_BR/achv.ts index 78c4da6ae2e..5874616bcdc 100644 --- a/src/locales/pt_BR/achv.ts +++ b/src/locales/pt_BR/achv.ts @@ -169,6 +169,10 @@ export const PGMachv: AchievementTranslationEntries = { name: "Invencível", description: "Vença o jogo no modo clássico", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Tire as Crianças da Sala", + description: "Vença o jogo no Modo Clássico com pelo menos um membro da equipe não evoluído.." + }, "MONO_GEN_ONE": { @@ -440,6 +444,10 @@ export const PGFachv: AchievementTranslationEntries = { name: "Invencível", description: "Vença o jogo no modo clássico", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Bring Your Child To Work Day", + description: "Beat the game in Classic Mode with at least one unevolved party member." + }, "MONO_GEN_ONE": { diff --git a/src/locales/pt_BR/arena-tag.ts b/src/locales/pt_BR/arena-tag.ts index 0d3b8ff587f..ebdf886f9a6 100644 --- a/src/locales/pt_BR/arena-tag.ts +++ b/src/locales/pt_BR/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "{{moveName}} protegeu a\nequipe adversária!", "conditionalProtectApply": "{{moveName}} protegeu {{pokemonNameWithAffix}}!", "matBlockOnAdd": "{{pokemonNameWithAffix}} pretende levantar um tapete\npara bloquear ataques!", + "noCritOnAddPlayer": "{{moveName}} protegeu sua\equipe de acertos críticos!", + "noCritOnAddEnemy": "{{moveName}} protegeu a\equipe adversária de acertos críticos", + "noCritOnRemove": "{{moveName}} de {{pokemonNameWithAffix}}\nacabou!", "wishTagOnAdd": "O desejo de {{pokemonNameWithAffix}}\nfoi concedido!", "mudSportOnAdd": "O poder de movimentos elétricos foi enfraquecido!", "mudSportOnRemove": "Os efeitos de Mud Sport\nsumiram.", diff --git a/src/locales/pt_BR/bgm-name.ts b/src/locales/pt_BR/bgm-name.ts index ae2756e74be..01baf93c2a5 100644 --- a/src/locales/pt_BR/bgm-name.ts +++ b/src/locales/pt_BR/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries": "{{name}}", "battle_kanto_champion": "B2W2 Batalha do Campeão de Kanto", "battle_johto_champion": "B2W2 Batalha do Campeão de Johto", - "battle_hoenn_champion": "B2W2 Batalha do Campeão de Hoenn", + "battle_hoenn_champion_g5": "B2W2 Batalha do Campeão de Hoenn", + "battle_hoenn_champion_g6": "ORAS Batalha do Campeão de Hoenn", "battle_sinnoh_champion": "B2W2 Batalha do Campeão de Sinnoh", "battle_champion_alder": "BW Batalha do Campeão de Unova", "battle_champion_iris": "B2W2 Batalha do Campeão de Unova", diff --git a/src/locales/pt_BR/challenges.ts b/src/locales/pt_BR/challenges.ts index 7c991189bc8..31f29da48f3 100644 --- a/src/locales/pt_BR/challenges.ts +++ b/src/locales/pt_BR/challenges.ts @@ -22,4 +22,10 @@ export const challenges: TranslationEntries = { "desc": "Você só pode user Pokémon do tipo {{type}}.", "desc_default": "Você só pode user Pokémon de um único tipo." }, + "freshStart": { + "name": "Novo Começo", + "desc": "Você só pode usar os iniciais originais, como se tivesse acabado de começar o PokéRogue.", + "value.0": "Desligado", + "value.1": "Ligado", + } } as const; diff --git a/src/locales/pt_BR/dialogue.ts b/src/locales/pt_BR/dialogue.ts index 3325cf81cf9..88018c7fcda 100644 --- a/src/locales/pt_BR/dialogue.ts +++ b/src/locales/pt_BR/dialogue.ts @@ -383,52 +383,297 @@ export const PGMdialogue: DialogueTranslationEntries = { 3: "Estou achando que quem tá enjoado sou eu..." }, }, - "rocket_grunt": { + "archer": { "encounter": { - 1: "Se prepara pra encrenca!" + 1: "Before you go any further, let's see how you far against us, Team Rocket!", + 2: "I have received reports that your skills are not insignificant. Let's see if they are true.", + 3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." }, "victory": { - 1: "Equipe Rocket decolando de novo!" + 1: "What a blunder!", + 2: "With my current skills, I was not up to the task after all.", + 3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." + }, + }, + "ariana": { + "encounter": { + 1: `Hold it right there! We can't someone on the loose." + $It's harmful to Team Rocket's pride, you see.`, + 2: `I don't know or care if what I'm doing is right or wrong... + $I just put my faith in Giovanni and do as I am told`, + 3: "Your trip ends here. I'm going to take you down!" + }, + "victory": { + 1: `Tch, you really are strong. It's too bad. + $If you were to join Team Rocket, you could become an Executive.`, + 2: "I... I'm shattered...", + 3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" + }, + }, + "proton": { + "encounter": { + 1: "What do you want? If you interrupt our work, don't expect any mercy!", + 2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket… + $I strongly urge you not to interfere with our business!`, + 3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" + }, + "victory": { + 1: "The fortress came down!", + 2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", + 3: "I am defeated… But I will not forget this!" + }, + }, + + "petrel": { + "encounter": { + 1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. + $The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni? + $I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, + 2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + 3: "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + 1: "OK, OK. I'll tell you where he is.", + 2: "I… I couldn't do a thing… Giovanni, please forgive me…", + 3: "No, I can't let this affect me. I have to inform the others…" + }, + }, + "tabitha": { + "encounter": { + 1: "Hehehe! So you've come all the way here! But you're too late!", + 2: `Hehehe... Got here already, did you? We underestimated you! But this is it! + $I'm a cut above the Grunts you've seen so far. I'm not stalling for time. + $I'm going to pulverize you!`, + 3: "I'm going to give you a little taste of pain! Resign yourself to it!" + }, + "victory": { + 1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! + $If you get lost now, you won't have to face a sound whipping!`, + 2: "Hehehe... So, I lost, too...", + 3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." + }, + }, + "courtney": { + "encounter": { + 1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", + 2: "... Well then...Deleting...", + 3: "...Ha. ...Analyzing... ...Hah♪" + }, + "victory": { + 1: "... ...Change...the world.", + 2: `As anticipated. Unanticipated. You. Target lock...completed. + $Commencing...experiment. You. Forever. Aha... ♪`, + 3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" + }, + }, + "shelly": { + "encounter": { + 1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? + $You're either absolutely fearless, simply ignorant, or both! + $You're so cute, you're disgusting! I'll put you down`, + 2: "What's this? Who's this spoiled brat?", + 3: "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + 1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. + $We'll have to pull out. But this isn't the last you'll see of Team Aqua! + $We have other plans! Don't you forget it!`, + 2: "Ahhh?! Did I go too easy on you?!", + 3: `Uh. Are you telling me you've upped your game even more during the fight? + $You're a brat with a bright future… My Pokémon and I don't have any strength left to fight… + $Go on… Go and be destroyed by Archie.` + }, + }, + "matt": { + "encounter": { + 1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", + 2: "Oho! You! You're that funny kid!", + 3: "What are you doing here? Did you follow us?" + }, + "victory": { + 1: "All right then, until the Boss has time for you, I'll be your opponent!", + 2: `I can feel it! I can feel it, all right! The strength coming offa you! + $More! I still want more! But looks like we're outta time...`, + 3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" + }, + }, + "mars": { + "encounter": { + 1: "I'm Mars, one of Team Galactic's top Commanders.", + 2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + 3: "Feeling nervous? You should be!" + }, + "victory": { + 1: "This can't be happening! How did I lose?!", + 2: "You have some skill, I'll give you that.", + 3: "Defeated... This was a costly mistake." + } + }, + "jupiter": { + "encounter": { + 1: "Jupiter, Commander of Team Galactic, at your service.", + 2: "Resistance is futile. Team Galactic will prevail!", + 3: "You're trembling... scared already?" + }, + "victory": { + 1: "No way... I lost?!", + 2: "Impressive, you've got guts!", + 3: "Losing like this... How embarrassing." + } + }, + "saturn": { + "encounter": { + 1: "I am Saturn, Commander of Team Galactic.", + 2: "Our mission is absolute. Any hindrance will be obliterated!", + 3: "Is that fear I see in your eyes?" + }, + "victory": { + 1: "Impossible... Defeated by you?!", + 2: "You have proven yourself a worthy adversary.", + 3: "Bestowed in defeat... This is unacceptable." + }}, + "zinzolin": { + "encounter": { + 1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", + 2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", + 3: "You're an impressive Trainer to have made it this far. But it ends here." + }, + "victory": { + 1: "Ghetsis... I have failed you...", + 2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", + 3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } + }, + "rood": { + "encounter": { + 1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + 2: "Oh, this icy wind... I never thought I'd have to fight here!", + 3: "You are a remarkable Trainer to have made it this far. But this is where it ends." + }, + "victory": { + 1: "Ghetsis... I have failed my mission...", + 2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", + 3: "Hm. You are a talented Trainer, but unfortunately not talented enough." + } + }, + "xerosic": { + "encounter": { + 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", + 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", + 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" + }, + "victory": { + 1: "Ah, you're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You did it! To the victor go the spoils!", + 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + 1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + 2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + 3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + 1: "You're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You've done well. Victory is yours.", + 3: "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, + "rocket_grunt": { + "encounter": { + 1: "Se prepara pra encrenca!", + 2: "We're pulling a big job here! Get lost, kid!", + 3: "Hand over your Pokémon, or face the wrath of Team Rocket!", + 4: "You're about to experience the true terror of Team Rocket!", + 5: "Hey, kid! Me am a Team Rocket member kind of guy!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "Equipe Rocket decolando de novo!", + 2: "Oh no! I dropped the Lift Key!", + 3: "I blew it!", + 4: "My associates won't stand for this!", + 5: "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" //Use of wrong grammar is deliberate. }, }, "magma_grunt": { "encounter": { - 1: "Se você se meter com a Equipe Magma, não teremos piedade!" + 1: "Se você se meter com a Equipe Magma, não teremos piedade!", + 2: "You'd better not interfere with our plans! We're making the world a better place!", + 3: "You're in the way! Team Magma has no time for kids like you!", + 4: "I hope you brought marshmallows because things are about to heat up!", + 5: "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" }, "victory": { - 1: "Ahn? Eu perdi?!" + 1: "Ahn? Eu perdi?!", + 2: "I can't believe I lost! I even skipped lunch for this", + 3: "No way! You're just a kid!", + 4: "Urrrgh... I should've ducked into our hideout right away...", + 5: "You beat me... Do you think the boss will dock my pay for this?" }, }, "aqua_grunt": { "encounter": { - 1: "Não pegamos leve com quem se mete com a Equipe Aqua, nem mesmo crianças!" + 1: "Não pegamos leve com quem se mete com a Equipe Aqua, nem mesmo crianças!", + 2: "Grrr... You've got some nerve meddling with Team Aqua!", + 3: "You're about to get soaked! And not just from my water Pokémon!", + 4: "We, Team Aqua, exist for the good of all!", + 5: "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" }, "victory": { - 1: "Tá de brincadeira!" + 1: "Tá de brincadeira!", + 2: "Arrgh, I didn't count on being meddled with by some meddling kid!", + 3: "I lost?! Guess I'll have to swim back to the hideout now...", + 4: "Oh, man, what a disaster... The boss is going to be furious...", + 5: "You beat me... Do you think the boss will make me walk the plank for this?" }, }, "galactic_grunt": { "encounter": { - 1: "Não mexe com a Equipe Galáctica!" + 1: "Não mexe com a Equipe Galáctica!", + 2: "Witness the power of our technology and the future we envision!", + 3: "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + 4: "Get ready to lose!", + 5: "Hope you're ready for a cosmic beatdown!" }, "victory": { - 1: "Fui amassado..." + 1: "Fui amassado...", + 2: "This setback means nothing in the grand scheme.", + 3: "Our plans are bigger than this defeat.", + 4: "How?!", + 5: "Note to self: practice Pokémon battling, ASAP." }, }, "plasma_grunt": { "encounter": { - 1: "Não toleramos pessoas que pensam diferente de nós!" + 1: "Não toleramos pessoas que pensam diferente de nós!", + 2: "If I win against you, release your Pokémon!", + 3: "If you get in the way of Team Plasma, I'll take care of you!", + 4: "Team Plasma will liberate Pokémon from selfish humans like you!", + 5: "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." }, "victory": { - 1: "Plasmaaaaaaaaa!" + 1: "Plasmaaaaaaaaa!", + 2: "How could I lose...", + 3: "...What a weak Pokémon, I'll just have to go steal some better ones!", + 4: "Great plans are always interrupted.", + 5: "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" }, }, "flare_grunt": { "encounter": { - 1: "A moda é a coisa mais importante pra gente!" + 1: "Your Pokémon are no match for the elegance of Team Flare.", + 2: "Hope you brought your sunglasses, because things are about to get bright!", + 3: "Team Flare will cleanse the world of imperfection!", + 4: "Prepare to face the brilliance of Team Flare!", + 5: "Fashion is most important to us!" }, "victory": { - 1: "O futuro não parece brilhante pra mim." + 1: "The future doesn't look bright for me.", + 2: "Perhaps there's more to battling than I thought. Back to the drawing board.", + 3: "Gahh?! I lost?!", + 4: "Even in defeat, Team Flare's elegance shines through.", + 5: "You may have beaten me, but when I lose, I go out in style!" }, }, "rocket_boss_giovanni_1": { @@ -4805,7 +5050,7 @@ export const PGFdialogue: DialogueTranslationEntries = { export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `Parece que a hora finalmente chegou novamente.\nVocê sabe por que veio aqui, não sabe? $Você foi atraído para cá, porque já esteve aqui antes.\nInúmeras vezes. - $Embora talvez isso possa ser contado.\nPara ser preciso, este é de fato o seu 5.643.853º ciclo. + $Embora talvez isso possa ser contado.\nPara ser preciso, este é de fato o seu {{cycleCount}}º ciclo. $A cada ciclo, sua mente retorna ao seu estado anterior.\nMesmo assim, de alguma forma, vestígios de seus antigos "eus" permanecem. $Até agora, você ainda não conseguiu, mas sinto uma presença diferente em você desta vez.\n $Você é o único aqui, embora pareça haver... outro. @@ -4820,7 +5065,7 @@ export const PGMbattleSpecDialogue: SimpleTranslationEntries = { export const PGFbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `Parece que a hora finalmente chegou novamente.\nVocê sabe por que veio aqui, não sabe? $Você foi atraída para cá, porque já esteve aqui antes.\nInúmeras vezes. - $Embora talvez isso possa ser contado.\nPara ser preciso, este é de fato o seu 5.643.853º ciclo. + $Embora talvez isso possa ser contado.\nPara ser preciso, este é de fato o seu {{cycleCount}}º ciclo. $A cada ciclo, sua mente retorna ao seu estado anterior.\nMesmo assim, de alguma forma, vestígios de seus antigos "eus" permanecem. $Até agora, você ainda não conseguiu, mas sinto uma presença diferente em você desta vez.\n $Você é a única aqui, embora pareça haver... outro. diff --git a/src/locales/pt_BR/filter-bar.ts b/src/locales/pt_BR/filter-bar.ts index 0c6a8e9ae50..d08d2d28707 100644 --- a/src/locales/pt_BR/filter-bar.ts +++ b/src/locales/pt_BR/filter-bar.ts @@ -3,20 +3,29 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "Ger.", "typeFilter": "Tipo", - "dexFilter": "Dex", - "unlocksFilter": "Outros", - "miscFilter": "Misc", + "caughtFilter": "Capturado", + "unlocksFilter": "Desbloqueios", + "miscFilter": "Outros", "sortFilter": "Ordem", "all": "Tudo", "normal": "Normal", "uncaught": "Não Capturado", - "passive": "Passive", - "passiveUnlocked": "Passiva Desbloq.", - "passiveLocked": "Passiva Bloq.", - "ribbon": "Ribbon", - "hasWon": "Ribbon - Yes", - "hasNotWon": "Ribbon - No", - "sortByNumber": "Núm.", + "passive": "Passiva", + "passiveUnlocked": "Passiva Desbloqueada", + "passiveLocked": "Passiva Bloqueada", + "costReduction": "Redução de Custo", + "costReductionUnlocked": "Redução de Custo Desbloq.", + "costReductionLocked": "Redução de Custo Bloq.", + "ribbon": "Fita", + "hasWon": "Fita - Sim", + "hasNotWon": "Fita - Não", + "hiddenAbility": "Habilidade Oculta", + "hasHiddenAbility": "Habilidade Oculta - Sim", + "noHiddenAbility": "Habilidade Oculta - Não", + "pokerus": "Pokérus", + "hasPokerus": "Pokérus - Sim", + "noPokerus": "Pokérus - Não", + "sortByNumber": "Número", "sortByCost": "Custo", "sortByCandies": "# Doces", "sortByIVs": "IVs", diff --git a/src/locales/pt_BR/move-trigger.ts b/src/locales/pt_BR/move-trigger.ts index c6f35d8f6d1..a96cdb27953 100644 --- a/src/locales/pt_BR/move-trigger.ts +++ b/src/locales/pt_BR/move-trigger.ts @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "{{pokemonName}} ficou envolto em uma luz forte!", "bellChimed": "Um sino tocou!", "foresawAnAttack": "{{pokemonName}} previu/num ataque!", + "isTighteningFocus": "{{pokemonName}} está\naumentando seu foco!", "hidUnderwater": "{{pokemonName}} se escondeu/nembaixo d'água!", "soothingAromaWaftedThroughArea": "Um aroma suave se espalhou pelo ambiente!", "sprangUp": "{{pokemonName}} se levantou!", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "O tipo de {{pokemonName}}\nmudou para combinar com {{targetPokemonName}}!", "suppressAbilities": "A habilidade de {{pokemonName}}\nfoi suprimida!", "swapArenaTags": "{{pokemonName}} trocou os efeitos de batalha que afetam cada lado do campo!", + "exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!", } as const; diff --git a/src/locales/pt_BR/settings.ts b/src/locales/pt_BR/settings.ts index c3a077acc4a..316dacb8fc6 100644 --- a/src/locales/pt_BR/settings.ts +++ b/src/locales/pt_BR/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "Pular Diálogos Vistos", "battleStyle": "Estilo de Batalha", "enableRetries": "Habilitar Novas Tentativas", + "hideIvs": "Esconder scanner de IV", "tutorials": "Tutorial", "touchControls": "Controles de Toque", "vibrations": "Vibração", diff --git a/src/locales/pt_BR/starter-select-ui-handler.ts b/src/locales/pt_BR/starter-select-ui-handler.ts index a6407b5368d..660076da413 100644 --- a/src/locales/pt_BR/starter-select-ui-handler.ts +++ b/src/locales/pt_BR/starter-select-ui-handler.ts @@ -7,7 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "Começar com esses Pokémon?", - "confirmExit": "Do you want to exit?", + "confirmExit": "Deseja sair?", "invalidParty": "Essa equipe de iniciais não é válida!", "gen1": "G1", "gen2": "G2", diff --git a/src/locales/pt_BR/trainers.ts b/src/locales/pt_BR/trainers.ts index 32cc46300d5..e920d07a22a 100644 --- a/src/locales/pt_BR/trainers.ts +++ b/src/locales/pt_BR/trainers.ts @@ -19,6 +19,19 @@ export const titles: SimpleTranslationEntries = { "galactic_boss": "Chefe da Equipe Galáctica", "plasma_boss": "Chefe da Equipe Plasma", "flare_boss": "Chefe da Equipe Flare", + + "rocket_admin": "Team Rocket Admin", + "rocket_admin_female": "Team Rocket Admin", + "magma_admin": "Team Magma Admin", + "magma_admin_female": "Team Magma Admin", + "aqua_admin": "Team Aqua Admin", + "aqua_admin_female": "Team Aqua Admin", + "galactic_commander": "Team Galactic Commander", + "galactic_commander_female": "Team Galactic Commander", + "plasma_sage": "Team Plasma Sage", + "plasma_admin": "Team Plasma Admin", + "flare_admin": "Team Flare Admin", + "flare_admin_female": "Team Flare Admin", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -128,32 +141,21 @@ export const trainerClasses: SimpleTranslationEntries = { "rocket_grunt": "Recruta da Equipe Rocket", "rocket_grunt_female": "Recruta da Equipe Rocket", "rocket_grunts": "Recrutas da Equipe Rocket", - "rocket_admin": "Rocket Admin", - "rocket_admin_female": "Rocket Admin", "magma_grunt": "Recruta da Equipe Magma", "magma_grunt_female": "Recruta da Equipe Magma", "magma_grunts": "Recrutas da Equipe Magma", - "magma_admin": "Magma Admin", - "magma_admin_female": "Magma Admin", "aqua_grunt": "Recruta da Equipe Aqua", "aqua_grunt_female": "Recruta da Equipe Aqua", "aqua_grunts": "Recrutas da Equipe Aqua", - "aqua_admin": "Aqua Admin", - "aqua_admin_female": "Aqua Admin", "galactic_grunt": "Recruta da Equipe Galáctica", "galactic_grunt_female": "Recruta da Equipe Galáctica", "galactic_grunts": "Recrutas da Equipe Galáctica", - "galactic_admin": "Galactic Admin", - "galactic_admin_female": "Galactic Admin", "plasma_grunt": "Recruta da Equipe Plasma", "plasma_grunt_female": "Recruta da Equipe Plasma", "plasma_grunts": "Recrutas da Equipe Plasma", - "plasma_sage": "Plasma Sage", "flare_grunt": "Recruta da Equipe Flare", "flare_grunt_female": "Recruta da Equipe Flare", "flare_grunts": "Recrutas da Equipe Flare", - "flare_admin": "Flare Admin", - "flare_admin_female": "Flare Admin", } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -282,6 +284,24 @@ export const trainerNames: SimpleTranslationEntries = { "leon": "Leon", "rival": "Finn", "rival_female": "Ivy", + + // Evil Team Admins + "archer": "Archer", + "ariana": "Ariana", + "proton": "Proton", + "petrel": "Petrel", + "tabitha": "Tabitha", + "courtney": "Courtney", + "shelly": "Shelly", + "matt": "Matt", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturn", + "zinzolin": "Zinzolin", + "rood": "Rood", + "xerosic": "Xerosic", + "bryony": "Bryony", + "maxie": "Maxie", "archie": "Archie", "cyrus": "Cyrus", diff --git a/src/locales/zh_CN/achv.ts b/src/locales/zh_CN/achv.ts index a32f244400d..303beeffea3 100644 --- a/src/locales/zh_CN/achv.ts +++ b/src/locales/zh_CN/achv.ts @@ -169,6 +169,10 @@ export const PGMachv: AchievementTranslationEntries = { name: "战无不胜", description: "在经典模式中通关游戏", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "带孩子来上班", + description: "通关经典模式时队伍中至少有一名未进化的宝可梦" + }, "MONO_GEN_ONE": { name: "最初的劲敌", diff --git a/src/locales/zh_CN/arena-tag.ts b/src/locales/zh_CN/arena-tag.ts index 027a5667415..974ef36d7af 100644 --- a/src/locales/zh_CN/arena-tag.ts +++ b/src/locales/zh_CN/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "{{moveName}}\n保护了敌方!", "conditionalProtectApply": "{{moveName}}\n保护了{{pokemonNameWithAffix}}!", "matBlockOnAdd": "{{pokemonNameWithAffix}}正在\n伺机使出掀榻榻米!", + "noCritOnAddPlayer": "{{moveName}}保护了你的\n队伍不被击中要害!", + "noCritOnAddEnemy": "{{moveName}}保护了对方的\n队伍不被击中要害!", + "noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!", "wishTagOnAdd": "{{pokemonNameWithAffix}}的\n祈愿实现了!", "mudSportOnAdd": "电气的威力减弱了!", "mudSportOnRemove": "玩泥巴的效果消失了!", diff --git a/src/locales/zh_CN/bgm-name.ts b/src/locales/zh_CN/bgm-name.ts index 71db565eb05..6039a259fd3 100644 --- a/src/locales/zh_CN/bgm-name.ts +++ b/src/locales/zh_CN/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries" : "{{name}}", "battle_kanto_champion": "黑2白2「决战!关都冠军」", "battle_johto_champion": "黑2白2「决战!城都冠军」", - "battle_hoenn_champion": "黑2白2「决战!丰缘冠军」", + "battle_hoenn_champion_g5": "黑2白2「决战!丰缘冠军」", + "battle_hoenn_champion_g6": "Ω红宝石α蓝宝石「决战!丰缘冠军」", "battle_sinnoh_champion": "黑2白2「决战!神奥冠军」", "battle_champion_alder": "黑白「决战!合众冠军」", "battle_champion_iris": "黑2白2「决战!合众冠军」", diff --git a/src/locales/zh_CN/dialogue.ts b/src/locales/zh_CN/dialogue.ts index 50d6c5f4333..20d1d0d6040 100644 --- a/src/locales/zh_CN/dialogue.ts +++ b/src/locales/zh_CN/dialogue.ts @@ -382,124 +382,297 @@ export const PGMdialogue: DialogueTranslationEntries = { 3: "好像是我晕船了…" }, }, - "rocket_grunt": { + "archer": { "encounter": { - 1: "你要有麻烦了!" + 1: "在你继续前进之前,\n让我看看你要如何和对付火箭队。", + 2: "我收到报告说你的实力与众不同,\n就让我来看看这是否属实吧。", + 3: "我是阿波罗,火箭对的干部。\n我不会对组织的敌人手软。" }, "victory": { - 1: "好讨厌的感觉啊!" + 1: "大失误……", + 2: "以我现在的实力,无法胜任我的任务……", + 3: "原……谅我,坂木。\n我竟被一名训练师打败了。." }, }, - "rocket_admin": { + "ariana": { "encounter": { - 1: "Oh? You managed to get this far? You must be quite the trainer.", - 2: "That's quite enough of you playing hero, kid.", - 3: "I'll show you how scary an angry adult can be!" + 1: `站住!我们可不能放过你!" + $这会损伤火箭对的名誉,明白吗?`, + 2: `我不知道也不想知道我的所作所为正确与否… + $我只要遵从坂木老大的指令就可以了!`, + 3: "你的旅途到此为止了,我会把你狠狠扳倒!" }, "victory": { - 1: "No! Forgive me Giovanni!", - 2: "How could this be?", - 3: "Urgh... You were too strong..." + 1: `切,你好强,可恶。 + $如果你加入火箭队,肯定能成为干部。`, + 2: "好……好崩溃……", + 3: "嗯啊啊!这不可能!我使出全力还是输了!" + }, + }, + "proton": { + "encounter": { + 1: "你想干什么?如果你要妨碍我们的事业,我可不会手下留情。", + 2: `你在这干什么?别人总说我是火箭队里最残忍和恐怖的人… + $我强烈推荐你别来碍我们的事!`, + 3: "我是兰斯,火箭队的干部。就让来扫除你对我们的阻挠。" + }, + "victory": { + 1: "我的防线崩溃了……", + 2: "你虽然这次赢了,但是这只是让火箭队的怒火继续燃烧!", + 3: "我输了…但是我不会忘记的。" + }, + }, + + "petrel": { + "encounter": { + 1: `哇哈哈哈,我们一直在等你。我?你不知道我是谁?是我,坂木啊。 + $伟大的坂木大人本人!哇哈哈哈!…啊?我听起来不像坂木吗? + $我连看起来都不像?怎么会呢,我可认真的变装了!`, + 2: "我是拉姆达,火箭队的干部。我不会允许你干涉我们的计划!", + 3: "火箭队干部拉姆达来会会这个入侵者!" + }, + "victory": { + 1: "好好好,我会说他在哪的", + 2: "我……我什么也做不了……坂木,请原谅我……", + 3: "不,我不能慌了神,必须通知其他人…" + }, + }, + "tabitha": { + "encounter": { + 1: "呵呵呵!原来你都一路来到这里了!但你来晚了!", + 2: `呵呵呵……你终于来了?我们小瞧你了,没不过事! + $我比你见过的所有队员都要厉害,我可不会拖延时间。 + $我会把你碾碎!`, + 3: "我要让你尝尝痛苦的滋味!认命吧!" + }, + "victory": { + 1: `呵呵呵!虽然你打败了我,但你根本没机会打败老大! + $如果你现在输了,你就不用面对那样严厉的鞭笞了!`, + 2: "呵呵呵……所以,我也输了……", + 3: "啊哈!怎么会这样?像我这样的干部\n竟然输给了一个随处可见的训练师……" + }, + }, + "courtney": { + "encounter": { + 1: "那个东西……你所拥有的那个东西……\n那就是……那就是我们熔岩队所寻找的东西……", + 2: "……那么……删除记忆……", + 3: "……哈……分析中……啊哈♪" + }, + "victory": { + 1: "……改变……世界。", + 2: `如预期。出乎意料。目标锁定…锁定你……完成。 + $开始……实验。材料是你…永远…啊哈……♪`, + 3: "……又来了?出乎意料……我就知道。你……很有趣!……啊哈哈!♪" + }, + }, + "shelly": { + "encounter": { + 1: `啊哈哈哈哈!你要插手海洋队的事? + $你要么是绝对无畏,要么就是无知,或者两者兼有! + $你太可爱了,太恶心了!我要把你打倒!`, + 2: "怎么回事?这个小鬼头是谁?", + 3: "冷静点,耐心点。我很快就会把你击溃。" + }, + "victory": { + 1: `啊哈哈哈哈!我们意外地被人干扰了!我们别无选择。 + $不得不撤退了,但这会不是你最后一次面对海洋队! + $我们还有其他计划!别忘了!`, + 2: "啊?!我是不是对你太温柔了?!", + 3: `呃…难道在对战中你也一刻不停地在变强吗? + $你真是个前途光明的小鬼……\n我和我的宝可梦已经没有任何力量去战斗了…… + $继续吧……准备去被水梧桐摧毁吧。` + }, + }, + "matt": { + "encounter": { + 1: "嚯!哈哈哈!怎么,你是不是脑子不正常了?\n看看你,像个幕下力士!", + 2: "“哦吼!你!你真是个有趣的孩子!", + 3: "你在这里干什么?你跟踪我们了吗?" + }, + "victory": { + 1: "好吧,在老大有时间对付你之前,我来成为你的对手!", + 2: `我能感觉到!我感觉到了,没错!你身上散发出的力量! + $更多!还想要更多!但看起来我们没时间了……`, + 3: "真有趣!我就知道你会让我尽兴的!\n我期待有一天再次面对你!" + }, + }, + "mars": { + "encounter": { + 1: "我是伙星,银河队的顶级干部之一。", + 2: "银河队对未来的愿景坚定不移。\n反对者将被无情地粉碎!", + 3: "“紧张吗?你是该感到紧张了!" + }, + "victory": { + 1: "这不可能!我怎么会输?!", + 2: "你很有本事,我承认。", + 3: "输了……犯了一个代价高昂的大错。" + } + }, + "jupiter": { + "encounter": { + 1: "岁星,银河队干部,为您效劳。", + 2: "抵抗是徒劳的。银河队必将获胜!", + 3: "你在发抖啊……已经害怕了吗?" + }, + "victory": { + 1: "不会吧……我输了?!", + 2: "厉害,你胆子真大!", + 3: "输成这样……真丢人。" + } + }, + "saturn": { + "encounter": { + 1: "我是镇星,银河队的干部。", + 2: "我们的使命是绝对的,任何阻碍都将被消灭!", + 3: "我从你的眼中看到的是恐惧吗?" + }, + "victory": { + 1: "不可能……被你打败了?!", + 2: "你证明了自己是一个值得尊敬的对手。", + 3: "失败的苦涩……难以接受……。" + }}, + "zinzolin": { + "encounter": { + 1: "你可能会对等离子队构成威胁,所以我们现在就消灭你!", + 2: "哦,天哪……我没想到要在这么冷的天气里战斗!", + 3: "能走到今天这一步,你真是个了不起的训练师。\n但一切到此结束。" + }, + "victory": { + 1: "魁奇思大人……我让你失望了……", + 2: "好冷,我不仅发抖,还要遭罪。", + 3: "哼。你比我想象的要聪明,但还不够。" + } + }, + "rood": { + "encounter": { + 1: "你对等离子队是个威胁。我们现在不能让你离开这里!", + 2: "哦,这寒风……我从没想过我必须在这里战斗!", + 3: "能走到今天这一步,你是一位了不起的训练师,但这就是你的结局了。" + }, + "victory": { + 1: "魁奇思大人……我的任务失败了", + 2: "寒风刺骨。我瑟瑟发抖。我痛苦不堪。", + 3: "嗯,你是很有才。但是要打败等离子队还不够……!" + } + }, + "xerosic": { + "encounter": { + 1: "啊哈哈!我很乐意。\n来吧,小训练师!让我们看看你有什么本事!", + 2: "嗯……你比看上去更强大。\n我想知道你体内有多少能量。", + 3: "我一直在等你!我需要对你做一点研究!\n来吧,我们开始吧!" + }, + "victory": { + 1: "啊,你好强大啊……嗯……确实非常强大。", + 2: "叮叮叮!你成功了!\n战利品归胜利者!", + 3: "太棒了!太神奇了!\n你的技巧和勇气都无与伦比!" + } + }, + "bryony": { + "encounter": { + 1: "我是芭菈,能与你一战是我的荣幸。\n让我看看你的实力。", + 2: "令人印象深刻……你比你看上去的还要强大。\n让我们看看你真正的实力。", + 3: "我预料到了你的到来。\n是时候进行一个小实验了,我们开始吧?" + }, + "victory": { + 1: "你很强大。哦,嗯嗯!确实非常强大", + 2: "叮叮叮!你做得很好。胜利属于你。", + 3: "太棒了!了不起!你的技巧和勇气值得称赞。" + } + }, + "rocket_grunt": { + "encounter": { + 1: "你要有麻烦了!", + 2: "我们要干大事了!\n闪一边去,小子!", + 3: "把你的宝可梦交过来,\n不然就尝尝火箭队的厉害!", + 4: "你准备好感受火箭队真正的恐怖了吗!", + 5: "喂,小子!俺可是火箭队滴!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "好讨厌的感觉啊!", + 2: "哎呀!不小心丢掉电梯钥匙啦!", + 3: "我搞砸了。", + 4: "我的伙计们不会放过你……!", + 5: "你说啥?俺们火箭队要玩完了?" //Use of wrong grammar is deliberate. }, }, "magma_grunt": { "encounter": { - 1: "如果你挡在熔岩队路上,那就别指望我们手下留情!" + 1: "如果你挡在熔岩队路上,那就别指望我们手下留情!", + 2: "你最好别妨碍我们的计划!\n我们会让世界变得更美好!", + 3: "少碍事!熔岩队没时间理你这样的小孩!", + 4: "你有棉花糖没?我来给你好好烤烤!", + 5: "我们会利用火山的力量!\n它马上要…爆发了!懂吗?嘿嘿嘿……" }, "victory": { - 1: "哈?我输了?!" - }, - }, - "magma_admin": { - "encounter": { - 1: "Hehehe! So you've come all the way here! But you're too late!", - 2: "You're going to meddle in Team Magma's affairs? You're so cute you're disgusting! I'll put you down kiddy!", - 3: "I'm going to give you a little taste of pain! Resign yourself to it!" - }, - "victory": { - 1: "Hehehe... So I lost...", - 2: "You're disgustingly strong!", - 3: "Ahahaha! Ouch!" + 1: "哈?我输了?!", + 2: "我怎么会输!我为了训练饭都不吃了!", + 3: "不会吧,不就是一个小孩!", + 4: "呃啊…我得赶快逃回基地…", + 5: "你打败我了…你觉得老大会扣我工资吗?" }, }, "aqua_grunt": { "encounter": { - 1: "即使是小孩,如果要和海洋队作对,也别指望我们手下留情!" + 1: "即使是小孩,如果要和海洋队作对,也别指望我们手下留情!", + 2: "嚯…你好大的胆子,敢惹我们海洋队!", + 3: "不仅是我的水系宝可梦,整片大海即将淹没你!", + 4: "我们海洋队,是为了大义!", + 5: "准备好被我的…呃…我宝可梦的海流冲走吧!" }, "victory": { - 1: "你在开玩笑吧?" - }, - }, - "aqua_admin": { - "encounter": { - 1: "I'm a cut above the grunts you've seen so far. I'm going to puvlerize you!", - 2: "Hahn? What's this? Who's this spoiled brat?", - 3: "What are you doing here? Did you follow us?" - }, - "victory": { - 1: "So I lost too...", - 2: "Ahhh?! Did I go too easy on you?!", - 3: "Wh-what was that?" + 1: "你在开玩笑吧?", + 2: "害,没想到这种小屁孩也要管我的闲事!", + 3: "我输了?看来我得自己游回基地了。", + 4: "不是吧,怎么会,老大要生气了……", + 5: "你打败了我…老大不会要让我上跳板吧……" }, }, "galactic_grunt": { "encounter": { - 1: "别惹银河队!" + 1: "别惹银河队!", + 2: "见识下我们的科技,和我们所设想的未来!", + 3: "以银河队之名,我会扫清一切挡路的人!", + 4: "准备输的一败涂地吧!", + 5: "希望你做好被宇宙力量碾压的准备。" }, "victory": { - 1: "停机了…" - }, - }, - "galactic_admin": { - "encounter": { - 1: "I'm one of Team Galactic's Commanders.", - 2: "Anything that opposes Team Galactic must be crushed! Even the very thought of opposition will not be tolerated!", - 3: "What's the matter? Don't tell me you're shaking?" - }, - "victory": { - 1: "This can't be?! I lost?! You... you uppity brat!", - 2: "You, my friend, are tough!", - 3: "Losing to some child... Being careless cost me too much." + 1: "停机了…", + 2: "从长远来看,这次的挫折不用在意。", + 3: "小失败不会影响我们的宏图伟业!", + 4: "咋回事!?", + 5: "个人记录:提升对战水平,优先级,高……" }, }, "plasma_grunt": { "encounter": { - 1: "异端不共戴天!" + 1: "异端不共戴天!", + 2: "要是我赢了你!就把你的宝可梦放生!", + 3: "要是敢和等离子队作对,我来好好关照你!", + 4: "等离子队会从你们这种自私的人手里解放宝可梦!", + 5: "我们的发型帅的一批,而我们的战斗水平呢,\n马上让你见识一下。" }, "victory": { - 1: "等离子子子子子子!" - }, - }, - "plasma_sage": { - "encounter": { - 1: "You could become a threat to Team Plasma, so we will eliminate you here!", - 2: "Oh, for crying out loud... I didn't expect to have to fight!", - 3: "You're an impressive Trainer to have made it this far." - }, - "victory": { - 1: "Ghetsis...", - 2: "It's bitter cold. I'm shivering. I'm suffering.", - 3: "Hmph. You're a smarter Trainer than I expected." + 1: "等离子子子子子子!", + 2: "我怎么会输……", + 3: "…没用的家伙!我得去偷个厉害点的宝可梦!", + 4: "伟大的事业总会被人阻挠…", + 5: "烂完了…烂烂烂烂烂!等离子队烂了!\n说短点就是,等烂子队!" }, }, "flare_grunt": { "encounter": { - 1: "时尚最重要!" + 1: "你的宝可梦无法与闪焰队的优雅相提并论", + 2: "带个墨镜吧,别被我闪瞎狗眼了!", + 3: "闪焰队将净化这个不完美的世界!", + 4: "准备面对闪焰队的美!", + 5: "时尚对我们来说最重要!" }, "victory": { - 1: "未来一片黑暗啊…" - }, - }, - "flare_admin": { - "encounter": { - 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", - 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", - 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" - }, - "victory": { - 1: "You're quite strong. Oh yes-very strong, indeed.", - 2: "Ding-ding-ding! Yup, you did it! To the victor goes the spoils!", - 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + 1: "我的未来看起来并不明亮…", + 2: "这战斗比我想的更难搞,我得重头训练了。", + 3: "啊啊?我输了?!", + 4: "就算是在失败当中,闪焰队依旧优雅动人!", + 5: "你虽然打败了我,但是我输的也这么潇洒!" }, }, "rocket_boss_giovanni_1": { @@ -2405,7 +2578,7 @@ export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `看来终于又到了那个时候。\n你知道自己为何会来到这里,不是吗? $你被吸引到这里,因为你以前就来过这里。\n无数次。 - $尽管,或许可以数一数。\n准确地说,这实际上是你的第5,643,853次循环。 + $尽管,或许可以数一数。\n准确地说,这实际上是你的第{{cycleCount}}次循环。 $每一次循环,你的思想都会恢复到之前的状态。\n即便如此,不知何故,你之前自我的残留仍然存在。 $直到现在,你仍未成功,\n但我感觉这次你身上有一种异样的气息。 $你是这里唯一的人,尽管感觉上还有……另一个人。 diff --git a/src/locales/zh_CN/filter-bar.ts b/src/locales/zh_CN/filter-bar.ts index 5ccc5b8d9d9..6f484fc8635 100644 --- a/src/locales/zh_CN/filter-bar.ts +++ b/src/locales/zh_CN/filter-bar.ts @@ -3,7 +3,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "世代", "typeFilter": "属性", - "dexFilter": "Dex", + "caughtFilter": "捕获", "unlocksFilter": "解锁", "miscFilter": "混合", "sortFilter": "排序", @@ -13,9 +13,18 @@ export const filterBar: SimpleTranslationEntries = { "passive": "被动", "passiveUnlocked": "被动解锁", "passiveLocked": "被动未解锁", + "costReduction": "费用降低", + "costReductionUnlocked": "已降费", + "costReductionLocked": "未降费", "ribbon": "缎带", "hasWon": "有缎带", "hasNotWon": "无缎带", + "hiddenAbility": "梦特", + "hasHiddenAbility": "有梦特", + "noHiddenAbility": "无梦特", + "pokerus": "病毒", + "hasPokerus": "有病毒", + "noPokerus": "无病毒", "sortByNumber": "编号", "sortByCost": "费用", "sortByCandies": "糖果", diff --git a/src/locales/zh_CN/move-trigger.ts b/src/locales/zh_CN/move-trigger.ts index 0efe24f76f0..d46ef1e313e 100644 --- a/src/locales/zh_CN/move-trigger.ts +++ b/src/locales/zh_CN/move-trigger.ts @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "强光包围了{{pokemonName}}\n!", "bellChimed": "铃声响彻四周!", "foresawAnAttack": "{{pokemonName}}\n预知了未来的攻击!", + "isTighteningFocus": "{{pokemonName}}正在集中注意力!", "hidUnderwater": "{{pokemonName}}\n潜入了水中!", "soothingAromaWaftedThroughArea": "怡人的香气扩散了开来!", "sprangUp": "{{pokemonName}}\n高高地跳了起来!", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "{{pokemonName}}\n变成了{{targetPokemonName}}的属性!", "suppressAbilities": "{{pokemonName}}的特性\n变得无效了!", "swapArenaTags": "{{pokemonName}}\n交换了双方的场地效果!", + "exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!", } as const; diff --git a/src/locales/zh_CN/settings.ts b/src/locales/zh_CN/settings.ts index 92372deec42..a55fe3e8cd9 100644 --- a/src/locales/zh_CN/settings.ts +++ b/src/locales/zh_CN/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "跳过已读对话", "battleStyle": "对战模式", "enableRetries": "允许重试", + "hideIvs": "禁用个体值探测器信息", "tutorials": "教程", "touchControls": "触摸操作", "vibrations": "手柄震动", diff --git a/src/locales/zh_CN/starter-select-ui-handler.ts b/src/locales/zh_CN/starter-select-ui-handler.ts index b8c491288e1..c9c1cf501ef 100644 --- a/src/locales/zh_CN/starter-select-ui-handler.ts +++ b/src/locales/zh_CN/starter-select-ui-handler.ts @@ -7,7 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "使用这些宝可梦开始游戏吗?", - "confirmExit": "Do you want to exit?", + "confirmExit": "确定要退出吗?", "invalidParty": "初始队伍不可用!", "gen1": "I", "gen2": "II", diff --git a/src/locales/zh_CN/trainers.ts b/src/locales/zh_CN/trainers.ts index 1d32fdf6e02..ab7b3fce6ac 100644 --- a/src/locales/zh_CN/trainers.ts +++ b/src/locales/zh_CN/trainers.ts @@ -19,6 +19,19 @@ export const titles: SimpleTranslationEntries = { "galactic_boss": "银河队老大", "plasma_boss": "等离子队老大", "flare_boss": "闪焰队老大", + + "rocket_admin": "火箭队干部", + "rocket_admin_female": "火箭队干部", + "magma_admin": "熔岩队干部", + "magma_admin_female": "熔岩队干部", + "aqua_admin": "海洋队干部", + "aqua_admin_female": "海洋队干部", + "galactic_commander": "银河队干部", + "galactic_commander_female": "银河队干部", + "plasma_sage": "等离子队贤人", + "plasma_admin": "等离子队干部", + "flare_admin": "闪焰队干部", + "flare_admin_female": "闪焰队干部", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -128,32 +141,21 @@ export const trainerClasses: SimpleTranslationEntries = { "rocket_grunt": "火箭队手下", "rocket_grunt_female": "火箭队手下", "rocket_grunts": "火箭队手下们", - "rocket_admin": "Rocket Admin", - "rocket_admin_female": "Rocket Admin", "magma_grunt": "熔岩队手下", "magma_grunt_female": "熔岩队手下", "magma_grunts": "熔岩队手下们", - "magma_admin": "Magma Admin", - "magma_admin_female": "Magma Admin", "aqua_grunt": "海洋队手下", "aqua_grunt_female": "海洋队手下", "aqua_grunts": "海洋队手下们", - "aqua_admin": "Aqua Admin", - "aqua_admin_female": "Aqua Admin", "galactic_grunt": "银河队手下", "galactic_grunt_female": "银河队手下", "galactic_grunts": "银河队手下们", - "galactic_admin": "Galactic Admin", - "galactic_admin_female": "Galactic Admin", "plasma_grunt": "等离子队手下", "plasma_grunt_female": "等离子队手下", "plasma_grunts": "等离子队手下们", - "plasma_sage": "Plasma Sage", "flare_grunt": "闪焰队手下", "flare_grunt_female": "闪焰队手下", "flare_grunts": "闪焰队手下们", - "flare_admin": "Flare Admin", - "flare_admin_female": "Flare Admin", } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -339,6 +341,23 @@ export const trainerNames: SimpleTranslationEntries = { "rival": "芬恩", "rival_female": "艾薇", + // Evil Team Admins + "archer": "阿波罗", + "ariana": "雅典娜", + "proton": "兰斯", + "petrel": "拉姆达", + "tabitha": "火村", + "courtney": "火雁", + "shelly": "阿泉", + "matt": "阿潮", + "mars": "伙星", + "jupiter": "碎星", + "saturn": "镇星", + "zinzolin": "维奥", + "rood": "罗德", + "xerosic": "库瑟洛斯奇", + "bryony": "芭菈", + // ---- 组织老大 Bosses ---- "maxie": "赤焰松", "archie": "水梧桐", diff --git a/src/locales/zh_TW/achv.ts b/src/locales/zh_TW/achv.ts index 8b85f59447e..dbd1d44d220 100644 --- a/src/locales/zh_TW/achv.ts +++ b/src/locales/zh_TW/achv.ts @@ -169,6 +169,10 @@ export const PGMachv: AchievementTranslationEntries = { name: "戰無不勝", description: "在經典模式中通關遊戲", }, + "UNEVOLVED_CLASSIC_VICTORY": { + name: "Bring Your Child To Work Day", + description: "Beat the game in Classic Mode with at least one unevolved party member." + }, "MONO_GEN_ONE": { name: "最初的勁敵", @@ -264,6 +268,10 @@ export const PGMachv: AchievementTranslationEntries = { "MONO_FAIRY": { name: "林克,醒醒!", }, + "FRESH_START": { + name: "First Try!", + description: "Complete the Fresh Start challenge." + } } as const; // Achievement translations for the when the player character is female (it for now uses the same translations as the male version) diff --git a/src/locales/zh_TW/arena-tag.ts b/src/locales/zh_TW/arena-tag.ts index 8bc2302368a..ee7d2eb7bc5 100644 --- a/src/locales/zh_TW/arena-tag.ts +++ b/src/locales/zh_TW/arena-tag.ts @@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = { "conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!", "conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!", "matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!", + "noCritOnAddPlayer": "{{moveName}}保護了你的\n隊伍不被擊中要害!", + "noCritOnAddEnemy": "{{moveName}}保護了對方的\n隊伍不被擊中要害!", + "noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!", "wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!", "mudSportOnAdd": "Electricity's power was weakened!", "mudSportOnRemove": "The effects of Mud Sport\nhave faded.", diff --git a/src/locales/zh_TW/bgm-name.ts b/src/locales/zh_TW/bgm-name.ts index 01fb86b281d..be9a8f621c7 100644 --- a/src/locales/zh_TW/bgm-name.ts +++ b/src/locales/zh_TW/bgm-name.ts @@ -5,7 +5,8 @@ export const bgmName: SimpleTranslationEntries = { "missing_entries" : "{{name}}", "battle_kanto_champion": "B2W2 Kanto Champion Battle", "battle_johto_champion": "B2W2 Johto Champion Battle", - "battle_hoenn_champion": "B2W2 Hoenn Champion Battle", + "battle_hoenn_champion_g5": "B2W2 Hoenn Champion Battle", + "battle_hoenn_champion_g6": "ORAS Hoenn Champion Battle", "battle_sinnoh_champion": "B2W2 Sinnoh Champion Battle", "battle_champion_alder": "BW Unova Champion Battle", "battle_champion_iris": "B2W2 Unova Champion Battle", diff --git a/src/locales/zh_TW/challenges.ts b/src/locales/zh_TW/challenges.ts index e702ec4f278..15571e11912 100644 --- a/src/locales/zh_TW/challenges.ts +++ b/src/locales/zh_TW/challenges.ts @@ -22,4 +22,10 @@ export const challenges: TranslationEntries = { "desc": "你只能使用{{type}}\n屬性的寶可夢", "desc_default": "你只能使用所選\n屬性的寶可夢" }, + "freshStart": { + "name": "Fresh Start", + "desc": "You can only use the original starters, and only as if you had just started PokéRogue.", + "value.0": "Off", + "value.1": "On", + } } as const; diff --git a/src/locales/zh_TW/dialogue.ts b/src/locales/zh_TW/dialogue.ts index 530906eda5b..cfe43317bb7 100644 --- a/src/locales/zh_TW/dialogue.ts +++ b/src/locales/zh_TW/dialogue.ts @@ -382,124 +382,297 @@ export const PGMdialogue: DialogueTranslationEntries = { 3: "好像是我暈船了…" }, }, - "rocket_grunt": { + "archer": { "encounter": { - 1: "Prepare for trouble!" + 1: "Before you go any further, let's see how you far against us, Team Rocket!", + 2: "I have received reports that your skills are not insignificant. Let's see if they are true.", + 3: "I am Archer, an Admin of Team Rocket. And I do not go easy on enemies of our organization." }, "victory": { - 1: "Team Rocket blasting off again!" + 1: "What a blunder!", + 2: "With my current skills, I was not up to the task after all.", + 3: "F-forgive me, Giovanni... For me to be defeated by a mere trainer..." }, }, - "rocket_admin": { + "ariana": { "encounter": { - 1: "Oh? You managed to get this far? You must be quite the trainer.", - 2: "That's quite enough of you playing hero, kid.", - 3: "I'll show you how scary an angry adult can be!" + 1: `Hold it right there! We can't someone on the loose." + $It's harmful to Team Rocket's pride, you see.`, + 2: `I don't know or care if what I'm doing is right or wrong... + $I just put my faith in Giovanni and do as I am told`, + 3: "Your trip ends here. I'm going to take you down!" }, "victory": { - 1: "No! Forgive me Giovanni!", - 2: "How could this be?", - 3: "Urgh... You were too strong..." + 1: `Tch, you really are strong. It's too bad. + $If you were to join Team Rocket, you could become an Executive.`, + 2: "I... I'm shattered...", + 3: "Aaaieeeee! This can't be happening! I fought hard, but I still lost…" }, }, - "magma_grunt": { + "proton": { "encounter": { - 1: " If you get in the way of Team Magma, don’t expect any mercy!" + 1: "What do you want? If you interrupt our work, don't expect any mercy!", + 2: `What do we have here? I am often labeled as the scariest and cruelest guy in Team Rocket… + $I strongly urge you not to interfere with our business!`, + 3: "I am Proton, an Admin of Team Rocket. I am here to put an end to your meddling!" }, "victory": { - 1: "Huh? I lost?!" + 1: "The fortress came down!", + 2: "You may have won this time… But all you did was make Team Rocket's wrath grow…", + 3: "I am defeated… But I will not forget this!" }, }, - "magma_admin": { + + "petrel": { + "encounter": { + 1: `Muhahaha, we've been waiting for you. Me? You don't know who I am? It is me, Giovanni. + $The majestic Giovanni himself! Wahahaha! …Huh? I don't sound anything like Giovanni? + $I don't even look like Giovanni? How come? I've worked so hard to mimic him!`, + 2: "I am Petrel, an Admin of Team Rocket. I will not allow you to interfere with our plans!", + 3: "Rocket Executive Petrel will deal with this intruder!" + }, + "victory": { + 1: "OK, OK. I'll tell you where he is.", + 2: "I… I couldn't do a thing… Giovanni, please forgive me…", + 3: "No, I can't let this affect me. I have to inform the others…" + }, + }, + "tabitha": { "encounter": { 1: "Hehehe! So you've come all the way here! But you're too late!", - 2: "You're going to meddle in Team Magma's affairs? You're so cute you're disgusting! I'll put you down kiddy!", + 2: `Hehehe... Got here already, did you? We underestimated you! But this is it! + $I'm a cut above the Grunts you've seen so far. I'm not stalling for time. + $I'm going to pulverize you!`, 3: "I'm going to give you a little taste of pain! Resign yourself to it!" }, "victory": { - 1: "Hehehe... So I lost...", - 2: "You're disgustingly strong!", - 3: "Ahahaha! Ouch!" + 1: `Hehehe! You might have beaten me, but you don't stand a chance against the Boss! + $If you get lost now, you won't have to face a sound whipping!`, + 2: "Hehehe... So, I lost, too...", + 3: "Ahya! How could this be? For an Admin like me to lose to some random trainer..." }, }, - "aqua_grunt": { + "courtney": { "encounter": { - 1: "No one who crosses Team Aqua gets any mercy, not even kids!" + 1: "The thing...The thing that you hold...That is what... That's what we of Team Magma seek...", + 2: "... Well then...Deleting...", + 3: "...Ha. ...Analyzing... ...Hah♪" }, "victory": { - 1: "You're kidding me!" + 1: "... ...Change...the world.", + 2: `As anticipated. Unanticipated. You. Target lock...completed. + $Commencing...experiment. You. Forever. Aha... ♪`, + 3: "...Again? That's unanticipated. ...I knew it. You...are interesting! ...Haha. ♪" }, }, - "aqua_admin": { + "shelly": { "encounter": { - 1: "I'm a cut above the grunts you've seen so far. I'm going to puvlerize you!", - 2: "Hahn? What's this? Who's this spoiled brat?", + 1: `Ahahahaha! You're going to meddle in Team Aqua's affairs? + $You're either absolutely fearless, simply ignorant, or both! + $You're so cute, you're disgusting! I'll put you down`, + 2: "What's this? Who's this spoiled brat?", + 3: "Cool your jets. Be patient. I'll crush you shortly." + }, + "victory": { + 1: `Ahahahaha! We got meddled with unexpectedly! We're out of options. + $We'll have to pull out. But this isn't the last you'll see of Team Aqua! + $We have other plans! Don't you forget it!`, + 2: "Ahhh?! Did I go too easy on you?!", + 3: `Uh. Are you telling me you've upped your game even more during the fight? + $You're a brat with a bright future… My Pokémon and I don't have any strength left to fight… + $Go on… Go and be destroyed by Archie.` + }, + }, + "matt": { + "encounter": { + 1: "Hoohahaha! What, you got a screw loose or something? Look at you, little Makuhita person!", + 2: "Oho! You! You're that funny kid!", 3: "What are you doing here? Did you follow us?" }, "victory": { - 1: "So I lost too...", - 2: "Ahhh?! Did I go too easy on you?!", - 3: "Wh-what was that?" + 1: "All right then, until the Boss has time for you, I'll be your opponent!", + 2: `I can feel it! I can feel it, all right! The strength coming offa you! + $More! I still want more! But looks like we're outta time...`, + 3: "That was fun! I knew you'd show me a good time! I look forward to facing you again someday!" }, }, - "galactic_grunt": { + "mars": { "encounter": { - 1: "Don't mess with Team Galactic!" + 1: "I'm Mars, one of Team Galactic's top Commanders.", + 2: "Team Galactic's vision for the future is unwavering. Opposition will be crushed without mercy!", + 3: "Feeling nervous? You should be!" }, "victory": { - 1: "Shut down..." - }, + 1: "This can't be happening! How did I lose?!", + 2: "You have some skill, I'll give you that.", + 3: "Defeated... This was a costly mistake." + } }, - "galactic_admin": { + "jupiter": { "encounter": { - 1: "I'm one of Team Galactic's Commanders.", - 2: "Anything that opposes Team Galactic must be crushed! Even the very thought of opposition will not be tolerated!", - 3: "What's the matter? Don't tell me you're shaking?" + 1: "Jupiter, Commander of Team Galactic, at your service.", + 2: "Resistance is futile. Team Galactic will prevail!", + 3: "You're trembling... scared already?" }, "victory": { - 1: "This can't be?! I lost?! You... you uppity brat!", - 2: "You, my friend, are tough!", - 3: "Losing to some child... Being careless cost me too much." - }, + 1: "No way... I lost?!", + 2: "Impressive, you've got guts!", + 3: "Losing like this... How embarrassing." + } }, - "plasma_grunt": { + "saturn": { "encounter": { - 1: "We won't tolerate people who have different ideas!" + 1: "I am Saturn, Commander of Team Galactic.", + 2: "Our mission is absolute. Any hindrance will be obliterated!", + 3: "Is that fear I see in your eyes?" }, "victory": { - 1: "Plasmaaaaaaaaa!" - }, - }, - "plasma_sage": { + 1: "Impossible... Defeated by you?!", + 2: "You have proven yourself a worthy adversary.", + 3: "Bestowed in defeat... This is unacceptable." + }}, + "zinzolin": { "encounter": { - 1: "You could become a threat to Team Plasma, so we will eliminate you here!", - 2: "Oh, for crying out loud... I didn't expect to have to fight!", - 3: "You're an impressive Trainer to have made it this far." + 1: "You could become a threat to Team Plasma, so we will eliminate you here and now!", + 2: "Oh, for crying out loud... I didn't expect to have to battle in this freezing cold!", + 3: "You're an impressive Trainer to have made it this far. But it ends here." }, "victory": { - 1: "Ghetsis...", - 2: "It's bitter cold. I'm shivering. I'm suffering.", - 3: "Hmph. You're a smarter Trainer than I expected." - }, + 1: "Ghetsis... I have failed you...", + 2: "It's bitter cold. I'm shivering. I'm suffering. Yet, I still stand victorious.", + 3: "Hmph. You're a smarter Trainer than I expected, but not smart enough." + } }, - "flare_grunt": { + "rood": { "encounter": { - 1: "Fashion is most important to us!" + 1: "You are a threat to Team Plasma. We cannot let you walk away from here and now!", + 2: "Oh, this icy wind... I never thought I'd have to fight here!", + 3: "You are a remarkable Trainer to have made it this far. But this is where it ends." }, "victory": { - 1: "The future doesn't look bright for me." - }, + 1: "Ghetsis... I have failed my mission...", + 2: "The cold is piercing. I'm shivering. I'm suffering. Yet, I have triumphed.", + 3: "Hm. You are a talented Trainer, but unfortunately not talented enough." + } }, - "flare_admin": { + "xerosic": { "encounter": { 1: "Ah ha ha! It would be my pleasure. Come on, little Trainer! Let's see what you've got!", 2: "Hmm... You're more powerful than you look. I wonder how much energy there is inside you.", 3: "I've been waiting for you! I need to do a little research on you! Come, let us begin!" }, "victory": { - 1: "You're quite strong. Oh yes-very strong, indeed.", - 2: "Ding-ding-ding! Yup, you did it! To the victor goes the spoils!", + 1: "Ah, you're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You did it! To the victor go the spoils!", 3: "Wonderful! Amazing! You have tremendous skill and bravery!" + } + }, + "bryony": { + "encounter": { + 1: "I am Bryony, and it would be my pleasure to battle you. Show me what you've got.", + 2: "Impressive... You're more powerful than you appear. Let's see the true extent of your energy.", + 3: "I've anticipated your arrival. It's time for a little test. Shall we begin?" + }, + "victory": { + 1: "You're quite strong. Oh yes—very strong, indeed.", + 2: "Ding-ding-ding! You've done well. Victory is yours.", + 3: "Wonderful! Remarkable! Your skill and bravery are commendable." + } + }, + "rocket_grunt": { + "encounter": { + 1: "Prepare for trouble!", + 2: "We're pulling a big job here! Get lost, kid!", + 3: "Hand over your Pokémon, or face the wrath of Team Rocket!", + 4: "You're about to experience the true terror of Team Rocket!", + 5: "Hey, kid! Me am a Team Rocket member kind of guy!" //Use of wrong grammar is deliberate + }, + "victory": { + 1: "Team Rocket blasting off again!", + 2: "Oh no! I dropped the Lift Key!", + 3: "I blew it!", + 4: "My associates won't stand for this!", + 5: "You say what? Team Rocket bye-bye a go-go? Broken it is says you?" //Use of wrong grammar is deliberate. + }, + }, + "magma_grunt": { + "encounter": { + 1: "If you get in the way of Team Magma, don’t expect any mercy!", + 2: "You'd better not interfere with our plans! We're making the world a better place!", + 3: "You're in the way! Team Magma has no time for kids like you!", + 4: "I hope you brought marshmallows because things are about to heat up!", + 5: "We're going to use the power of a volcano! It's gonna be... explosive! Get it? Heh heh!" + }, + "victory": { + 1: "Huh? I lost?!", + 2: "I can't believe I lost! I even skipped lunch for this", + 3: "No way! You're just a kid!", + 4: "Urrrgh... I should've ducked into our hideout right away...", + 5: "You beat me... Do you think the boss will dock my pay for this?" + }, + }, + "aqua_grunt": { + "encounter": { + 1: "No one who crosses Team Aqua gets any mercy, not even kids!", + 2: "Grrr... You've got some nerve meddling with Team Aqua!", + 3: "You're about to get soaked! And not just from my water Pokémon!", + 4: "We, Team Aqua, exist for the good of all!", + 5: "Prepare to be washed away by the tides of my... uh, Pokémon! Yeah, my Pokémon!" + }, + "victory": { + 1: "You're kidding me!", + 2: "Arrgh, I didn't count on being meddled with by some meddling kid!", + 3: "I lost?! Guess I'll have to swim back to the hideout now...", + 4: "Oh, man, what a disaster... The boss is going to be furious...", + 5: "You beat me... Do you think the boss will make me walk the plank for this?" + }, + }, + "galactic_grunt": { + "encounter": { + 1: "Don't mess with Team Galactic!", + 2: "Witness the power of our technology and the future we envision!", + 3: "In the name of Team Galactic, I'll eliminate anyone who stands in our way!", + 4: "Get ready to lose!", + 5: "Hope you're ready for a cosmic beatdown!" + }, + "victory": { + 1: "Shut down...", + 2: "This setback means nothing in the grand scheme.", + 3: "Our plans are bigger than this defeat.", + 4: "How?!", + 5: "Note to self: practice Pokémon battling, ASAP." + }, + }, + "plasma_grunt": { + "encounter": { + 1: "We won't tolerate people who have different ideas!", + 2: "If I win against you, release your Pokémon!", + 3: "If you get in the way of Team Plasma, I'll take care of you!", + 4: "Team Plasma will liberate Pokémon from selfish humans like you!", + 5: "Our hairstyles are out of this world... but our battling skills? You'll find out soon enough." + }, + "victory": { + 1: "Plasmaaaaaaaaa!", + 2: "How could I lose...", + 3: "...What a weak Pokémon, I'll just have to go steal some better ones!", + 4: "Great plans are always interrupted.", + 5: "This is bad... Badbadbadbadbadbadbad! Bad for Team Plasma! Or Plasbad, for short!" + }, + }, + "flare_grunt": { + "encounter": { + 1: "Your Pokémon are no match for the elegance of Team Flare.", + 2: "Hope you brought your sunglasses, because things are about to get bright!", + 3: "Team Flare will cleanse the world of imperfection!", + 4: "Prepare to face the brilliance of Team Flare!", + 5: "Fashion is most important to us!" + }, + "victory": { + 1: "The future doesn't look bright for me.", + 2: "Perhaps there's more to battling than I thought. Back to the drawing board.", + 3: "Gahh?! I lost?!", + 4: "Even in defeat, Team Flare's elegance shines through.", + 5: "You may have beaten me, but when I lose, I go out in style!" }, }, "rocket_boss_giovanni_1": { @@ -2404,7 +2577,7 @@ export const PGFdialogue: DialogueTranslationEntries = PGMdialogue; export const PGMbattleSpecDialogue: SimpleTranslationEntries = { "encounter": `看來終於又到了那個時候。\n你知道自己為何會來到這裡,不是嗎? $你被吸引到這裡,因為你以前就來過這裡。\n無數次。 - $儘管,或許可以數一數。\n準確地說,這實際上是你的第5,643,853次循環。 + $儘管,或許可以數一數。\n準確地說,這實際上是你的第{{cycleCount}}次循環。 $每一次循環,你的思想都會恢復到之前的狀態。\n即便如此,不知何故,你之前自我的殘留仍然存在。 $直到現在,你仍未成功,但我感覺這次你身上有一種異樣的氣息。 $你是這裡唯一的人,儘管感覺上還有……另一個人。 diff --git a/src/locales/zh_TW/filter-bar.ts b/src/locales/zh_TW/filter-bar.ts index 0290bda62de..8916088d884 100644 --- a/src/locales/zh_TW/filter-bar.ts +++ b/src/locales/zh_TW/filter-bar.ts @@ -3,7 +3,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const filterBar: SimpleTranslationEntries = { "genFilter": "世代", "typeFilter": "屬性", - "dexFilter": "Dex", + "caughtFilter": "捕獲", "unlocksFilter": "解鎖", "miscFilter": "混合", "sortFilter": "排序", @@ -13,9 +13,18 @@ export const filterBar: SimpleTranslationEntries = { "passive": "被動", "passiveUnlocked": "被動解鎖", "passiveLocked": "被動未解鎖", + "costReduction": "Cost Reduction", + "costReductionUnlocked": "Cost Reduction Unlocked", + "costReductionLocked": "Cost Reduction Locked", "ribbon": "緞帶", "hasWon": "有緞帶", "hasNotWon": "無緞帶", + "hiddenAbility": "Hidden Ability", + "hasHiddenAbility": "Hidden Ability - Yes", + "noHiddenAbility": "Hidden Ability - No", + "pokerus": "Pokerus", + "hasPokerus": "Pokerus - Yes", + "noPokerus": "Pokerus - No", "sortByNumber": "編號", "sortByCost": "花費", "sortByCandies": "糖果", diff --git a/src/locales/zh_TW/move-trigger.ts b/src/locales/zh_TW/move-trigger.ts index 019aa84390c..0831ef82637 100644 --- a/src/locales/zh_TW/move-trigger.ts +++ b/src/locales/zh_TW/move-trigger.ts @@ -21,6 +21,7 @@ export const moveTriggers: SimpleTranslationEntries = { "isGlowing": "強光包圍了\n{{pokemonName}}!", "bellChimed": "鈴聲響徹四周!", "foresawAnAttack": "{{pokemonName}}\n預知了未來的攻擊!", + "isTighteningFocus": "{{pokemonName}}正在集中注意力!", "hidUnderwater": "{{pokemonName}}\n潛入了水中!", "soothingAromaWaftedThroughArea": "怡人的香氣擴散了開來!", "sprangUp": "{{pokemonName}}\n高高地跳了起來!", @@ -59,4 +60,5 @@ export const moveTriggers: SimpleTranslationEntries = { "copyType": "{{pokemonName}}變成了{{targetPokemonName}}的屬性!", "suppressAbilities": "{{pokemonName}}的特性\n變得無效了!", "swapArenaTags": "{{pokemonName}}\n交換了雙方的場地效果!", + "exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!", } as const; diff --git a/src/locales/zh_TW/settings.ts b/src/locales/zh_TW/settings.ts index c6c055ed4d2..133cce61fd3 100644 --- a/src/locales/zh_TW/settings.ts +++ b/src/locales/zh_TW/settings.ts @@ -15,6 +15,7 @@ export const settings: SimpleTranslationEntries = { "skipSeenDialogues": "跳過已讀對話", "battleStyle": "對戰模式", "enableRetries": "允許重試", + "hideIvs": "禁用個體值探測器信息", "tutorials": "教程", "touchControls": "觸摸操作", "vibrations": "手柄震動", diff --git a/src/locales/zh_TW/trainers.ts b/src/locales/zh_TW/trainers.ts index 0efae11bbea..48159efd1c6 100644 --- a/src/locales/zh_TW/trainers.ts +++ b/src/locales/zh_TW/trainers.ts @@ -13,6 +13,25 @@ export const titles: SimpleTranslationEntries = { "rival": "勁敵", "professor": "博士", "frontier_brain": "開拓頭腦", + "rocket_boss": "Team Rocket Boss", + "magma_boss": "Team Magma Boss", + "aqua_boss": "Team Aqua Boss", + "galactic_boss": "Team Galactic Boss", + "plasma_boss": "Team Plasma Boss", + "flare_boss": "Team Flare Boss", + + "rocket_admin": "Team Rocket Admin", + "rocket_admin_female": "Team Rocket Admin", + "magma_admin": "Team Magma Admin", + "magma_admin_female": "Team Magma Admin", + "aqua_admin": "Team Aqua Admin", + "aqua_admin_female": "Team Aqua Admin", + "galactic_commander": "Team Galactic Commander", + "galactic_commander_female": "Team Galactic Commander", + "plasma_sage": "Team Plasma Sage", + "plasma_admin": "Team Plasma Admin", + "flare_admin": "Team Flare Admin", + "flare_admin_female": "Team Flare Admin", // Maybe if we add the evil teams we can add "Team Rocket" and "Team Aqua" etc. here as well as "Team Rocket Boss" and "Team Aqua Admin" etc. } as const; @@ -120,32 +139,21 @@ export const trainerClasses: SimpleTranslationEntries = { "workers": "工人組合", "youngster": "短褲小子", "rocket_grunts": "火箭队手下們", - "rocket_admin": "Rocket Admin", - "rocket_admin_female": "Rocket Admin", "magma_grunt": "熔岩队手下", "magma_grunt_female": "熔岩队手下", "magma_grunts": "熔岩队手下們", - "magma_admin": "Magma Admin", - "magma_admin_female": "Magma Admin", "aqua_grunt": "海洋队手下", "aqua_grunt_female": "海洋队手下", "aqua_grunts": "海洋队手下們", - "aqua_admin": "Aqua Admin", - "aqua_admin_female": "Aqua Admin", "galactic_grunt": "银河队手下", "galactic_grunt_female": "银河队手下", "galactic_grunts": "银河队手下們", - "galactic_admin": "Galactic Admin", - "galactic_admin_female": "Galactic Admin", "plasma_grunt": "等离子队手下", "plasma_grunt_female": "等离子队手下", "plasma_grunts": "等离子队手下們", - "plasma_sage": "Plasma Sage", "flare_grunt": "闪焰队手下", "flare_grunt_female": "闪焰队手下", "flare_grunts": "闪焰队手下們", - "flare_admin": "Flare Admin", - "flare_admin_female": "Flare Admin", } as const; // Names of special trainers like gym leaders, elite four, and the champion @@ -331,6 +339,29 @@ export const trainerNames: SimpleTranslationEntries = { "rival": "芬恩", "rival_female": "艾薇", + // Evil Team Admins + "archer": "Archer", + "ariana": "Ariana", + "proton": "Proton", + "petrel": "Petrel", + "tabitha": "Tabitha", + "courtney": "Courtney", + "shelly": "Shelly", + "matt": "Matt", + "mars": "Mars", + "jupiter": "Jupiter", + "saturn": "Saturn", + "zinzolin": "Zinzolin", + "rood": "Rood", + "xerosic": "Xerosic", + "bryony": "Bryony", + + "maxie": "Maxie", + "archie": "Archie", + "cyrus": "Cyrus", + "ghetsis": "Ghetsis", + "lysandre": "Lysandre", + // Double Names "blue_red_double": "青綠 & 赤紅", "red_blue_double": "赤紅 & 青綠", diff --git a/src/messages.ts b/src/messages.ts index 5dd6b6aa2c2..d36fec1fee4 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -7,7 +7,10 @@ import i18next from "i18next"; * @param pokemon {@linkcode Pokemon} name and battle context will be retrieved from this instance * @returns {string} ex: "Wild Gengar", "Ectoplasma sauvage" */ -export function getPokemonNameWithAffix(pokemon: Pokemon, useIllusion: boolean = true): string { +export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusion: boolean = true): string { + if (!pokemon) { + return "Missigno"; + } // TODO: little easter-egg, lol switch (pokemon.scene.currentBattle.battleSpec) { case BattleSpec.DEFAULT: return !pokemon.isPlayer() diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 235b48067b1..07a9b74b9ff 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -51,13 +51,13 @@ export class ModifierType { public group: string; public soundName: string; public tier: ModifierTier; - protected newModifierFunc: NewModifierFunc; + protected newModifierFunc: NewModifierFunc | null; - constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, group?: string, soundName?: string) { - this.localeKey = localeKey; - this.iconImage = iconImage; - this.group = group || ""; - this.soundName = soundName || "restore"; + constructor(localeKey: string | null, iconImage: string | null, newModifierFunc: NewModifierFunc | null, group?: string, soundName?: string) { + this.localeKey = localeKey!; // TODO: is this bang correct? + this.iconImage = iconImage!; // TODO: is this bang correct? + this.group = group!; // TODO: is this bang correct? + this.soundName = soundName ?? "restore"; this.newModifierFunc = newModifierFunc; } @@ -73,7 +73,7 @@ export class ModifierType { this.tier = tier; } - getOrInferTier(poolType: ModifierPoolType = ModifierPoolType.PLAYER): ModifierTier { + getOrInferTier(poolType: ModifierPoolType = ModifierPoolType.PLAYER): ModifierTier | null { if (this.tier) { return this.tier; } @@ -111,16 +111,16 @@ export class ModifierType { } withIdFromFunc(func: ModifierTypeFunc): ModifierType { - this.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === func); + this.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === func)!; // TODO: is this bang correct? return this; } - newModifier(...args: any[]): Modifier { - return this.newModifierFunc(this, args); + newModifier(...args: any[]): Modifier | null { + return this.newModifierFunc && this.newModifierFunc(this, args); } } -type ModifierTypeGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => ModifierType; +type ModifierTypeGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => ModifierType | null; export class ModifierTypeGenerator extends ModifierType { private genTypeFunc: ModifierTypeGeneratorFunc; @@ -197,7 +197,7 @@ class AddVoucherModifierType extends ModifierType { } export class PokemonModifierType extends ModifierType { - public selectFilter: PokemonSelectFilter; + public selectFilter: PokemonSelectFilter | undefined; constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, group?: string, soundName?: string) { super(localeKey, iconImage, newModifierFunc, group, soundName); @@ -298,7 +298,7 @@ export class PokemonStatusHealModifierType extends PokemonModifierType { } export abstract class PokemonMoveModifierType extends PokemonModifierType { - public moveSelectFilter: PokemonMoveSelectFilter; + public moveSelectFilter: PokemonMoveSelectFilter | undefined; constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, moveSelectFilter?: PokemonMoveSelectFilter, group?: string) { super(localeKey, iconImage, newModifierFunc, selectFilter, group); @@ -338,7 +338,7 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType { constructor(localeKey: string, iconImage: string, restorePoints: integer) { super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints), (pokemon: PlayerPokemon) => { - if (!pokemon.getMoveset().filter(m => m.ppUsed).length) { + if (!pokemon.getMoveset().filter(m => m?.ppUsed).length) { return PartyUiHandler.NoEffectMessage; } return null; @@ -518,7 +518,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i public boostPercent: integer; constructor(moveType: Type, boostPercent: integer) { - super("", `${getAttackTypeBoosterItemName(moveType).replace(/[ \-]/g, "_").toLowerCase()}`, + super("", `${getAttackTypeBoosterItemName(moveType)?.replace(/[ \-]/g, "_").toLowerCase()}`, (_type, args) => new Modifiers.AttackTypeBoosterModifier(this, (args[0] as Pokemon).id, moveType, boostPercent)); this.moveType = moveType; @@ -526,7 +526,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i } get name(): string { - return i18next.t(`modifierType:AttackTypeBoosterItem.${getAttackTypeBoosterItemName(this.moveType).replace(/[ \-]/g, "_").toLowerCase()}`); + return i18next.t(`modifierType:AttackTypeBoosterItem.${getAttackTypeBoosterItemName(this.moveType)?.replace(/[ \-]/g, "_").toLowerCase()}`); } getDescription(scene: BattleScene): string { @@ -638,7 +638,7 @@ class AllPokemonFullHpRestoreModifierType extends ModifierType { constructor(localeKey: string, iconImage: string, descriptionKey?: string, newModifierFunc?: NewModifierFunc) { super(localeKey, iconImage, newModifierFunc || ((_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 0, 100, false))); - this.descriptionKey = descriptionKey; + this.descriptionKey = descriptionKey!; // TODO: is this bang correct? } getDescription(scene: BattleScene): string { @@ -773,7 +773,7 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge if (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && pokemonEvolutions[pokemon.species.speciesId].filter(e => e.item === this.evolutionItem && (!e.condition || e.condition.predicate(pokemon))).length && (pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX)) { return null; - } else if (pokemon.isFusion() && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.item === this.evolutionItem + } else if (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.item === this.evolutionItem && (!e.condition || e.condition.predicate(pokemon))).length && (pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX)) { return null; } @@ -859,7 +859,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { return new AttackTypeBoosterModifierType(pregenArgs[0] as Type, 20); } - const attackMoveTypes = party.map(p => p.getMoveset().map(m => m.getMove()).filter(m => m instanceof AttackMove).map(m => m.type)).flat(); + const attackMoveTypes = party.map(p => p.getMoveset().map(m => m?.getMove()).filter(m => m instanceof AttackMove).map(m => m.type)).flat(); if (!attackMoveTypes.length) { return null; } @@ -868,8 +868,8 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { let totalWeight = 0; for (const t of attackMoveTypes) { if (attackMoveTypeWeights.has(t)) { - if (attackMoveTypeWeights.get(t) < 3) { - attackMoveTypeWeights.set(t, attackMoveTypeWeights.get(t) + 1); + if (attackMoveTypeWeights.get(t)! < 3) { // attackMoveTypeWeights.has(t) was checked before + attackMoveTypeWeights.set(t, attackMoveTypeWeights.get(t)! + 1); } else { continue; } @@ -889,7 +889,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { let weight = 0; for (const t of attackMoveTypeWeights.keys()) { - const typeWeight = attackMoveTypeWeights.get(t); + const typeWeight = attackMoveTypeWeights.get(t)!; // guranteed to be defined if (randInt <= weight + typeWeight) { type = t; break; @@ -897,7 +897,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { weight += typeWeight; } - return new AttackTypeBoosterModifierType(type, 20); + return new AttackTypeBoosterModifierType(type!, 20); }); } } @@ -930,7 +930,7 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator { for (const p of party) { const speciesId = p.getSpeciesForm(true).speciesId; const fusionSpeciesId = p.isFusion() ? p.getFusionSpeciesForm(true).speciesId : null; - const hasFling = p.getMoveset(true).some(m => m.moveId === Moves.FLING); + const hasFling = p.getMoveset(true).some(m => m?.moveId === Moves.FLING); for (const i in values) { const checkedSpecies = values[i].species; @@ -980,7 +980,7 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator { class TmModifierTypeGenerator extends ModifierTypeGenerator { constructor(tier: ModifierTier) { super((party: Pokemon[]) => { - const partyMemberCompatibleTms = party.map(p => (p as PlayerPokemon).compatibleTms.filter(tm => !p.moveset.find(m => m.moveId === tm))); + const partyMemberCompatibleTms = party.map(p => (p as PlayerPokemon).compatibleTms.filter(tm => !p.moveset.find(m => m?.moveId === tm))); const tierUniqueCompatibleTms = partyMemberCompatibleTms.flat().filter(tm => tmPoolTiers[tm] === tier).filter(tm => !allMoves[tm].name.endsWith(" (N)")).filter((tm, i, array) => array.indexOf(tm) === i); if (!tierUniqueCompatibleTms.length) { return null; @@ -1003,17 +1003,17 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { const evolutions = pokemonEvolutions[p.species.speciesId]; return evolutions.filter(e => e.item !== EvolutionItem.NONE && (e.evoFormKey === null || (e.preFormKey || "") === p.getFormKey()) && (!e.condition || e.condition.predicate(p))); }).flat(), - party.filter(p => p.isFusion() && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId)).map(p => { - const evolutions = pokemonEvolutions[p.fusionSpecies.speciesId]; + party.filter(p => p.isFusion() && p.fusionSpecies && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId)).map(p => { + const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId]; return evolutions.filter(e => e.item !== EvolutionItem.NONE && (e.evoFormKey === null || (e.preFormKey || "") === p.getFusionFormKey()) && (!e.condition || e.condition.predicate(p))); }).flat() - ].flat().flatMap(e => e.item).filter(i => (i > 50) === rare); + ].flat().flatMap(e => e.item).filter(i => (!!i && i > 50) === rare); if (!evolutionItemPool.length) { return null; } - return new EvolutionItemModifierType(evolutionItemPool[Utils.randSeedInt(evolutionItemPool.length)]); + return new EvolutionItemModifierType(evolutionItemPool[Utils.randSeedInt(evolutionItemPool.length)]!); // TODO: is the bang correct? }); } } @@ -1156,7 +1156,7 @@ class WeightedModifierType { constructor(modifierTypeFunc: ModifierTypeFunc, weight: integer | WeightedModifierTypeWeightFunc, maxWeight?: integer) { this.modifierType = modifierTypeFunc(); - this.modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc); + this.modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc)!; // TODO: is this bang correct? this.weight = weight; this.maxWeight = maxWeight || (!(weight instanceof Function) ? weight : 0); } @@ -1410,7 +1410,7 @@ export const modifierTypes = { VOUCHER_PLUS: () => new AddVoucherModifierType(VoucherType.PLUS, 1), VOUCHER_PREMIUM: () => new AddVoucherModifierType(VoucherType.PREMIUM, 1), - GOLDEN_POKEBALL: () => new ModifierType("modifierType:ModifierType.GOLDEN_POKEBALL", "pb_gold", (type, _args) => new Modifiers.ExtraModifierModifier(type), null, "pb_bounce_1"), + GOLDEN_POKEBALL: () => new ModifierType("modifierType:ModifierType.GOLDEN_POKEBALL", "pb_gold", (type, _args) => new Modifiers.ExtraModifierModifier(type), undefined, "pb_bounce_1"), ENEMY_DAMAGE_BOOSTER: () => new ModifierType("modifierType:ModifierType.ENEMY_DAMAGE_BOOSTER", "wl_item_drop", (type, _args) => new Modifiers.EnemyDamageBoosterModifier(type, 5)), ENEMY_DAMAGE_REDUCTION: () => new ModifierType("modifierType:ModifierType.ENEMY_DAMAGE_REDUCTION", "wl_guard_spec", (type, _args) => new Modifiers.EnemyDamageReducerModifier(type, 2.5)), @@ -1451,11 +1451,11 @@ const modifierPool: ModifierPool = { return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.ETHER, (party: Pokemon[]) => { - const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount * 3; }, 9), new WeightedModifierType(modifierTypes.MAX_ETHER, (party: Pokemon[]) => { - const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.LURE, 2), @@ -1471,7 +1471,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => { const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => { if (i instanceof Modifiers.TurnStatusEffectModifier) { - return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status.effect; + return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status?.effect; } return false; })).length, 3); @@ -1499,7 +1499,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.FULL_RESTORE, (party: Pokemon[]) => { const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => { if (i instanceof Modifiers.TurnStatusEffectModifier) { - return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status.effect; + return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status?.effect; } return false; })).length, 3); @@ -1507,11 +1507,11 @@ const modifierPool: ModifierPool = { return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.ELIXIR, (party: Pokemon[]) => { - const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount * 3; }, 9), new WeightedModifierType(modifierTypes.MAX_ELIXIR, (party: Pokemon[]) => { - const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.DIRE_HIT, 4), @@ -1544,7 +1544,12 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.MINT, 4), new WeightedModifierType(modifierTypes.RARE_EVOLUTION_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * 4, 32), 32), new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), - //new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => party.some(p => ((p.getSpeciesForm(true).speciesId in pokemonEvolutions) || (p.isFusion() && (p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))) && !p.getHeldItems().some(i => i instanceof Modifiers.EvolutionStatBoosterModifier)) ? 10 : 0), + new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { + if (party[0].scene.gameData.unlocks[Unlockables.EVIOLITE]) { + return party.some(p => ((p.getSpeciesForm(true).speciesId in pokemonEvolutions) || (p.isFusion() && (p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))) && !p.getHeldItems().some(i => i instanceof Modifiers.EvolutionStatBoosterModifier)) ? 10 : 0; + } + return 0; + }), new WeightedModifierType(modifierTypes.SPECIES_STAT_BOOSTER, 12), new WeightedModifierType(modifierTypes.LEEK, (party: Pokemon[]) => { const checkedSpecies = [ Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD ]; @@ -1555,21 +1560,21 @@ const modifierPool: ModifierPool = { const checkedAbilities = [Abilities.QUICK_FEET, Abilities.GUTS, Abilities.MARVEL_SCALE, Abilities.TOXIC_BOOST, Abilities.POISON_HEAL, Abilities.MAGIC_GUARD]; const checkedMoves = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT]; // If a party member doesn't already have one of these two orbs and has one of the above moves or abilities, the orb can appear - return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => checkedMoves.includes(m.moveId)))) ? 10 : 0; + return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && checkedMoves.includes(m.moveId)))) ? 10 : 0; }, 10), new WeightedModifierType(modifierTypes.FLAME_ORB, (party: Pokemon[]) => { const checkedAbilities = [Abilities.QUICK_FEET, Abilities.GUTS, Abilities.MARVEL_SCALE, Abilities.FLARE_BOOST, Abilities.MAGIC_GUARD]; const checkedMoves = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT]; // If a party member doesn't already have one of these two orbs and has one of the above moves or abilities, the orb can appear - return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => checkedMoves.includes(m.moveId)))) ? 10 : 0; + return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && checkedMoves.includes(m.moveId)))) ? 10 : 0; }, 10), new WeightedModifierType(modifierTypes.WHITE_HERB, (party: Pokemon[]) => { const checkedAbilities = [Abilities.WEAK_ARMOR, Abilities.CONTRARY, Abilities.MOODY, Abilities.ANGER_SHELL, Abilities.COMPETITIVE, Abilities.DEFIANT]; const weightMultiplier = party.filter( p => !p.getHeldItems().some(i => i instanceof Modifiers.PokemonResetNegativeStatStageModifier && i.stackCount >= i.getMaxHeldItemCount(p)) && - (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => selfStatLowerMoves.includes(m.moveId)))).length; + (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && selfStatLowerMoves.includes(m.moveId)))).length; // If a party member has one of the above moves or abilities and doesn't have max herbs, the herb will appear more frequently - return 2 * (weightMultiplier ? 2 : 1) + (weightMultiplier ? weightMultiplier : 0); + return 0 * (weightMultiplier ? 2 : 1) + (weightMultiplier ? weightMultiplier * 0 : 0); }, 10), new WeightedModifierType(modifierTypes.REVIVER_SEED, 4), new WeightedModifierType(modifierTypes.CANDY_JAR, 5), @@ -1637,7 +1642,7 @@ const wildModifierPool: ModifierPool = { }), [ModifierTier.ULTRA]: [ new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10), - new WeightedModifierType(modifierTypes.WHITE_HERB, 1) + new WeightedModifierType(modifierTypes.WHITE_HERB, 0) ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), @@ -1667,7 +1672,7 @@ const trainerModifierPool: ModifierPool = { }), [ModifierTier.ULTRA]: [ new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10), - new WeightedModifierType(modifierTypes.WHITE_HERB, 1), + new WeightedModifierType(modifierTypes.WHITE_HERB, 0), ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), @@ -1722,10 +1727,10 @@ const enemyBuffModifierPool: ModifierPool = { ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), - [ModifierTier.ROGUE]: [ ].map(m => { + [ModifierTier.ROGUE]: [ ].map((m: WeightedModifierType) => { m.setTier(ModifierTier.ROGUE); return m; }), - [ModifierTier.MASTER]: [ ].map(m => { + [ModifierTier.MASTER]: [ ].map((m: WeightedModifierType) => { m.setTier(ModifierTier.MASTER); return m; }) }; @@ -1770,7 +1775,7 @@ const dailyStarterModifierPool: ModifierPool = { export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierType { const modifierType = modifierTypeFunc(); if (!modifierType.id) { - modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc); + modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc)!; // TODO: is this bang correct? } return modifierType; } @@ -1809,7 +1814,7 @@ export function getModifierPoolForType(poolType: ModifierPoolType): ModifierPool return pool; } -const tierWeights = [ 769 / 1024, 192 / 1024, 48 / 1024, 12 / 1024, 1 / 1024 ]; +const tierWeights = [ 768 / 1024, 195 / 1024, 48 / 1024, 12 / 1024, 1 / 1024 ]; export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount: integer = 0) { const pool = getModifierPoolForType(poolType); @@ -1893,12 +1898,14 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo const options: ModifierTypeOption[] = []; const retryCount = Math.min(count * 5, 50); new Array(count).fill(0).map((_, i) => { - let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, modifierTiers?.length > i ? modifierTiers[i] : undefined); + let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, modifierTiers && modifierTiers.length > i ? modifierTiers[i] : undefined); let r = 0; - while (options.length && ++r < retryCount && options.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length) { - candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate.type.tier, candidate.upgradeCount); + while (options.length && ++r < retryCount && options.filter(o => o.type?.name === candidate?.type?.name || o.type?.group === candidate?.type?.group).length) { + candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate?.type?.tier, candidate?.upgradeCount); + } + if (candidate) { + options.push(candidate); } - options.push(candidate); }); // OVERRIDE IF NECESSARY @@ -1950,21 +1957,21 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: Modifiers.PersistentModifier[], scene: BattleScene): Modifiers.EnemyPersistentModifier { const tierStackCount = tier === ModifierTier.ULTRA ? 5 : tier === ModifierTier.GREAT ? 3 : 1; const retryCount = 50; - let candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier); + let candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier); let r = 0; - let matchingModifier: Modifiers.PersistentModifier; - while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate.type.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) { - candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier); + let matchingModifier: Modifiers.PersistentModifier | undefined; + while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate?.type?.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) { + candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier); } - const modifier = candidate.type.newModifier() as Modifiers.EnemyPersistentModifier; + const modifier = candidate?.type?.newModifier() as Modifiers.EnemyPersistentModifier; modifier.stackCount = tierStackCount; return modifier; } export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, upgradeChance: integer = 0): PokemonHeldItemModifierType[] { - const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !Utils.randSeedInt(upgradeChance) ? 1 : 0).type as PokemonHeldItemModifierType); + const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !Utils.randSeedInt(upgradeChance) ? 1 : 0)?.type as PokemonHeldItemModifierType); if (!(waveIndex % 1000)) { ret.push(getModifierType(modifierTypes.MINI_BLACK_HOLE) as PokemonHeldItemModifierType); } @@ -1977,7 +1984,7 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.P for (let m = 0; m < 3; m++) { const tierValue = Utils.randSeedInt(64); const tier = tierValue > 25 ? ModifierTier.COMMON : tierValue > 12 ? ModifierTier.GREAT : tierValue > 4 ? ModifierTier.ULTRA : tierValue ? ModifierTier.ROGUE : ModifierTier.MASTER; - const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier).type.newModifier(p) as Modifiers.PokemonHeldItemModifier; + const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier)?.type?.newModifier(p) as Modifiers.PokemonHeldItemModifier; ret.push(modifier); } } @@ -1985,7 +1992,7 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.P return ret; } -function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer, retryCount: integer = 0): ModifierTypeOption { +function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer, retryCount: integer = 0): ModifierTypeOption | null { const player = !poolType; const pool = getModifierPoolForType(poolType); let thresholds: object; @@ -2023,10 +2030,6 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, } while (upgraded); } tier = tierValue > 255 ? ModifierTier.COMMON : tierValue > 60 ? ModifierTier.GREAT : tierValue > 12 ? ModifierTier.ULTRA : tierValue ? ModifierTier.ROGUE : ModifierTier.MASTER; - // Does this actually do anything? - if (!upgradeCount) { - upgradeCount = Math.min(upgradeCount, ModifierTier.MASTER - tier); - } tier += upgradeCount; while (tier && (!modifierPool.hasOwnProperty(tier) || !modifierPool[tier].length)) { tier--; @@ -2056,7 +2059,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, const tierThresholds = Object.keys(thresholds[tier]); const totalWeight = parseInt(tierThresholds[tierThresholds.length - 1]); const value = Utils.randSeedInt(totalWeight); - let index: integer; + let index: integer | undefined; for (const t of tierThresholds) { const threshold = parseInt(t); if (value < threshold) { @@ -2072,7 +2075,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, if (player) { console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]); } - let modifierType: ModifierType = (pool[tier][index]).modifierType; + let modifierType: ModifierType | null = (pool[tier][index]).modifierType; if (modifierType instanceof ModifierTypeGenerator) { modifierType = (modifierType as ModifierTypeGenerator).generateType(party); if (modifierType === null) { @@ -2085,7 +2088,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, console.log(modifierType, !player ? "(enemy)" : ""); - return new ModifierTypeOption(modifierType as ModifierType, upgradeCount); + return new ModifierTypeOption(modifierType as ModifierType, upgradeCount!); // TODO: is this bang correct? } export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType { @@ -2097,7 +2100,7 @@ export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType } export class ModifierTypeOption { - public type: ModifierType; + public type: ModifierType | null; public upgradeCount: integer; public cost: integer; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 91041dc7564..c6871353a7d 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -33,16 +33,16 @@ export type ModifierPredicate = (modifier: Modifier) => boolean; const iconOverflowIndex = 24; -export const modifierSortFunc = (a: Modifier, b: Modifier) => { +export const modifierSortFunc = (a: Modifier, b: Modifier): number => { const itemNameMatch = a.type.name.localeCompare(b.type.name); const typeNameMatch = a.constructor.name.localeCompare(b.constructor.name); - const aId = a instanceof PokemonHeldItemModifier ? a.pokemonId : 4294967295; - const bId = b instanceof PokemonHeldItemModifier ? b.pokemonId : 4294967295; + const aId = a instanceof PokemonHeldItemModifier && a.pokemonId ? a.pokemonId : 4294967295; + const bId = b instanceof PokemonHeldItemModifier && b.pokemonId ? b.pokemonId : 4294967295; //First sort by pokemonID if (aId < bId) { return 1; - } else if (aId>bId) { + } else if (aId > bId) { return -1; } else if (aId === bId) { //Then sort by item type @@ -52,6 +52,8 @@ export const modifierSortFunc = (a: Modifier, b: Modifier) => { } else { return typeNameMatch; } + } else { + return 0; } }; @@ -150,7 +152,7 @@ export abstract class PersistentModifier extends Modifier { public stackCount: integer; public virtualStackCount: integer; - constructor(type: ModifierType, stackCount: integer) { + constructor(type: ModifierType, stackCount?: integer) { super(type); this.stackCount = stackCount === undefined ? 1 : stackCount; this.virtualStackCount = 0; @@ -221,7 +223,7 @@ export abstract class PersistentModifier extends Modifier { return container; } - getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.BitmapText { + getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.BitmapText | null { if (this.getMaxStackCount(scene) === 1 || (virtual && !this.virtualStackCount)) { return null; } @@ -295,7 +297,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier { constructor(type: ModifierTypes.ModifierType, battlesLeft?: integer, stackCount?: integer) { super(type, stackCount); - this.battlesLeft = battlesLeft; + this.battlesLeft = battlesLeft!; // TODO: is this bang correct? } lapse(args: any[]): boolean { @@ -306,7 +308,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier { const container = super.getIcon(scene); const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: "66px", color: "#f89890" }); - battleCountText.setShadow(0, 0, null); + battleCountText.setShadow(0, 0); battleCountText.setStroke("#984038", 16); battleCountText.setOrigin(1, 0); container.add(battleCountText); @@ -472,7 +474,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { public pokemonId: integer; readonly isTransferrable: boolean = true; - constructor(type: ModifierType, pokemonId: integer, stackCount: integer) { + constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { super(type, stackCount); this.pokemonId = pokemonId; @@ -489,11 +491,11 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { } shouldApply(args: any[]): boolean { - return super.shouldApply(args) && args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId); + return super.shouldApply(args) && args.length !== 0 && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId); } isIconVisible(scene: BattleScene): boolean { - return this.getPokemon(scene).isOnField(); + return !!(this.getPokemon(scene)?.isOnField()); } getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { @@ -501,9 +503,10 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { if (!forSummary) { const pokemon = this.getPokemon(scene); - const pokemonIcon = scene.addPokemonIcon(pokemon, -2, 10, 0, 0.5); - - container.add(pokemonIcon); + if (pokemon) { + const pokemonIcon = scene.addPokemonIcon(pokemon, -2, 10, 0, 0.5); + container.add(pokemonIcon); + } const item = scene.add.sprite(16, this.virtualStackCount ? 8 : 16, "items"); item.setScale(0.5); @@ -527,8 +530,8 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { return container; } - getPokemon(scene: BattleScene): Pokemon { - return scene.getPokemonById(this.pokemonId); + getPokemon(scene: BattleScene): Pokemon | undefined { + return this.pokemonId ? scene.getPokemonById(this.pokemonId) ?? undefined : undefined; } getScoreMultiplier(): number { @@ -562,7 +565,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { return this.getMaxHeldItemCount(pokemon); } - abstract getMaxHeldItemCount(pokemon: Pokemon): integer; + abstract getMaxHeldItemCount(pokemon?: Pokemon): integer; } export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModifier { @@ -572,7 +575,7 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi constructor(type: ModifierTypes.ModifierType, pokemonId: integer, battlesLeft?: integer, stackCount?: integer) { super(type, pokemonId, stackCount); - this.battlesLeft = battlesLeft; + this.battlesLeft = battlesLeft!; // TODO: is this bang correct? } lapse(args: any[]): boolean { @@ -582,9 +585,9 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { const container = super.getIcon(scene, forSummary); - if (this.getPokemon(scene).isPlayer()) { + if (this.getPokemon(scene)?.isPlayer()) { const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: "66px", color: "#f89890" }); - battleCountText.setShadow(0, 0, null); + battleCountText.setShadow(0, 0); battleCountText.setStroke("#984038", 16); battleCountText.setOrigin(1, 0); container.add(battleCountText); @@ -1442,7 +1445,7 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier { } shouldApply(args: any[]): boolean { - return args.length && args[0] instanceof PlayerPokemon && (this.pokemonId === -1 || (args[0] as PlayerPokemon).id === this.pokemonId); + return args.length !== 0 && args[0] instanceof PlayerPokemon && (this.pokemonId === -1 || (args[0] as PlayerPokemon).id === this.pokemonId); } getPokemon(scene: BattleScene) { @@ -1519,7 +1522,7 @@ export class PokemonPpRestoreModifier extends ConsumablePokemonMoveModifier { apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; - const move = pokemon.getMoveset()[this.moveIndex]; + const move = pokemon.getMoveset()[this.moveIndex]!; //TODO: is the bang correct? move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; return true; @@ -1538,7 +1541,7 @@ export class PokemonAllMovePpRestoreModifier extends ConsumablePokemonModifier { apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; for (const move of pokemon.getMoveset()) { - move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; + move!.ppUsed = this.restorePoints > -1 ? Math.max(move!.ppUsed - this.restorePoints, 0) : 0; // TODO: are those bangs correct? } return true; @@ -1556,7 +1559,7 @@ export class PokemonPpUpModifier extends ConsumablePokemonMoveModifier { apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; - const move = pokemon.getMoveset()[this.moveIndex]; + const move = pokemon.getMoveset()[this.moveIndex]!; // TODO: is the bang correct? move.ppUp = Math.min(move.ppUp + this.upPoints, 3); return true; @@ -1658,7 +1661,7 @@ export class EvolutionItemModifier extends ConsumablePokemonModifier { : null; if (!matchingEvolution && pokemon.isFusion()) { - matchingEvolution = pokemonEvolutions[pokemon.fusionSpecies.speciesId].find(e => e.item === (this.type as ModifierTypes.EvolutionItemModifierType).evolutionItem + matchingEvolution = pokemonEvolutions[pokemon.fusionSpecies!.speciesId].find(e => e.item === (this.type as ModifierTypes.EvolutionItemModifierType).evolutionItem // TODO: is the bang correct? && (e.evoFormKey === null || (e.preFormKey || "") === pokemon.getFusionFormKey()) && (!e.condition || e.condition.predicate(pokemon))); if (matchingEvolution) { @@ -1815,7 +1818,7 @@ export class PokemonExpBoosterModifier extends PokemonHeldItemModifier { } apply(args: any[]): boolean { - (args[1] as Utils.NumberHolder).value += (this.getStackCount() * this.boostMultiplier); + (args[1] as Utils.NumberHolder).value = Math.floor((args[1] as Utils.NumberHolder).value * (1 + (this.getStackCount() * this.boostMultiplier))); return true; } @@ -2135,7 +2138,7 @@ export class MoneyInterestModifier extends PersistentModifier { const userLocale = navigator.language || "en-US"; const formattedMoneyAmount = interestAmount.toLocaleString(userLocale); const message = i18next.t("modifier:moneyInterestApply", { moneyAmount: formattedMoneyAmount, typeName: this.type.name }); - scene.queueMessage(message, null, true); + scene.queueMessage(message, undefined, true); return true; } @@ -2290,7 +2293,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { const transferredModifierTypes: ModifierTypes.ModifierType[] = []; const itemModifiers = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === targetPokemon.id && m.isTransferrable, targetPokemon.isPlayer()) as PokemonHeldItemModifier[]; - let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier, highestTier), 0); + let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is this bang correct? let tierItemModifiers = itemModifiers.filter(m => m.type.getOrInferTier(poolType) === highestItemTier); const heldItemTransferPromises: Promise[] = []; @@ -2782,8 +2785,8 @@ export function overrideHeldItems(scene: BattleScene, pokemon: Pokemon, player: const modifierType: ModifierType = modifierTypes[itemName](); // we retrieve the item in the list let itemModifier: PokemonHeldItemModifier; if (modifierType instanceof ModifierTypes.ModifierTypeGenerator) { - const pregenArgs = "type" in item ? [item.type] : null; - itemModifier = modifierType.generateType(null, pregenArgs).withIdFromFunc(modifierTypes[itemName]).newModifier(pokemon) as PokemonHeldItemModifier; + const pregenArgs = "type" in item ? [item.type] : undefined; + itemModifier = modifierType.generateType([], pregenArgs)?.withIdFromFunc(modifierTypes[itemName]).newModifier(pokemon) as PokemonHeldItemModifier; } else { itemModifier = modifierType.withIdFromFunc(modifierTypes[itemName]).newModifier(pokemon) as PokemonHeldItemModifier; } diff --git a/src/overrides.ts b/src/overrides.ts index 9dd394354aa..88db105475c 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -48,9 +48,9 @@ class DefaultOverrides { readonly BATTLE_TYPE_OVERRIDE: "double" | "single" | null = null; readonly STARTING_WAVE_OVERRIDE: integer = 0; readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN; - readonly ARENA_TINT_OVERRIDE: TimeOfDay = null; + readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null; /** Multiplies XP gained by this value including 0. Set to null to ignore the override */ - readonly XP_MULTIPLIER_OVERRIDE: number = null; + readonly XP_MULTIPLIER_OVERRIDE: number | null = null; /** default 1000 */ readonly STARTING_MONEY_OVERRIDE: integer = 0; /** Sets all shop item prices to 0 */ @@ -96,7 +96,7 @@ class DefaultOverrides { readonly ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; - readonly GENDER_OVERRIDE: Gender = null; + readonly GENDER_OVERRIDE: Gender | null = null; readonly MOVESET_OVERRIDE: Array = []; readonly SHINY_OVERRIDE: boolean = false; readonly VARIANT_OVERRIDE: Variant = 0; @@ -109,7 +109,7 @@ class DefaultOverrides { readonly OPP_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly OPP_PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly OPP_STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; - readonly OPP_GENDER_OVERRIDE: Gender = null; + readonly OPP_GENDER_OVERRIDE: Gender | null = null; readonly OPP_MOVESET_OVERRIDE: Array = []; readonly OPP_SHINY_OVERRIDE: boolean = false; readonly OPP_VARIANT_OVERRIDE: Variant = 0; @@ -119,9 +119,9 @@ class DefaultOverrides { // EGG OVERRIDES // ------------- readonly EGG_IMMEDIATE_HATCH_OVERRIDE: boolean = false; - readonly EGG_TIER_OVERRIDE: EggTier = null; + readonly EGG_TIER_OVERRIDE: EggTier | null = null; readonly EGG_SHINY_OVERRIDE: boolean = false; - readonly EGG_VARIANT_OVERRIDE: VariantTier = null; + readonly EGG_VARIANT_OVERRIDE: VariantTier | null = null; readonly EGG_FREE_GACHA_PULLS_OVERRIDE: boolean = false; readonly EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; diff --git a/src/phases.ts b/src/phases.ts index 6cc1db399c0..af4a4959c23 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1,7 +1,7 @@ import BattleScene, { bypassLogin } from "./battle-scene"; import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon"; import * as Utils from "./utils"; -import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, MoveTarget, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, PreMoveMessageAttr, HealStatusEffectAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, ForceSwitchOutAttr, VariableTargetAttr, IncrementMovePriorityAttr } from "./data/move"; +import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, MoveTarget, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, PreMoveMessageAttr, HealStatusEffectAttr, NoEffectAttr, BypassRedirectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, ForceSwitchOutAttr, VariableTargetAttr, IncrementMovePriorityAttr, MoveHeaderAttr } from "./data/move"; import { Mode } from "./ui/ui"; import { Command } from "./ui/command-ui-handler"; import { Stat } from "./data/pokemon-stat"; @@ -53,7 +53,7 @@ import { GameMode, GameModes, getGameMode } from "./game-mode"; import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./data/pokemon-species"; import i18next from "./plugins/i18n"; import Overrides from "#app/overrides"; -import { TextStyle, addTextObject } from "./ui/text"; +import { TextStyle, addTextObject, getTextColor } from "./ui/text"; import { Type } from "./data/type"; import { BerryUsedEvent, EncounterPhaseEvent, MoveUsedEvent, TurnEndEvent, TurnInitEvent } from "./events/battle-scene"; import { Abilities } from "#enums/abilities"; @@ -68,6 +68,7 @@ import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import { applyChallenges, ChallengeType } from "./data/challenge"; +import { pokemonEvolutions } from "./data/pokemon-evolutions"; const { t } = i18next; @@ -197,7 +198,7 @@ export class TitlePhase extends Phase { this.scene.playBgm("title", true); - this.scene.gameData.getSession(loggedInUser.lastSessionSlot).then(sessionData => { + this.scene.gameData.getSession(loggedInUser!.lastSessionSlot).then(sessionData => { // TODO: is this bang correct? if (sessionData) { this.lastSessionData = sessionData; const biomeKey = getBiomeKey(sessionData.arena.biome); @@ -213,11 +214,11 @@ export class TitlePhase extends Phase { showOptions(): void { const options: OptionSelectItem[] = []; - if (loggedInUser.lastSessionSlot > -1) { + if (loggedInUser && loggedInUser.lastSessionSlot > -1) { options.push({ - label: i18next.t("continue", null, { ns: "menu"}), + label: i18next.t("continue", {ns: "menu"}), handler: () => { - this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser.lastSessionSlot); + this.loadSaveSlot(this.lastSessionData || !loggedInUser ? -1 : loggedInUser.lastSessionSlot); return true; } }); @@ -321,9 +322,9 @@ export class TitlePhase extends Phase { } loadSaveSlot(slotId: integer): void { - this.scene.sessionSlotId = slotId > -1 ? slotId : loggedInUser.lastSessionSlot; + this.scene.sessionSlotId = slotId > -1 || !loggedInUser ? slotId : loggedInUser.lastSessionSlot; this.scene.ui.setMode(Mode.MESSAGE); - this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : null).then((success: boolean) => { + this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : undefined).then((success: boolean) => { if (success) { this.loaded = true; this.scene.ui.showText(i18next.t("menu:sessionSuccess"), null, () => this.end()); @@ -373,7 +374,8 @@ export class TitlePhase extends Phase { regenerateModifierPoolThresholds(party, ModifierPoolType.DAILY_STARTER); const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier()) .concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier())) - .concat(getDailyRunStarterModifiers(party)); + .concat(getDailyRunStarterModifiers(party)) + .filter((m) => m !== null); for (const m of modifiers) { this.scene.addModifier(m, true, false, false, true); @@ -395,7 +397,7 @@ export class TitlePhase extends Phase { // If Online, calls seed fetch from db to generate daily run. If Offline, generates a daily run based on current date. if (!Utils.isLocal) { fetchDailyRunSeed().then(seed => { - generateDaily(seed); + generateDaily(seed!); // TODO: is this bang correct? }).catch(err => { console.error("Failed to load daily run:\n", err); }); @@ -464,12 +466,12 @@ export class UnavailablePhase extends Phase { } export class ReloadSessionPhase extends Phase { - private systemDataStr: string; + private systemDataStr: string | null; constructor(scene: BattleScene, systemDataStr?: string) { super(scene); - this.systemDataStr = systemDataStr; + this.systemDataStr = systemDataStr!; // TODO: is this bang correct? } start(): void { @@ -602,9 +604,9 @@ export class SelectStarterPhase extends Phase { let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0)); if ( starter.species.speciesId in Overrides.STARTER_FORM_OVERRIDES && - starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]] + starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!] ) { - starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]; + starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!; } let starterGender = starter.species.malePercent !== null @@ -615,7 +617,7 @@ export class SelectStarterPhase extends Phase { } const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0); const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, starterIvs, starter.nature); - starterPokemon.tryPopulateMoveset(starter.moveset); + starter.moveset && starterPokemon.tryPopulateMoveset(starter.moveset); if (starter.passive) { starterPokemon.passive = true; } @@ -645,6 +647,10 @@ export class SelectStarterPhase extends Phase { this.scene.arena.init(); this.scene.sessionPlayTime = 0; this.scene.lastSavePlayTime = 0; + // Ensures Keldeo (or any future Pokemon that have this type of form change) starts in the correct form + this.scene.getParty().forEach((p: PlayerPokemon) => { + this.scene.triggerPokemonFormChange(p, SpeciesFormChangeMoveLearnedTrigger); + }); this.end(); }); } @@ -656,8 +662,8 @@ export class BattlePhase extends Phase { } showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { - const sprites = this.scene.currentBattle.trainer.getSprites(); - const tintSprites = this.scene.currentBattle.trainer.getTintSprites(); + const sprites = this.scene.currentBattle.trainer?.getSprites()!; // TODO: is this bang correct? + const tintSprites = this.scene.currentBattle.trainer?.getTintSprites()!; // TODO: is this bang correct? for (let i = 0; i < sprites.length; i++) { const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2; [sprites[i], tintSprites[i]].map(sprite => { @@ -737,11 +743,11 @@ export abstract class PokemonPhase extends FieldPhase { public player: boolean; public fieldIndex: integer; - constructor(scene: BattleScene, battlerIndex: BattlerIndex | integer) { + constructor(scene: BattleScene, battlerIndex?: BattlerIndex | integer) { super(scene); if (battlerIndex === undefined) { - battlerIndex = scene.getField().find(p => p?.isActive()).getBattlerIndex(); + battlerIndex = scene.getField().find(p => p?.isActive())!.getBattlerIndex(); // TODO: is the bang correct here? } this.battlerIndex = battlerIndex; @@ -749,11 +755,11 @@ export abstract class PokemonPhase extends FieldPhase { this.fieldIndex = battlerIndex % 2; } - getPokemon() { + getPokemon(): Pokemon { if (this.battlerIndex > BattlerIndex.ENEMY_2) { - return this.scene.getPokemonById(this.battlerIndex); + return this.scene.getPokemonById(this.battlerIndex)!; //TODO: is this bang correct? } - return this.scene.getField()[this.battlerIndex]; + return this.scene.getField()[this.battlerIndex]!; //TODO: is this bang correct? } } @@ -824,16 +830,16 @@ export class EncounterPhase extends BattlePhase { this.scene.unshiftPhase(new GameOverPhase(this.scene)); } - const loadEnemyAssets = []; + const loadEnemyAssets: Promise[] = []; const battle = this.scene.currentBattle; let totalBst = 0; - battle.enemyLevels.forEach((level, e) => { + battle.enemyLevels?.forEach((level, e) => { if (!this.loaded) { if (battle.battleType === BattleType.TRAINER) { - battle.enemyParty[e] = battle.trainer.genPartyMember(e); + battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here? } else { const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); @@ -881,7 +887,7 @@ export class EncounterPhase extends BattlePhase { } if (battle.battleType === BattleType.TRAINER) { - loadEnemyAssets.push(battle.trainer.loadAssets().then(() => battle.trainer.initSprite())); + loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct? } else { // This block only applies for double battles to init the boss segments (idk why it's split up like this) if (battle.enemyParty.filter(p => p.isBoss()).length > 1) { @@ -910,7 +916,7 @@ export class EncounterPhase extends BattlePhase { enemyPokemon.tint(0, 0.5); } else if (battle.battleType === BattleType.TRAINER) { enemyPokemon.setVisible(false); - this.scene.currentBattle.trainer.tint(0, 0.5); + this.scene.currentBattle.trainer?.tint(0, 0.5); } if (battle.double) { enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT); @@ -925,7 +931,8 @@ export class EncounterPhase extends BattlePhase { this.scene.ui.setMode(Mode.MESSAGE).then(() => { if (!this.loaded) { - this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || this.scene.lastSavePlayTime >= 300).then(success => { + //@ts-ignore + this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || this.scene.lastSavePlayTime >= 300).then(success => { // TODO: get rid of ts-ignore this.scene.disableMenu = false; if (!success) { return this.scene.reset(true); @@ -982,10 +989,10 @@ export class EncounterPhase extends BattlePhase { if (this.scene.currentBattle.battleType === BattleType.TRAINER) { if (this.scene.currentBattle.double) { - return i18next.t("battle:trainerAppearedDouble", { trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }); + return i18next.t("battle:trainerAppearedDouble", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); } else { - return i18next.t("battle:trainerAppeared", { trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }); + return i18next.t("battle:trainerAppeared", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); } } @@ -1014,8 +1021,8 @@ export class EncounterPhase extends BattlePhase { } } else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { const trainer = this.scene.currentBattle.trainer; - trainer.untint(100, "Sine.easeOut"); - trainer.playAnim(); + trainer?.untint(100, "Sine.easeOut"); + trainer?.playAnim(); const doSummon = () => { this.scene.currentBattle.started = true; @@ -1038,21 +1045,21 @@ export class EncounterPhase extends BattlePhase { } }; - const encounterMessages = this.scene.currentBattle.trainer.getEncounterMessages(); + const encounterMessages = this.scene.currentBattle.trainer?.getEncounterMessages(); if (!encounterMessages?.length) { doSummon(); } else { let message: string; this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); - + message = message!; // tell TS compiler it's defined now const showDialogueAndSummon = () => { - this.scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE, true), null, () => { + this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => { this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); }); }; - if (this.scene.currentBattle.trainer.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { - this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer.getKey(), getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); + if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { + this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer?.getKey()!, getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); // TODO: is this bang correct? } else { showDialogueAndSummon(); } @@ -1130,7 +1137,16 @@ export class EncounterPhase extends BattlePhase { case BattleSpec.FINAL_BOSS: const enemy = this.scene.getEnemyPokemon(); this.scene.ui.showText(this.getEncounterMessage(), null, () => { - this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].encounter, enemy.species.name, null, () => { + const count = 5643853 + this.scene.gameData.gameStats.classicSessionsPlayed; + //The two lines below check if English ordinals (1st, 2nd, 3rd, Xth) are used and determine which one to use. + //Otherwise, it defaults to an empty string. + //As of 08-07-24: Spanish and Italian default to the English translations + const ordinalUse = ["en", "es", "it"]; + const currentLanguage = i18next.resolvedLanguage ?? "en"; + const ordinalIndex = (ordinalUse.includes(currentLanguage)) ? ["st", "nd", "rd"][((count + 90) % 100 - 10) % 10 - 1] ?? "th" : ""; + const cycleCount = count.toLocaleString() + ordinalIndex; + const encounterDialogue = i18next.t(`${(this.scene.gameData.gender === PlayerGender.FEMALE) ? "PGF" : "PGM"}battleSpecDialogue:encounter`, {cycleCount: cycleCount}); + this.scene.ui.showDialogue(encounterDialogue, enemy?.species.name, null, () => { this.doEncounterCommon(false); }); }, 1500, true); @@ -1263,14 +1279,14 @@ export class SelectBiomePhase extends BattlePhase { } else if (this.scene.gameMode.hasRandomBiomes) { setNextBiome(this.generateNextBiome()); } else if (Array.isArray(biomeLinks[currentBiome])) { - let biomes: Biome[]; + let biomes: Biome[] = []; this.scene.executeWithSeedOffset(() => { biomes = (biomeLinks[currentBiome] as (Biome | [Biome, integer])[]) .filter(b => !Array.isArray(b) || !Utils.randSeedInt(b[1])) .map(b => !Array.isArray(b) ? b : b[0]); }, this.scene.currentBattle.waveIndex); if (biomes.length > 1 && this.scene.findModifier(m => m instanceof MapModifier)) { - let biomeChoices: Biome[]; + let biomeChoices: Biome[] = []; this.scene.executeWithSeedOffset(() => { biomeChoices = (!Array.isArray(biomeLinks[currentBiome]) ? [biomeLinks[currentBiome] as Biome] @@ -1440,7 +1456,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { }); this.scene.time.delayedCall(750, () => this.summon()); } else { - const trainerName = this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); + const trainerName = this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); const pokemonName = this.getPokemon().getNameToRender(); const message = i18next.t("battle:trainerSendOut", { trainerName, pokemonName }); @@ -1583,7 +1599,8 @@ export class SwitchSummonPhase extends SummonPhase { preSummon(): void { if (!this.player) { if (this.slotIndex === -1) { - this.slotIndex = this.scene.currentBattle.trainer.getNextSummonIndex(!this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); + //@ts-ignore + this.slotIndex = this.scene.currentBattle.trainer?.getNextSummonIndex(!this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); // TODO: what would be the default trainer-slot fallback? } if (this.slotIndex > -1) { this.showEnemyTrainer(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); @@ -1610,7 +1627,7 @@ export class SwitchSummonPhase extends SummonPhase { this.scene.ui.showText(this.player ? i18next.t("battle:playerComeBack", { pokemonName: getPokemonNameWithAffix(pokemon) }) : i18next.t("battle:trainerComeBack", { - trainerName: this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), + trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), pokemonName: getPokemonNameWithAffix(pokemon) }) ); @@ -1655,7 +1672,7 @@ export class SwitchSummonPhase extends SummonPhase { this.scene.ui.showText(this.player ? i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(switchedInPokemon) }) : i18next.t("battle:trainerGo", { - trainerName: this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), + trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), pokemonName: this.getPokemon().getNameToRender() }) ); @@ -1934,8 +1951,8 @@ export class CommandPhase extends FieldPhase { this.fieldIndex = FieldPosition.CENTER; } else { const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1]; - if (allyCommand.command === Command.BALL || allyCommand.command === Command.RUN) { - this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand.command, skip: true }; + if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) { + this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand?.command, skip: true }; } } } @@ -1949,8 +1966,8 @@ export class CommandPhase extends FieldPhase { const moveQueue = playerPokemon.getMoveQueue(); while (moveQueue.length && moveQueue[0] - && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m.moveId === moveQueue[0].move) - || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m.moveId === moveQueue[0].move)].isUsable(playerPokemon, moveQueue[0].ignorePP))) { + && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) + || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct? moveQueue.shift(); } @@ -1959,8 +1976,8 @@ export class CommandPhase extends FieldPhase { if (!queuedMove.move) { this.handleCommand(Command.FIGHT, -1, false); } else { - const moveIndex = playerPokemon.getMoveset().findIndex(m => m.moveId === queuedMove.move); - if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, queuedMove.ignorePP)) { + const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move); + if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct? this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 }); } else { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); @@ -1981,8 +1998,8 @@ export class CommandPhase extends FieldPhase { let useStruggle = false; if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean) || - (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) { - const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor].moveId : Moves.NONE : Moves.STRUGGLE; + (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) { + const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor]!.moveId : Moves.NONE : Moves.STRUGGLE; // TODO: is the bang correct? const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args }; const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2]; if (!moveId) { @@ -1993,16 +2010,16 @@ export class CommandPhase extends FieldPhase { this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); } if (moveTargets.targets.length <= 1 || moveTargets.multiple) { - turnCommand.move.targets = moveTargets.targets; + turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here? } else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) { - turnCommand.move.targets = playerPokemon.getMoveQueue()[0].targets; + turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here? } else { this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); } this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; success = true; } else if (cursor < playerPokemon.getMoveset().length) { - const move = playerPokemon.getMoveset()[cursor]; + const move = playerPokemon.getMoveset()[cursor]!; //TODO: is this bang correct? this.scene.ui.setMode(Mode.MESSAGE); // Decides between a Disabled, Not Implemented, or No PP translation message @@ -2022,14 +2039,14 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballForce"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballTrainer"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else { @@ -2038,23 +2055,23 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballMulti"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else if (cursor < 5) { const targetPokemon = this.scene.getEnemyField().find(p => p.isActive(true)); - if (targetPokemon.isBoss() && targetPokemon.bossSegmentIndex >= 1 && !targetPokemon.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) { + if (targetPokemon?.isBoss() && targetPokemon?.bossSegmentIndex >= 1 && !targetPokemon?.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballStrong"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else { this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor }; - this.scene.currentBattle.turnCommands[this.fieldIndex].targets = targets; + this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets; if (this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true; + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; } success = true; } @@ -2068,14 +2085,14 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else if (!isSwitch && this.scene.currentBattle.battleType === BattleType.TRAINER) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else { @@ -2091,10 +2108,10 @@ export class CommandPhase extends FieldPhase { : { command: Command.RUN }; success = true; if (!isSwitch && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true; + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; } } else if (trapTag) { - if (trapTag.sourceMove === Moves.INGRAIN && this.scene.getPokemonById(trapTag.sourceId).isOfType(Type.GHOST)) { + if (trapTag.sourceMove === Moves.INGRAIN && trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId)?.isOfType(Type.GHOST)) { success = true; this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } @@ -2107,13 +2124,13 @@ export class CommandPhase extends FieldPhase { } this.scene.ui.showText( i18next.t("battle:noEscapePokemon", { - pokemonName: getPokemonNameWithAffix(this.scene.getPokemonById(trapTag.sourceId)), + pokemonName: trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId) ? getPokemonNameWithAffix(this.scene.getPokemonById(trapTag.sourceId)!) : "", moveName: trapTag.getMoveName(), escapeVerb: isSwitch ? i18next.t("battle:escapeVerbSwitch") : i18next.t("battle:escapeVerbFlee") }), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); if (!isSwitch) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); } @@ -2123,11 +2140,11 @@ export class CommandPhase extends FieldPhase { break; } - if (success) { + if (success!) { // TODO: is the bang correct? this.end(); } - return success; + return success!; // TODO: is the bang correct? } cancel() { @@ -2147,9 +2164,9 @@ export class CommandPhase extends FieldPhase { return false; } - const moveIndex = pokemon.getMoveset().findIndex(m => m.moveId === encoreTag.moveId); + const moveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === encoreTag.moveId); - if (moveIndex === -1 || !pokemon.getMoveset()[moveIndex].isUsable(pokemon)) { + if (moveIndex === -1 || !pokemon.getMoveset()[moveIndex]!.isUsable(pokemon)) { // TODO: is this bang correct? return false; } @@ -2259,17 +2276,17 @@ export class SelectTargetPhase extends PokemonPhase { super.start(); const turnCommand = this.scene.currentBattle.turnCommands[this.fieldIndex]; - const move = turnCommand.move?.move; + const move = turnCommand?.move?.move; this.scene.ui.setMode(Mode.TARGET_SELECT, this.fieldIndex, move, (targets: BattlerIndex[]) => { this.scene.ui.setMode(Mode.MESSAGE); if (targets.length < 1) { this.scene.currentBattle.turnCommands[this.fieldIndex] = null; this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex)); } else { - turnCommand.targets = targets; + turnCommand!.targets = targets; //TODO: is the bang correct here? } - if (turnCommand.command === Command.BALL && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true; + if (turnCommand?.command === Command.BALL && this.fieldIndex) { + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; //TODO: is the bang correct here? } this.end(); }); @@ -2302,24 +2319,24 @@ export class TurnStartPhase extends FieldPhase { const aCommand = this.scene.currentBattle.turnCommands[a]; const bCommand = this.scene.currentBattle.turnCommands[b]; - if (aCommand.command !== bCommand.command) { - if (aCommand.command === Command.FIGHT) { + if (aCommand?.command !== bCommand?.command) { + if (aCommand?.command === Command.FIGHT) { return 1; - } else if (bCommand.command === Command.FIGHT) { + } else if (bCommand?.command === Command.FIGHT) { return -1; } - } else if (aCommand.command === Command.FIGHT) { - const aMove = allMoves[aCommand.move.move]; - const bMove = allMoves[bCommand.move.move]; + } else if (aCommand?.command === Command.FIGHT) { + const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? + const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a), null, aMove, aPriority); - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b), null, bMove, bPriority); + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? - applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a), null, aMove, aPriority); - applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b), null, bMove, bPriority); + applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? + applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? if (aPriority.value !== bPriority.value) { return aPriority.value < bPriority.value ? 1 : -1; @@ -2343,34 +2360,37 @@ export class TurnStartPhase extends FieldPhase { const pokemon = field[o]; const turnCommand = this.scene.currentBattle.turnCommands[o]; - if (turnCommand.skip) { + if (turnCommand?.skip) { continue; } - switch (turnCommand.command) { + switch (turnCommand?.command) { case Command.FIGHT: const queuedMove = turnCommand.move; pokemon.turnData.order = orderIndex++; if (!queuedMove) { continue; } - const move = pokemon.getMoveset().find(m => m.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); + const move = pokemon.getMoveset().find(m => m?.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); + if (move.getMove().hasAttr(MoveHeaderAttr)) { + this.scene.unshiftPhase(new MoveHeaderPhase(this.scene, pokemon, move)); + } if (pokemon.isPlayer()) { if (turnCommand.cursor === -1) { - this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move.targets, move)); + this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move));//TODO: is the bang correct here? } else { - const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move.targets, move, false, queuedMove.ignorePP); + const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP);//TODO: is the bang correct here? this.scene.pushPhase(playerPhase); } } else { - this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move.targets, move, false, queuedMove.ignorePP)); + this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP));//TODO: is the bang correct here? } break; case Command.BALL: - this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets[0] % 2, turnCommand.cursor)); + this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets![0] % 2, turnCommand.cursor!));//TODO: is the bang correct here? break; case Command.POKEMON: - this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor, true, turnCommand.args[0] as boolean, pokemon.isPlayer())); + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor!, true, turnCommand.args![0] as boolean, pokemon.isPlayer()));//TODO: is the bang correct here? break; case Command.RUN: let runningPokemon = pokemon; @@ -2565,6 +2585,15 @@ export class BattleEndPhase extends BattlePhase { this.scene.updateModifiers().then(() => this.end()); } + + end() { + // removing pokemon at the end of a battle + for (const p of this.scene.getEnemyParty()) { + p.destroy(); + } + + super.end(); + } } export class NewBattlePhase extends BattlePhase { @@ -2578,13 +2607,13 @@ export class NewBattlePhase extends BattlePhase { } export class CommonAnimPhase extends PokemonPhase { - private anim: CommonAnim; - private targetIndex: integer; + private anim: CommonAnim | null; + private targetIndex: integer | undefined; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, targetIndex: BattlerIndex, anim: CommonAnim) { + constructor(scene: BattleScene, battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex | undefined, anim?: CommonAnim) { super(scene, battlerIndex); - this.anim = anim; + this.anim = anim!; // TODO: is this bang correct? this.targetIndex = targetIndex; } @@ -2599,6 +2628,32 @@ export class CommonAnimPhase extends PokemonPhase { } } +export class MoveHeaderPhase extends BattlePhase { + public pokemon: Pokemon; + public move: PokemonMove; + + constructor(scene: BattleScene, pokemon: Pokemon, move: PokemonMove) { + super(scene); + + this.pokemon = pokemon; + this.move = move; + } + + canMove(): boolean { + return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon); + } + + start() { + super.start(); + + if (this.canMove()) { + applyMoveAttrs(MoveHeaderAttr, this.pokemon, null, this.move.getMove()).then(() => this.end()); + } else { + this.end(); + } + } +} + export class MovePhase extends BattlePhase { public pokemon: Pokemon; public move: PokemonMove; @@ -2656,8 +2711,8 @@ export class MovePhase extends BattlePhase { this.scene.arena.setIgnoreAbilities(); } } else { - this.pokemon.turnData.hitsLeft = undefined; - this.pokemon.turnData.hitCount = undefined; + this.pokemon.turnData.hitsLeft = 0; // TODO: is `0` correct? + this.pokemon.turnData.hitCount = 0; // TODO: is `0` correct? } // Move redirection abilities (ie. Storm Drain) only support single target moves @@ -2695,7 +2750,8 @@ export class MovePhase extends BattlePhase { if (this.scene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) { if (this.scene.getField()[this.targets[0]].hp === 0) { const opposingField = this.pokemon.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); - this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex(); + //@ts-ignore + this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex(); //TODO: fix ts-ignore } } } @@ -2738,7 +2794,7 @@ export class MovePhase extends BattlePhase { if (this.cancelled || this.failed) { if (this.failed) { this.move.usePp(ppUsed); // Only use PP if the move failed - this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); + this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); } // Record a failed move so Abilities like Truant don't trigger next turn and soft-lock @@ -2769,9 +2825,9 @@ export class MovePhase extends BattlePhase { return this.end(); } - if (!moveQueue.length || !moveQueue.shift().ignorePP) { // using .shift here clears out two turn moves once they've been used + if (!moveQueue.length || !moveQueue.shift()?.ignorePP) { // using .shift here clears out two turn moves once they've been used this.move.usePp(ppUsed); - this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); + this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); } if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) { @@ -2787,7 +2843,7 @@ export class MovePhase extends BattlePhase { } else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) { success = false; if (failedText === null) { - failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain.terrainType); + failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain?.terrainType!); // TODO: is this bang correct? } } @@ -2887,10 +2943,10 @@ export class MovePhase extends BattlePhase { pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), moveName: this.move.getName() }), 500); - applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true), this.move.getMove()); + applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true)!, this.move.getMove()); //TODO: is the bang correct here? } - showFailedText(failedText: string = null): void { + showFailedText(failedText: string | null = null): void { this.scene.queueMessage(failedText || i18next.t("battle:attackFailed")); } @@ -2934,7 +2990,7 @@ export class MoveEffectPhase extends PokemonPhase { const move = this.move.getMove(); // Assume single target for override - applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget(), move, overridden, this.move.virtual).then(() => { + applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => { if (overridden.value) { return this.end(); @@ -2945,7 +3001,7 @@ export class MoveEffectPhase extends PokemonPhase { if (user.turnData.hitsLeft === undefined) { const hitCount = new Utils.IntegerHolder(1); // Assume single target for multi hit - applyMoveAttrs(MultiHitAttr, user, this.getTarget(), move, hitCount); + applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount); applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, targets.length, hitCount, new Utils.IntegerHolder(0)); if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) { this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); @@ -2956,11 +3012,11 @@ export class MoveEffectPhase extends PokemonPhase { const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual }; const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)])); - const activeTargets = targets.map(t => t.isActive(true)); - if (!activeTargets.length || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { + const hasActiveTargets = targets.some(t => t.isActive(true)); + if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { this.stopMultiHit(); - if (activeTargets.length) { - this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(this.getTarget()) })); + if (hasActiveTargets) { + this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getTarget()? getPokemonNameWithAffix(this.getTarget()!) : "" })); moveHistoryEntry.result = MoveResult.MISS; applyMoveAttrs(MissEffectAttr, user, null, move); } else { @@ -2974,7 +3030,9 @@ export class MoveEffectPhase extends PokemonPhase { const applyAttrs: Promise[] = []; // Move animation only needs one target - new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()).play(this.scene, () => { + new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()!).play(this.scene, () => { // TODO: is the bang correct here? + /** Has the move successfully hit a target (for damage) yet? */ + let hasHit: boolean = false; for (const target of targets) { if (!targetHitChecks[target.getBattlerIndex()]) { this.stopMultiHit(target); @@ -2990,7 +3048,6 @@ export class MoveEffectPhase extends PokemonPhase { const isProtected = !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target) && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)); const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount); - const firstTarget = (moveHistoryEntry.result === MoveResult.PENDING); if (firstHit) { user.pushMoveHistory(moveHistoryEntry); @@ -3000,6 +3057,18 @@ export class MoveEffectPhase extends PokemonPhase { const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT; + const dealsDamage = [ + HitResult.EFFECTIVE, + HitResult.SUPER_EFFECTIVE, + HitResult.NOT_VERY_EFFECTIVE, + HitResult.ONE_HIT_KO + ].includes(hitResult); + + const firstTarget = dealsDamage && !hasHit; + if (firstTarget) { + hasHit = true; + } + const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()); if (lastHit) { @@ -3007,17 +3076,17 @@ export class MoveEffectPhase extends PokemonPhase { } applyAttrs.push(new Promise(resolve => { - applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), + applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT, user, target, move).then(() => { if (hitResult !== HitResult.FAIL) { - const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget(), move)); + const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move)); // Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY && attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => { if (hitResult !== HitResult.NO_EFFECT) { applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY && !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => { - if (hitResult < HitResult.NO_EFFECT && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) { + if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) { const flinched = new Utils.BooleanHolder(false); user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched); if (flinched.value) { @@ -3074,13 +3143,13 @@ export class MoveEffectPhase extends PokemonPhase { move.type = move.defaultType; const user = this.getUserPokemon(); if (user) { - if (--user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) { + if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) { this.scene.unshiftPhase(this.getNewHitPhase()); } else { // Queue message for number of hits made by multi-move // If multi-hit attack only hits once, still want to render a message - const hitsTotal = user.turnData.hitCount - Math.max(user.turnData.hitsLeft, 0); - if (hitsTotal > 1 || user.turnData.hitsLeft > 0) { + const hitsTotal = user.turnData.hitCount! - Math.max(user.turnData.hitsLeft!, 0); // TODO: are those bangs correct? + if (hitsTotal > 1 || (user.turnData.hitsLeft && user.turnData.hitsLeft > 0)) { // If there are multiple hits, or if there are hits of the multi-hit move left this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); } @@ -3097,7 +3166,7 @@ export class MoveEffectPhase extends PokemonPhase { return true; } - const user = this.getUserPokemon(); + const user = this.getUserPokemon()!; // TODO: is this bang correct? // Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits. // However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal @@ -3126,7 +3195,7 @@ export class MoveEffectPhase extends PokemonPhase { return false; } - const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user, target); + const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user!, target); // TODO: is the bang correct here? if (moveAccuracy === -1) { return true; @@ -3135,12 +3204,12 @@ export class MoveEffectPhase extends PokemonPhase { const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove()); const rand = user.randSeedInt(100, 1); - return rand <= moveAccuracy * accuracyMultiplier; + return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct? } - getUserPokemon(): Pokemon { + getUserPokemon(): Pokemon | undefined { if (this.battlerIndex > BattlerIndex.ENEMY_2) { - return this.scene.getPokemonById(this.battlerIndex); + return this.scene.getPokemonById(this.battlerIndex) ?? undefined; } return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex]; } @@ -3149,7 +3218,7 @@ export class MoveEffectPhase extends PokemonPhase { return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1); } - getTarget(): Pokemon { + getTarget(): Pokemon | undefined { return this.getTargets().find(() => true); } @@ -3170,8 +3239,8 @@ export class MoveEffectPhase extends PokemonPhase { * targets, completely cancel all subsequent strikes. */ if (!target || this.targets.length === 0 ) { - this.getUserPokemon().turnData.hitCount = 1; - this.getUserPokemon().turnData.hitsLeft = 1; + this.getUserPokemon()!.turnData.hitCount = 1; // TODO: is the bang correct here? + this.getUserPokemon()!.turnData.hitsLeft = 1; // TODO: is the bang correct here? } } @@ -3225,7 +3294,7 @@ export class MoveAnimTestPhase extends BattlePhase { initMoveAnim(this.scene, moveId).then(() => { loadMoveAnimAssets(this.scene, [moveId], true) .then(() => { - new MoveAnim(moveId, player ? this.scene.getPlayerPokemon() : this.scene.getEnemyPokemon(), (player !== (allMoves[moveId] instanceof SelfStatusMove) ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon()).getBattlerIndex()).play(this.scene, () => { + new MoveAnim(moveId, player ? this.scene.getPlayerPokemon()! : this.scene.getEnemyPokemon()!, (player !== (allMoves[moveId] instanceof SelfStatusMove) ? this.scene.getEnemyPokemon()! : this.scene.getPlayerPokemon()!).getBattlerIndex()).play(this.scene, () => { // TODO: are the bangs correct here? if (player) { this.playMoveAnim(moveQueue, false); } else { @@ -3251,16 +3320,19 @@ export class ShowAbilityPhase extends PokemonPhase { const pokemon = this.getPokemon(); - this.scene.abilityBar.showAbility(pokemon, this.passive); - if (pokemon.battleData) { - pokemon.battleData.abilityRevealed = true; + if (pokemon) { + this.scene.abilityBar.showAbility(pokemon, this.passive); + + if (pokemon?.battleData) { + pokemon.battleData.abilityRevealed = true; + } } this.end(); } } -export type StatChangeCallback = (target: Pokemon, changed: BattleStat[], relativeChanges: number[]) => void; +export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void; export class StatChangePhase extends PokemonPhase { private stats: BattleStat[]; @@ -3269,10 +3341,10 @@ export class StatChangePhase extends PokemonPhase { private showMessage: boolean; private ignoreAbilities: boolean; private canBeCopied: boolean; - private onChange: StatChangeCallback; + private onChange: StatChangeCallback | null; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback = null) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback | null = null) { super(scene, battlerIndex); this.selfTarget = selfTarget; @@ -3321,9 +3393,9 @@ export class StatChangePhase extends PokemonPhase { } const battleStats = this.getPokemon().summonData.battleStats; - const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats[stat] + levels.value, 6) : Math.max(battleStats[stat] + levels.value, -6)) - battleStats[stat]); + const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats![stat] + levels.value, 6) : Math.max(battleStats![stat] + levels.value, -6)) - battleStats![stat]); - this.onChange?.(this.getPokemon(), filteredStats, relLevels); + this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels); const end = () => { if (this.showMessage) { @@ -3385,7 +3457,7 @@ export class StatChangePhase extends PokemonPhase { this.scene.playSound(`stat_${levels.value >= 1 ? "up" : "down"}`); - statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite)); + statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite ?? undefined)); this.scene.tweens.add({ targets: statSprite, @@ -3418,7 +3490,7 @@ export class StatChangePhase extends PokemonPhase { getRandomStat(): BattleStat { const allStats = Utils.getEnumValues(BattleStat); - return allStats[this.getPokemon().randSeedInt(BattleStat.SPD + 1)]; + return this.getPokemon() ? allStats[this.getPokemon()!.randSeedInt(BattleStat.SPD + 1)] : BattleStat.ATK; // TODO: return default ATK on random? idk... } aggregateStatChanges(random: boolean = false): void { @@ -3483,7 +3555,7 @@ export class StatChangePhase extends PokemonPhase { } export class WeatherEffectPhase extends CommonAnimPhase { - public weather: Weather; + public weather: Weather | null; constructor(scene: BattleScene) { super(scene, undefined, undefined, CommonAnim.SUNNY + ((scene?.arena?.weather?.weatherType || WeatherType.NONE) - 1)); @@ -3511,7 +3583,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { const inflictDamage = (pokemon: Pokemon) => { const cancelled = new Utils.BooleanHolder(false); - applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled); + applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather , cancelled); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (cancelled.value) { @@ -3520,12 +3592,12 @@ export class WeatherEffectPhase extends CommonAnimPhase { const damage = Math.ceil(pokemon.getMaxHp() / 16); - this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon)); + this.scene.queueMessage(getWeatherDamageMessage(this.weather?.weatherType!, pokemon)!); // TODO: are those bangs correct? pokemon.damageAndUpdate(damage, HitResult.EFFECTIVE, false, false, true); }; this.executeForAll((pokemon: Pokemon) => { - const immune = !pokemon || !!pokemon.getTypes(true, true).filter(t => this.weather.isTypeDamageImmune(t)).length; + const immune = !pokemon || !!pokemon.getTypes(true, true).filter(t => this.weather?.isTypeDamageImmune(t)).length; if (!immune) { inflictDamage(pokemon); } @@ -3533,7 +3605,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { } } - this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType), null, () => { + this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType)!, null, () => { // TODO: is this bang correct? this.executeForAll((pokemon: Pokemon) => applyPostWeatherLapseAbAttrs(PostWeatherLapseAbAttr, pokemon, this.weather)); super.start(); @@ -3542,31 +3614,31 @@ export class WeatherEffectPhase extends CommonAnimPhase { } export class ObtainStatusEffectPhase extends PokemonPhase { - private statusEffect: StatusEffect; - private cureTurn: integer; - private sourceText: string; - private sourcePokemon: Pokemon; + private statusEffect: StatusEffect | undefined; + private cureTurn: integer | null; + private sourceText: string | null; + private sourcePokemon: Pokemon | null; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, statusEffect: StatusEffect, cureTurn?: integer, sourceText?: string, sourcePokemon?: Pokemon) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, statusEffect?: StatusEffect, cureTurn?: integer | null, sourceText?: string, sourcePokemon?: Pokemon) { super(scene, battlerIndex); this.statusEffect = statusEffect; - this.cureTurn = cureTurn; - this.sourceText = sourceText; - this.sourcePokemon = sourcePokemon; // For tracking which Pokemon caused the status effect + this.cureTurn = cureTurn!; // TODO: is this bang correct? + this.sourceText = sourceText!; // TODO: is this bang correct? + this.sourcePokemon = sourcePokemon!; // For tracking which Pokemon caused the status effect // TODO: is this bang correct? } start() { const pokemon = this.getPokemon(); - if (!pokemon.status) { - if (pokemon.trySetStatus(this.statusEffect, false, this.sourcePokemon)) { + if (!pokemon?.status) { + if (pokemon?.trySetStatus(this.statusEffect, false, this.sourcePokemon)) { if (this.cureTurn) { - pokemon.status.cureTurn = this.cureTurn; + pokemon.status!.cureTurn = this.cureTurn; // TODO: is this bang correct? } pokemon.updateInfo(true); - new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => { - this.scene.queueMessage(getStatusEffectObtainText(this.statusEffect, getPokemonNameWithAffix(pokemon), this.sourceText)); - if (pokemon.status.isPostTurn()) { + new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, () => { + this.scene.queueMessage(getStatusEffectObtainText(this.statusEffect, getPokemonNameWithAffix(pokemon), this.sourceText ?? undefined)); + if (pokemon.status?.isPostTurn()) { this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.battlerIndex)); } this.end(); @@ -3632,17 +3704,17 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { export class MessagePhase extends Phase { private text: string; - private callbackDelay: integer; - private prompt: boolean; - private promptDelay: integer; + private callbackDelay: integer | null; + private prompt: boolean | null; + private promptDelay: integer | null; - constructor(scene: BattleScene, text: string, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + constructor(scene: BattleScene, text: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { super(scene); this.text = text; - this.callbackDelay = callbackDelay; - this.prompt = prompt; - this.promptDelay = promptDelay; + this.callbackDelay = callbackDelay!; // TODO: is this bang correct? + this.prompt = prompt!; // TODO: is this bang correct? + this.promptDelay = promptDelay!; // TODO: is this bang correct? } start() { @@ -3750,7 +3822,7 @@ export class FaintPhase extends PokemonPhase { constructor(scene: BattleScene, battlerIndex: BattlerIndex, preventEndure?: boolean) { super(scene, battlerIndex); - this.preventEndure = preventEndure; + this.preventEndure = preventEndure!; // TODO: is this bang correct? } start() { @@ -3776,6 +3848,7 @@ export class FaintPhase extends PokemonPhase { doFaint(): void { const pokemon = this.getPokemon(); + // Track total times pokemon have been KO'd for supreme overlord/last respects if (pokemon.isPlayer()) { this.scene.currentBattle.playerFaints += 1; @@ -3787,7 +3860,7 @@ export class FaintPhase extends PokemonPhase { if (pokemon.turnData?.attacksReceived?.length) { const lastAttack = pokemon.turnData.attacksReceived[0]; - applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId), new PokemonMove(lastAttack.move).getMove(), lastAttack.result); + applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId)!, new PokemonMove(lastAttack.move).getMove(), lastAttack.result); // TODO: is this bang correct? } const alivePlayField = this.scene.getField(true); @@ -3826,17 +3899,10 @@ export class FaintPhase extends PokemonPhase { } } + // in double battles redirect potential moves off fainted pokemon if (this.scene.currentBattle.double) { const allyPokemon = pokemon.getAlly(); - if (allyPokemon?.isActive(true)) { - let targetingMovePhase: MovePhase; - do { - targetingMovePhase = this.scene.findPhase(mp => mp instanceof MovePhase && mp.targets.length === 1 && mp.targets[0] === pokemon.getBattlerIndex() && mp.pokemon.isPlayer() !== allyPokemon.isPlayer()) as MovePhase; - if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) { - targetingMovePhase.targets[0] = allyPokemon.getBattlerIndex(); - } - } while (targetingMovePhase); - } + this.scene.redirectPokemonMoves(pokemon, allyPokemon); } pokemon.lapseTags(BattlerTagLapseType.FAINT); @@ -3908,7 +3974,7 @@ export class VictoryPhase extends PokemonPhase { const multipleParticipantExpBonusModifier = this.scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier; const nonFaintedPartyMembers = party.filter(p => p.hp); const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.scene.getMaxExpLevel()); - const partyMemberExp = []; + const partyMemberExp: number[] = []; if (participantIds.size) { let expValue = this.getPokemon().getExpValue(); @@ -3944,9 +4010,7 @@ export class VictoryPhase extends PokemonPhase { expMultiplier = Overrides.XP_MULTIPLIER_OVERRIDE; } const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier); - const modifierBonusExp = new Utils.NumberHolder(1); - this.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, modifierBonusExp); - pokemonExp.value *= modifierBonusExp.value; + this.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp); partyMemberExp.push(Math.floor(pokemonExp.value)); } @@ -3960,7 +4024,7 @@ export class VictoryPhase extends PokemonPhase { const medianLevel = Math.floor(totalLevel / expPartyMembers.length); - const recipientExpPartyMemberIndexes = []; + const recipientExpPartyMemberIndexes: number[] = []; expPartyMembers.forEach((expPartyMember, epm) => { if (expPartyMember.level <= medianLevel) { recipientExpPartyMemberIndexes.push(epm); @@ -4035,39 +4099,40 @@ export class TrainerVictoryPhase extends BattlePhase { start() { this.scene.disableMenu = true; - this.scene.playBgm(this.scene.currentBattle.trainer.config.victoryBgm); + this.scene.playBgm(this.scene.currentBattle.trainer?.config.victoryBgm); - this.scene.unshiftPhase(new MoneyRewardPhase(this.scene, this.scene.currentBattle.trainer.config.moneyMultiplier)); + this.scene.unshiftPhase(new MoneyRewardPhase(this.scene, this.scene.currentBattle.trainer?.config.moneyMultiplier!)); // TODO: is this bang correct? - const modifierRewardFuncs = this.scene.currentBattle.trainer.config.modifierRewardFuncs; + const modifierRewardFuncs = this.scene.currentBattle.trainer?.config.modifierRewardFuncs!; // TODO: is this bang correct? for (const modifierRewardFunc of modifierRewardFuncs) { this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, modifierRewardFunc)); } - const trainerType = this.scene.currentBattle.trainer.config.trainerType; + const trainerType = this.scene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? if (vouchers.hasOwnProperty(TrainerType[trainerType])) { - if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer.config.isBoss) { + if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) { this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType])); } } - this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }), null, () => { - const victoryMessages = this.scene.currentBattle.trainer.getVictoryMessages(); + this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }), null, () => { + const victoryMessages = this.scene.currentBattle.trainer?.getVictoryMessages()!; // TODO: is this bang correct? let message: string; this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex); + message = message!; // tell TS compiler it's defined now const showMessage = () => { const originalFunc = showMessageOrEnd; - showMessageOrEnd = () => this.scene.ui.showDialogue(message, this.scene.currentBattle.trainer.getName(), null, originalFunc); + showMessageOrEnd = () => this.scene.ui.showDialogue(message, this.scene.currentBattle.trainer?.getName(), null, originalFunc); showMessageOrEnd(); }; let showMessageOrEnd = () => this.end(); if (victoryMessages?.length) { - if (this.scene.currentBattle.trainer.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { + if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { const originalFunc = showMessageOrEnd; showMessageOrEnd = () => this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => originalFunc())); - this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(this.scene.currentBattle.trainer.getKey(), getCharVariantFromDialogue(victoryMessages[0])).then(() => showMessage())); + this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(this.scene.currentBattle.trainer?.getKey()!, getCharVariantFromDialogue(victoryMessages[0])).then(() => showMessage())); // TODO: is this bang correct? } else { showMessage(); } @@ -4128,7 +4193,7 @@ export class ModifierRewardPhase extends BattlePhase { const newModifier = this.modifierType.newModifier(); this.scene.addModifier(newModifier).then(() => { this.scene.playSound("item_fanfare"); - this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier.type.name }), null, () => resolve(), null, true); + this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => resolve(), null, true); }); }); } @@ -4146,7 +4211,7 @@ export class GameOverModifierRewardPhase extends ModifierRewardPhase { this.scene.playSound("level_up_fanfare"); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.fadeIn(250).then(() => { - this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier.type.name }), null, () => { + this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => { this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true)); resolve(); }, null, true, 1500); @@ -4174,7 +4239,7 @@ export class RibbonModifierRewardPhase extends ModifierRewardPhase { this.scene.ui.showText(i18next.t("battle:beatModeFirstTime", { speciesName: this.species.name, gameMode: this.scene.gameMode.getName(), - newModifier: newModifier.type.name + newModifier: newModifier?.type.name }), null, () => { resolve(); }, null, true, 1500); @@ -4244,6 +4309,7 @@ export class GameOverPhase extends BattlePhase { if (this.victory && newClear) { if (this.scene.gameMode.isClassic) { firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY); + this.scene.validateAchv(achvs.UNEVOLVED_CLASSIC_VICTORY); this.scene.gameData.gameStats.sessionsWon++; for (const pokemon of this.scene.getParty()) { this.awardRibbon(pokemon); @@ -4344,6 +4410,9 @@ export class GameOverPhase extends BattlePhase { if (!this.scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) { this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.MINI_BLACK_HOLE)); } + if (!this.scene.gameData.unlocks[Unlockables.EVIOLITE] && this.scene.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)) { + this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.EVIOLITE)); + } } } @@ -4415,12 +4484,12 @@ export class UnlockPhase extends Phase { } export class PostGameOverPhase extends Phase { - private endCardPhase: EndCardPhase; + private endCardPhase: EndCardPhase | null; - constructor(scene: BattleScene, endCardPhase: EndCardPhase) { + constructor(scene: BattleScene, endCardPhase?: EndCardPhase) { super(scene); - this.endCardPhase = endCardPhase; + this.endCardPhase = endCardPhase!; // TODO: is this bang correct? } start() { @@ -4446,8 +4515,8 @@ export class PostGameOverPhase extends Phase { this.scene.ui.fadeOut(500).then(() => { this.scene.ui.getMessageHandler().bg.setVisible(true); - this.endCardPhase.endCard.destroy(); - this.endCardPhase.text.destroy(); + this.endCardPhase?.endCard.destroy(); + this.endCardPhase?.text.destroy(); saveAndReset(); }); } else { @@ -4456,11 +4525,24 @@ export class PostGameOverPhase extends Phase { } } +/** + * Opens the party selector UI and transitions into a {@linkcode SwitchSummonPhase} + * for the player (if a switch would be valid for the current battle state). + */ export class SwitchPhase extends BattlePhase { protected fieldIndex: integer; private isModal: boolean; private doReturn: boolean; + /** + * Creates a new SwitchPhase + * @param scene {@linkcode BattleScene} Current battle scene + * @param fieldIndex Field index to switch out + * @param isModal Indicates if the switch should be forced (true) or is + * optional (false). + * @param doReturn Indicates if the party member on the field should be + * recalled to ball or has already left the field. Passed to {@linkcode SwitchSummonPhase}. + */ constructor(scene: BattleScene, fieldIndex: integer, isModal: boolean, doReturn: boolean) { super(scene); @@ -4472,13 +4554,17 @@ export class SwitchPhase extends BattlePhase { start() { super.start(); - // Skip modal switch if impossible + // Skip modal switch if impossible (no remaining party members that aren't in battle) if (this.isModal && !this.scene.getParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length) { return super.end(); } - // Skip if the fainted party member has been revived already - if (this.isModal && !this.scene.getParty()[this.fieldIndex].isFainted()) { + // Skip if the fainted party member has been revived already. doReturn is + // only passed as `false` from FaintPhase (as opposed to other usages such + // as ForceSwitchOutAttr or CheckSwitchPhase), so we only want to check this + // if the mon should have already been returned but is still alive and well + // on the field. see also; battle.test.ts + if (this.isModal && !this.doReturn && !this.scene.getParty()[this.fieldIndex].isFainted()) { return super.end(); } @@ -4710,7 +4796,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { } this.scene.ui.setMode(messageMode).then(() => { this.scene.ui.showText(i18next.t("battle:countdownPoof"), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex].getName() }), null, () => { + this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }), null, () => { // TODO: is the bang correct? this.scene.ui.showText(i18next.t("battle:learnMoveAnd"), null, () => { pokemon.setMove(moveIndex, Moves.NONE); this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); @@ -4732,14 +4818,14 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { export class PokemonHealPhase extends CommonAnimPhase { private hpHealed: integer; - private message: string; + private message: string | null; private showFullHpMessage: boolean; private skipAnim: boolean; private revive: boolean; private healStatus: boolean; private preventFullHeal: boolean; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, hpHealed: integer, message: string, showFullHpMessage: boolean, skipAnim: boolean = false, revive: boolean = false, healStatus: boolean = false, preventFullHeal: boolean = false) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, hpHealed: integer, message: string | null, showFullHpMessage: boolean, skipAnim: boolean = false, revive: boolean = false, healStatus: boolean = false, preventFullHeal: boolean = false) { super(scene, battlerIndex, undefined, CommonAnim.HEALTH_UP); this.hpHealed = hpHealed; @@ -5147,7 +5233,7 @@ export class SelectModifierPhase extends BattlePhase { super(scene); this.rerollCount = rerollCount; - this.modifierTiers = modifierTiers; + this.modifierTiers = modifierTiers!; // TODO: is this bang correct? } start() { @@ -5190,7 +5276,7 @@ export class SelectModifierPhase extends BattlePhase { return false; } else { this.scene.reroll = true; - this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type.tier))); + this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[])); this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { @@ -5228,17 +5314,21 @@ export class SelectModifierPhase extends BattlePhase { } return true; case 1: - modifierType = typeOptions[cursor].type; + if (typeOptions[cursor].type) { + modifierType = typeOptions[cursor].type; + } break; default: const shopOptions = getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1)); const shopOption = shopOptions[rowCursor > 2 || shopOptions.length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT]; - modifierType = shopOption.type; + if (shopOption.type) { + modifierType = shopOption.type; + } cost = shopOption.cost; break; } - if (cost && (this.scene.money < cost) && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + if (cost! && (this.scene.money < cost) && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) { // TODO: is the bang on cost correct? this.scene.ui.playError(); return false; } @@ -5273,12 +5363,12 @@ export class SelectModifierPhase extends BattlePhase { } }; - if (modifierType instanceof PokemonModifierType) { + if (modifierType! instanceof PokemonModifierType) { //TODO: is the bang correct? if (modifierType instanceof FusePokemonModifierType) { this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.SPLICE, -1, (fromSlotIndex: integer, spliceSlotIndex: integer) => { if (spliceSlotIndex !== undefined && fromSlotIndex < 6 && spliceSlotIndex < 6 && fromSlotIndex !== spliceSlotIndex) { this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { - const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex]); + const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct? applyModifier(modifier, true); }); } else { @@ -5306,7 +5396,7 @@ export class SelectModifierPhase extends BattlePhase { ? modifierType.newModifier(party[slotIndex]) : modifierType.newModifier(party[slotIndex], option as integer) : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1); - applyModifier(modifier, true); + applyModifier(modifier!, true); // TODO: is the bang correct? }); } else { this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); @@ -5314,10 +5404,10 @@ export class SelectModifierPhase extends BattlePhase { }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined, tmMoveId, isPpRestoreModifier); } } else { - applyModifier(modifierType.newModifier()); + applyModifier(modifierType!.newModifier()!); // TODO: is the bang correct? } - return !cost; + return !cost!;// TODO: is the bang correct? }; this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); } @@ -5337,7 +5427,7 @@ export class SelectModifierPhase extends BattlePhase { } else if (lockRarities) { const tierValues = [50, 125, 300, 750, 2000]; for (const opt of typeOptions) { - baseValue += tierValues[opt.type.tier]; + baseValue += opt.type?.tier ? tierValues[opt.type.tier] : 0; } } else { baseValue = 250; @@ -5472,7 +5562,7 @@ export class PartyHealPhase extends BattlePhase { pokemon.hp = pokemon.getMaxHp(); pokemon.resetStatus(); for (const move of pokemon.moveset) { - move.ppUsed = 0; + move!.ppUsed = 0; // TODO: is this bang correct? } pokemon.updateInfo(true); } @@ -5519,19 +5609,45 @@ export class ScanIvsPhase extends PokemonPhase { const pokemon = this.getPokemon(); - this.scene.ui.showText(i18next.t("battle:ivScannerUseQuestion", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { - this.scene.ui.setMode(Mode.CONFIRM, () => { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.clearText(); - new CommonBattleAnim(CommonAnim.LOCK_ON, pokemon, pokemon).play(this.scene, () => { - this.scene.ui.getMessageHandler().promptIvs(pokemon.id, pokemon.ivs, this.shownIvs).then(() => this.end()); + let enemyIvs: number[] = []; + let statsContainer: Phaser.GameObjects.Sprite[] = []; + let statsContainerLabels: Phaser.GameObjects.Sprite[] = []; + const enemyField = this.scene.getEnemyField(); + const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible + for (let e = 0; e < enemyField.length; e++) { + enemyIvs = enemyField[e].ivs; + const currentIvs = this.scene.gameData.dexData[enemyField[e].species.getRootSpeciesId()].ivs; // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists + const ivsToShow = this.scene.ui.getMessageHandler().getTopIvs(enemyIvs, this.shownIvs); + statsContainer = enemyField[e].getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[]; + statsContainerLabels = statsContainer.filter(m => m.name.indexOf("icon_stat_label") >= 0); + for (let s = 0; s < statsContainerLabels.length; s++) { + const ivStat = Stat[statsContainerLabels[s].frame.name]; + if (enemyIvs[ivStat] > currentIvs[ivStat] && ivsToShow.indexOf(Number(ivStat)) >= 0) { + const hexColour = enemyIvs[ivStat] === 31 ? getTextColor(TextStyle.PERFECT_IV, false, uiTheme) : getTextColor(TextStyle.SUMMARY_GREEN, false, uiTheme); + const hexTextColour = Phaser.Display.Color.HexStringToColor(hexColour).color; + statsContainerLabels[s].setTint(hexTextColour); + } + statsContainerLabels[s].setVisible(true); + } + } + + if (!this.scene.hideIvs) { + this.scene.ui.showText(i18next.t("battle:ivScannerUseQuestion", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + this.scene.ui.setMode(Mode.CONFIRM, () => { + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.clearText(); + new CommonBattleAnim(CommonAnim.LOCK_ON, pokemon, pokemon).play(this.scene, () => { + this.scene.ui.getMessageHandler().promptIvs(pokemon.id, pokemon.ivs, this.shownIvs).then(() => this.end()); + }); + }, () => { + this.scene.ui.setMode(Mode.MESSAGE); + this.scene.ui.clearText(); + this.end(); }); - }, () => { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.clearText(); - this.end(); }); - }); + } else { + this.end(); + } } } diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts index 741c31183d4..c9a76dc50a4 100644 --- a/src/pipelines/sprite.ts +++ b/src/pipelines/sprite.ts @@ -377,7 +377,7 @@ export default class SpritePipeline extends FieldSpritePipeline { this.set2f("texSize", sprite.texture.source[0].width, sprite.texture.source[0].height); this.set1f("yOffset", sprite.height - sprite.frame.height * (isEntityObj ? sprite.parentContainer.scale : sprite.scale)); this.set4fv("tone", tone); - this.bindTexture(this.game.textures.get("tera").source[0].glTexture, 1); + this.bindTexture(this.game.textures.get("tera").source[0].glTexture!, 1); // TODO: is this bang correct? if ((gameObject.scene as BattleScene).fusionPaletteSwaps) { const spriteColors = ((ignoreOverride && data["spriteColorsBase"]) || data["spriteColors"] || []) as number[][]; diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index b6f242f3e89..82cde7cd2ad 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -2,59 +2,74 @@ import i18next from "i18next"; import LanguageDetector from "i18next-browser-languagedetector"; import processor, { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor"; +import { caESConfig} from "#app/locales/ca-ES/config.js"; import { deConfig } from "#app/locales/de/config.js"; import { enConfig } from "#app/locales/en/config.js"; import { esConfig } from "#app/locales/es/config.js"; import { frConfig } from "#app/locales/fr/config.js"; import { itConfig } from "#app/locales/it/config.js"; import { koConfig } from "#app/locales/ko/config.js"; +import { jaConfig } from "#app/locales/ja/config.js"; import { ptBrConfig } from "#app/locales/pt_BR/config.js"; import { zhCnConfig } from "#app/locales/zh_CN/config.js"; import { zhTwConfig } from "#app/locales/zh_TW/config.js"; -const unicodeHalfAndFullWidthForms = [ - "U+FF00-FFEF" +interface LoadingFontFaceProperty { + face: FontFace, + extraOptions?: { [key:string]: any }, + only?: Array +} + +const unicodeRanges = { + fullwidth: "U+FF00-FFEF", + hangul: "U+1100-11FF,U+3130-318F,U+A960-A97F,U+AC00-D7AF,U+D7B0-D7FF", + kana: "U+3040-30FF", + CJKCommon: "U+2E80-2EFF,U+3000-303F,U+31C0-31EF,U+3200-32FF,U+3400-4DBF,U+F900-FAFF,U+FE30-FE4F", + CJKIdeograph: "U+4E00-9FFF", +}; +const rangesByLanguage = { + korean: [unicodeRanges.CJKCommon, unicodeRanges.hangul].join(","), + chinese: [unicodeRanges.CJKCommon, unicodeRanges.fullwidth, unicodeRanges.CJKIdeograph].join(","), + japanese: [unicodeRanges.CJKCommon, unicodeRanges.fullwidth, unicodeRanges.kana, unicodeRanges.CJKIdeograph].join(",") +}; + +const fonts: Array = [ + { + face: new FontFace("emerald", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: rangesByLanguage.korean }), + }, + { + face: new FontFace("pkmnems", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: rangesByLanguage.korean }), + extraOptions: { sizeAdjust: "133%" }, + }, + // unicode (chinese) + { + face: new FontFace("emerald", "url(./fonts/unifont-15.1.05.subset.woff2)", { unicodeRange: rangesByLanguage.chinese }), + extraOptions: { sizeAdjust: "70%", format: "woff2" }, + only: [ "en", "es", "fr", "it", "de", "zh", "pt", "ko", "ca" ], + }, + { + face: new FontFace("pkmnems", "url(./fonts/unifont-15.1.05.subset.woff2)", { unicodeRange: rangesByLanguage.chinese }), + extraOptions: { format: "woff2" }, + only: [ "en", "es", "fr", "it", "de", "zh", "pt", "ko", "ca" ], + }, + // japanese + { + face: new FontFace("emerald", "url(./fonts/Galmuri11.subset.woff2)", { unicodeRange: rangesByLanguage.japanese }), + extraOptions: { sizeAdjust: "66%" }, + only: [ "ja" ], + }, + { + face: new FontFace("pkmnems", "url(./fonts/Galmuri9.subset.woff2)", { unicodeRange: rangesByLanguage.japanese }), + only: [ "ja" ], + }, ]; -const unicodeCJK = [ - "U+2E80-2EFF", - "U+3000-303F", - "U+31C0-31EF", - "U+3200-32FF", - "U+3400-4DBF", - "U+4E00-9FFF", - "U+F900-FAFF", - "U+FE30-FE4F", -].join(","); - -const unicodeHangul = [ - "U+1100-11FF", - "U+3130-318F", - "U+A960-A97F", - "U+AC00-D7AF", - "U+D7B0-D7FF", -].join(","); - -const fonts = [ - // korean - new FontFace("emerald", "url(./fonts/PokePT_Wansung.ttf)", { unicodeRange: unicodeHangul}), - Object.assign( - new FontFace("pkmnems", "url(./fonts/PokePT_Wansung.ttf)", { unicodeRange: unicodeHangul}), - { sizeAdjust: "133%" } - ), - // unicode - Object.assign( - new FontFace("emerald", "url(./fonts/unifont-15.1.05.otf)", { unicodeRange: [unicodeCJK, unicodeHalfAndFullWidthForms].join(",") }), - { sizeAdjust: "70%", format: "opentype" } - ), - Object.assign( - new FontFace("pkmnems", "url(./fonts/unifont-15.1.05.otf)", { unicodeRange: [unicodeCJK, unicodeHalfAndFullWidthForms].join(",") }), - { format: "opentype" } - ), -]; - -async function initFonts() { - const results = await Promise.allSettled(fonts.map(font => font.load())); +async function initFonts(language: string | undefined) { + const results = await Promise.allSettled( + fonts + .filter(font => !font.only || font.only.some(exclude => language?.indexOf(exclude) === 0)) + .map(font => Object.assign(font.face, font.extraOptions ?? {}).load()) + ); for (const result of results) { if (result.status === "fulfilled") { document.fonts?.add(result.value); @@ -94,7 +109,7 @@ export async function initI18n(): Promise { await i18next.init({ nonExplicitSupportedLngs: true, fallbackLng: "en", - supportedLngs: ["en", "es", "fr", "it", "de", "zh", "pt", "ko"], + supportedLngs: ["en", "es", "fr", "it", "de", "zh", "pt", "ko", "ja", "ca"], defaultNS: "menu", ns: Object.keys(enConfig), detection: { @@ -132,11 +147,17 @@ export async function initI18n(): Promise { ko: { ...koConfig }, + ja: { + ...jaConfig + }, + "ca-ES": { + ...caESConfig + } }, postProcess: ["korean-postposition"], }); - await initFonts(); + await initFonts(localStorage.getItem("prLang") ?? undefined); } export default i18next; diff --git a/src/system/achv.ts b/src/system/achv.ts index 0dcf74ce3a5..040a48d9a7e 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -1,11 +1,13 @@ import { Modifier } from "typescript"; import BattleScene from "../battle-scene"; import { TurnHeldItemTransferModifier } from "../modifier/modifier"; +import { pokemonEvolutions } from "#app/data/pokemon-evolutions"; import i18next from "i18next"; import * as Utils from "../utils"; import { PlayerGender } from "#enums/player-gender"; import { ParseKeys } from "i18next"; import { Challenge, FreshStartChallenge, SingleGenerationChallenge, SingleTypeChallenge } from "#app/data/challenge.js"; +import { ConditionFn } from "#app/@types/common.js"; export enum AchvTier { COMMON, @@ -27,9 +29,9 @@ export class Achv { public hasParent: boolean; public parentId: string; - private conditionFunc: (scene: BattleScene, args: any[]) => boolean; + private conditionFunc: ConditionFn | undefined; - constructor(localizationKey:string, name: string, description: string, iconImage: string, score: integer, conditionFunc?: (scene: BattleScene, args: any[]) => boolean) { + constructor(localizationKey:string, name: string, description: string, iconImage: string, score: integer, conditionFunc?: ConditionFn) { this.name = name; this.description = description; this.iconImage = iconImage; @@ -63,7 +65,7 @@ export class Achv { return this; } - validate(scene: BattleScene, args: any[]): boolean { + validate(scene: BattleScene, args?: any[]): boolean { return !this.conditionFunc || this.conditionFunc(scene, args); } @@ -239,6 +241,8 @@ export function getAchievementDescription(localizationKey: string): string { return i18next.t(`${genderPrefix}achv:PERFECT_IVS.description` as ParseKeys); case "CLASSIC_VICTORY": return i18next.t(`${genderPrefix}achv:CLASSIC_VICTORY.description` as ParseKeys); + case "UNEVOLVED_CLASSIC_VICTORY": + return i18next.t(`${genderPrefix}achv:UNEVOLVED_CLASSIC_VICTORY.description` as ParseKeys); case "MONO_GEN_ONE": return i18next.t(`${genderPrefix}achv:MONO_GEN_ONE.description` as ParseKeys); case "MONO_GEN_TWO": @@ -325,6 +329,7 @@ export const achvs = { HIDDEN_ABILITY: new Achv("HIDDEN_ABILITY","", "HIDDEN_ABILITY.description","ability_charm", 75), PERFECT_IVS: new Achv("PERFECT_IVS","", "PERFECT_IVS.description","blunder_policy", 100), CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY","", "CLASSIC_VICTORY.description","relic_crown", 150), + UNEVOLVED_CLASSIC_VICTORY: new Achv("UNEVOLVED_CLASSIC_VICTORY", "", "UNEVOLVED_CLASSIC_VICTORY.description", "eviolite", 175, c => c.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)), MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE","", "MONO_GEN_ONE.description", "ribbon_gen1", 100, c => c instanceof SingleGenerationChallenge && c.value === 1), MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO","", "MONO_GEN_TWO.description", "ribbon_gen2", 100, c => c instanceof SingleGenerationChallenge && c.value === 2), MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE","", "MONO_GEN_THREE.description", "ribbon_gen3", 100, c => c instanceof SingleGenerationChallenge && c.value === 3), diff --git a/src/system/arena-data.ts b/src/system/arena-data.ts index f6db53d7f7f..886129edcf6 100644 --- a/src/system/arena-data.ts +++ b/src/system/arena-data.ts @@ -6,15 +6,15 @@ import { Terrain } from "#app/data/terrain.js"; export default class ArenaData { public biome: Biome; - public weather: Weather; - public terrain: Terrain; + public weather: Weather | null; + public terrain: Terrain | null; public tags: ArenaTag[]; constructor(source: Arena | any) { const sourceArena = source instanceof Arena ? source as Arena : null; this.biome = sourceArena ? sourceArena.biomeType : source.biome; - this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : undefined; - this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : undefined; + this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null; + this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null; this.tags = sourceArena ? sourceArena.tags : []; } } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 161b5a2c49b..8b756735ee6 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -41,6 +41,8 @@ import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; +import { WeatherType } from "#app/enums/weather-type.js"; +import { TerrainType } from "#app/data/terrain.js"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -78,7 +80,7 @@ export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): str export function encrypt(data: string, bypassLogin: boolean): string { return (bypassLogin ? (data: string) => btoa(data) - : (data: string) => AES.encrypt(data, saveKey))(data); + : (data: string) => AES.encrypt(data, saveKey))(data) as unknown as string; // TODO: is this correct? } export function decrypt(data: string, bypassLogin: boolean): string { @@ -230,7 +232,7 @@ export class StarterPrefs { } export interface StarterDataEntry { - moveset: StarterMoveset | StarterFormMoveData; + moveset: StarterMoveset | StarterFormMoveData | null; eggMoves: integer; candyCount: integer; friendship: integer; @@ -279,7 +281,7 @@ export class GameData { public gender: PlayerGender; public dexData: DexData; - private defaultDexData: DexData; + private defaultDexData: DexData | null; public starterData: StarterData; @@ -307,7 +309,8 @@ export class GameData { this.unlocks = { [Unlockables.ENDLESS_MODE]: false, [Unlockables.MINI_BLACK_HOLE]: false, - [Unlockables.SPLICED_ENDLESS_MODE]: false + [Unlockables.SPLICED_ENDLESS_MODE]: false, + [Unlockables.EVIOLITE]: false }; this.achvUnlocks = {}; this.voucherUnlocks = {}; @@ -352,7 +355,7 @@ export class GameData { const maxIntAttrValue = 0x80000000; const systemData = JSON.stringify(data, (k: any, v: any) => typeof v === "bigint" ? v <= maxIntAttrValue ? Number(v) : v.toString() : v); - localStorage.setItem(`data_${loggedInUser.username}`, encrypt(systemData, bypassLogin)); + localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(systemData, bypassLogin)); if (!bypassLogin) { Utils.apiPost(`savedata/system/update?clientSessionId=${clientSessionId}`, systemData, undefined, true) @@ -384,7 +387,7 @@ export class GameData { return new Promise(resolve => { console.log("Client Session:", clientSessionId); - if (bypassLogin && !localStorage.getItem(`data_${loggedInUser.username}`)) { + if (bypassLogin && !localStorage.getItem(`data_${loggedInUser?.username}`)) { return resolve(false); } @@ -404,11 +407,11 @@ export class GameData { return resolve(false); } - const cachedSystem = localStorage.getItem(`data_${loggedInUser.username}`); - this.initSystem(response, cachedSystem ? AES.decrypt(cachedSystem, saveKey).toString(enc.Utf8) : null).then(resolve); + const cachedSystem = localStorage.getItem(`data_${loggedInUser?.username}`); + this.initSystem(response, cachedSystem ? AES.decrypt(cachedSystem, saveKey).toString(enc.Utf8) : undefined).then(resolve); }); } else { - this.initSystem(decrypt(localStorage.getItem(`data_${loggedInUser.username}`), bypassLogin)).then(resolve); + this.initSystem(decrypt(localStorage.getItem(`data_${loggedInUser?.username}`)!, bypassLogin)).then(resolve); // TODO: is this bang correct? } }); } @@ -431,7 +434,7 @@ export class GameData { console.debug(systemData); - localStorage.setItem(`data_${loggedInUser.username}`, encrypt(systemDataStr, bypassLogin)); + localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(systemDataStr, bypassLogin)); /*const versions = [ this.scene.game.config.gameVersion, data.gameVersion || '0.0.0' ]; @@ -606,9 +609,9 @@ export class GameData { if (bypassLogin) { return; } - localStorage.removeItem(`data_${loggedInUser.username}`); + localStorage.removeItem(`data_${loggedInUser?.username}`); for (let s = 0; s < 5; s++) { - localStorage.removeItem(`sessionData${s ? s : ""}_${loggedInUser.username}`); + localStorage.removeItem(`sessionData${s ? s : ""}_${loggedInUser?.username}`); } } @@ -621,7 +624,7 @@ export class GameData { public saveSetting(setting: string, valueIndex: integer): boolean { let settings: object = {}; if (localStorage.hasOwnProperty("settings")) { - settings = JSON.parse(localStorage.getItem("settings")); + settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct? } setSetting(this.scene, setting, valueIndex); @@ -644,7 +647,7 @@ export class GameData { const key = deviceName.toLowerCase(); // Convert the gamepad name to lowercase to use as a key let mappingConfigs: object = {}; // Initialize an empty object to hold the mapping configurations if (localStorage.hasOwnProperty("mappingConfigs")) {// Check if 'mappingConfigs' exists in localStorage - mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")); + mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")!); // TODO: is this bang correct? } // Parse the existing 'mappingConfigs' from localStorage if (!mappingConfigs[key]) { mappingConfigs[key] = {}; @@ -669,7 +672,7 @@ export class GameData { return false; } // If 'mappingConfigs' does not exist, return false - const mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")); // Parse the existing 'mappingConfigs' from localStorage + const mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")!); // Parse the existing 'mappingConfigs' from localStorage // TODO: is this bang correct? for (const key of Object.keys(mappingConfigs)) {// Iterate over the keys of the mapping configurations this.scene.inputController.injectConfig(key, mappingConfigs[key]); @@ -684,6 +687,7 @@ export class GameData { } // If 'mappingConfigs' does not exist, return false localStorage.removeItem("mappingConfigs"); this.scene.inputController.resetConfigs(); + return true; // TODO: is `true` the correct return value? } /** @@ -703,7 +707,7 @@ export class GameData { let settingsControls: object = {}; // Initialize an empty object to hold the gamepad settings if (localStorage.hasOwnProperty(localStoragePropertyName)) { // Check if 'settingsControls' exists in localStorage - settingsControls = JSON.parse(localStorage.getItem(localStoragePropertyName)); // Parse the existing 'settingsControls' from localStorage + settingsControls = JSON.parse(localStorage.getItem(localStoragePropertyName)!); // Parse the existing 'settingsControls' from localStorage // TODO: is this bang correct? } if (device === Device.GAMEPAD) { @@ -734,11 +738,13 @@ export class GameData { return false; } - const settings = JSON.parse(localStorage.getItem("settings")); + const settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct? for (const setting of Object.keys(settings)) { setSetting(this.scene, setting, settings[setting]); } + + return true; // TODO: is `true` the correct return value? } private loadGamepadSettings(): boolean { @@ -747,18 +753,20 @@ export class GameData { if (!localStorage.hasOwnProperty("settingsGamepad")) { return false; } - const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")); + const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct? for (const setting of Object.keys(settingsGamepad)) { setSettingGamepad(this.scene, setting as SettingGamepad, settingsGamepad[setting]); } + + return true; // TODO: is `true` the correct return value? } public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean { const key = getDataTypeKey(GameDataType.TUTORIALS); let tutorials: object = {}; if (localStorage.hasOwnProperty(key)) { - tutorials = JSON.parse(localStorage.getItem(key)); + tutorials = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct? } Object.keys(Tutorial).map(t => t as Tutorial).forEach(t => { @@ -784,7 +792,7 @@ export class GameData { return ret; } - const tutorials = JSON.parse(localStorage.getItem(key)); + const tutorials = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct? for (const tutorial of Object.keys(tutorials)) { ret[tutorial] = tutorials[tutorial]; @@ -812,7 +820,7 @@ export class GameData { return ret; } - const dialogues = JSON.parse(localStorage.getItem(key)); + const dialogues = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct? for (const dialogue of Object.keys(dialogues)) { ret[dialogue] = dialogues[dialogue]; @@ -843,7 +851,7 @@ export class GameData { } as SessionSaveData; } - getSession(slotId: integer): Promise { + getSession(slotId: integer): Promise { return new Promise(async (resolve, reject) => { if (slotId < 0) { return resolve(null); @@ -858,7 +866,7 @@ export class GameData { } }; - if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`)) { + if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`)) { Utils.apiFetch(`savedata/session/get?slot=${slotId}&clientSessionId=${clientSessionId}`, true) .then(response => response.text()) .then(async response => { @@ -867,12 +875,12 @@ export class GameData { return resolve(null); } - localStorage.setItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`, encrypt(response, bypassLogin)); + localStorage.setItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`, encrypt(response, bypassLogin)); await handleSessionData(response); }); } else { - const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`); + const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); if (sessionData) { await handleSessionData(decrypt(sessionData, bypassLogin)); } else { @@ -934,7 +942,7 @@ export class GameData { const battleType = sessionData.battleType || 0; const trainerConfig = sessionData.trainer ? trainerConfigs[sessionData.trainer.trainerType] : null; - const battle = scene.newBattle(sessionData.waveIndex, battleType, sessionData.trainer, battleType === BattleType.TRAINER ? trainerConfig?.doubleOnly || sessionData.trainer?.variant === TrainerVariant.DOUBLE : sessionData.enemyParty.length > 1); + const battle = scene.newBattle(sessionData.waveIndex, battleType, sessionData.trainer, battleType === BattleType.TRAINER ? trainerConfig?.doubleOnly || sessionData.trainer?.variant === TrainerVariant.DOUBLE : sessionData.enemyParty.length > 1)!; // TODO: is this bang correct? battle.enemyLevels = sessionData.enemyParty.map(p => p.level); scene.arena.init(); @@ -950,10 +958,10 @@ export class GameData { }); scene.arena.weather = sessionData.arena.weather; - scene.arena.eventTarget.dispatchEvent(new WeatherChangedEvent(null, scene.arena.weather?.weatherType, scene.arena.weather?.turnsLeft)); + scene.arena.eventTarget.dispatchEvent(new WeatherChangedEvent(WeatherType.NONE, scene.arena.weather?.weatherType!, scene.arena.weather?.turnsLeft!)); // TODO: is this bang correct? scene.arena.terrain = sessionData.arena.terrain; - scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(null, scene.arena.terrain?.terrainType, scene.arena.terrain?.turnsLeft)); + scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(TerrainType.NONE, scene.arena.terrain?.terrainType!, scene.arena.terrain?.turnsLeft!)); // TODO: is this bang correct? // TODO //scene.arena.tags = sessionData.arena.tags; @@ -983,7 +991,7 @@ export class GameData { initSessionFromData(sessionData); } else { this.getSession(slotId) - .then(data => initSessionFromData(data)) + .then(data => data && initSessionFromData(data)) .catch(err => { reject(err); return; @@ -999,7 +1007,7 @@ export class GameData { deleteSession(slotId: integer): Promise { return new Promise(resolve => { if (bypassLogin) { - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser.username}`); + localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); return resolve(true); } @@ -1009,8 +1017,8 @@ export class GameData { } Utils.apiFetch(`savedata/session/delete?slot=${slotId}&clientSessionId=${clientSessionId}`, true).then(response => { if (response.ok) { - loggedInUser.lastSessionSlot = -1; - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser.username}`); + loggedInUser!.lastSessionSlot = -1; // TODO: is the bang correct? + localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); resolve(true); } return response.text(); @@ -1040,7 +1048,7 @@ export class GameData { if (sessionData.gameMode === GameModes.DAILY) { if (localStorage.hasOwnProperty("daily")) { - daily = JSON.parse(atob(localStorage.getItem("daily"))); + daily = JSON.parse(atob(localStorage.getItem("daily")!)); // TODO: is this bang correct? if (daily.includes(seed)) { return resolve(false); } else { @@ -1062,7 +1070,7 @@ export class GameData { tryClearSession(scene: BattleScene, slotId: integer): Promise<[success: boolean, newClear: boolean]> { return new Promise<[boolean, boolean]>(resolve => { if (bypassLogin) { - localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`); + localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); return resolve([true, true]); } @@ -1073,8 +1081,8 @@ export class GameData { const sessionData = this.getSessionSaveData(scene); Utils.apiPost(`savedata/session/clear?slot=${slotId}&trainerId=${this.trainerId}&secretId=${this.secretId}&clientSessionId=${clientSessionId}`, JSON.stringify(sessionData), undefined, true).then(response => { if (response.ok) { - loggedInUser.lastSessionSlot = -1; - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser.username}`); + loggedInUser!.lastSessionSlot = -1; // TODO: is the bang correct? + localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); } return response.json(); }).then(jsonResponse => { @@ -1161,10 +1169,10 @@ export class GameData { if (sync) { this.scene.ui.savingIcon.show(); } - const sessionData = useCachedSession ? this.parseSessionData(decrypt(localStorage.getItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser.username}`), bypassLogin)) : this.getSessionSaveData(scene); + const sessionData = useCachedSession ? this.parseSessionData(decrypt(localStorage.getItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser?.username}`)!, bypassLogin)) : this.getSessionSaveData(scene); // TODO: is this bang correct? const maxIntAttrValue = 0x80000000; - const systemData = useCachedSystem ? this.parseSystemData(decrypt(localStorage.getItem(`data_${loggedInUser.username}`), bypassLogin)) : this.getSystemSaveData(); + const systemData = useCachedSystem ? this.parseSystemData(decrypt(localStorage.getItem(`data_${loggedInUser?.username}`)!, bypassLogin)) : this.getSystemSaveData(); // TODO: is this bang correct? const request = { system: systemData, @@ -1173,9 +1181,9 @@ export class GameData { clientSessionId: clientSessionId }; - localStorage.setItem(`data_${loggedInUser.username}`, encrypt(JSON.stringify(systemData, (k: any, v: any) => typeof v === "bigint" ? v <= maxIntAttrValue ? Number(v) : v.toString() : v), bypassLogin)); + localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(JSON.stringify(systemData, (k: any, v: any) => typeof v === "bigint" ? v <= maxIntAttrValue ? Number(v) : v.toString() : v), bypassLogin)); - localStorage.setItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser.username}`, encrypt(JSON.stringify(sessionData), bypassLogin)); + localStorage.setItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser?.username}`, encrypt(JSON.stringify(sessionData), bypassLogin)); console.debug("Session data saved"); @@ -1212,7 +1220,7 @@ export class GameData { public tryExportData(dataType: GameDataType, slotId: integer = 0): Promise { return new Promise(resolve => { - const dataKey: string = `${getDataTypeKey(dataType, slotId)}_${loggedInUser.username}`; + const dataKey: string = `${getDataTypeKey(dataType, slotId)}_${loggedInUser?.username}`; const handleData = (dataStr: string) => { switch (dataType) { case GameDataType.SYSTEM: @@ -1251,7 +1259,7 @@ export class GameData { } public importData(dataType: GameDataType, slotId: integer = 0): void { - const dataKey = `${getDataTypeKey(dataType, slotId)}_${loggedInUser.username}`; + const dataKey = `${getDataTypeKey(dataType, slotId)}_${loggedInUser?.username}`; let saveFile: any = document.getElementById("saveFile"); if (saveFile) { @@ -1270,7 +1278,7 @@ export class GameData { reader.onload = (_ => { return e => { let dataName: string; - let dataStr = AES.decrypt(e.target.result.toString(), saveKey).toString(enc.Utf8); + let dataStr = AES.decrypt(e.target?.result?.toString()!, saveKey).toString(enc.Utf8); // TODO: is this bang correct? let valid = false; try { dataName = GameDataType[dataType].toLowerCase(); @@ -1293,10 +1301,11 @@ export class GameData { console.error(ex); } - const displayError = (error: string) => this.scene.ui.showText(error, null, () => this.scene.ui.showText(null, 0), Utils.fixedInt(1500)); + const displayError = (error: string) => this.scene.ui.showText(error, null, () => this.scene.ui.showText("", 0), Utils.fixedInt(1500)); + dataName = dataName!; // tell TS compiler that dataName is defined! if (!valid) { - return this.scene.ui.showText(`Your ${dataName} data could not be loaded. It may be corrupted.`, null, () => this.scene.ui.showText(null, 0), Utils.fixedInt(1500)); + return this.scene.ui.showText(`Your ${dataName} data could not be loaded. It may be corrupted.`, null, () => this.scene.ui.showText("", 0), Utils.fixedInt(1500)); } this.scene.ui.showText(`Your ${dataName} data will be overridden and the page will reload. Proceed?`, null, () => { @@ -1329,7 +1338,7 @@ export class GameData { } }, () => { this.scene.ui.revertMode(); - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); }, false, -98); }); }; @@ -1652,7 +1661,7 @@ export class GameData { return dexAttr & DexAttr.SHINY ? dexAttr & DexAttr.VARIANT_3 ? 3 : dexAttr & DexAttr.VARIANT_2 ? 2 : 1 : 0; } - getNaturesForAttr(natureAttr: integer): Nature[] { + getNaturesForAttr(natureAttr: integer = 0): Nature[] { const ret: Nature[] = []; for (let n = 0; n < 25; n++) { if (natureAttr & (1 << (n + 1))) { @@ -1707,7 +1716,7 @@ export class GameData { entry.hatchedCount = 0; } if (!entry.hasOwnProperty("natureAttr") || (entry.caughtAttr && !entry.natureAttr)) { - entry.natureAttr = this.defaultDexData[k].natureAttr || (1 << Utils.randInt(25, 1)); + entry.natureAttr = this.defaultDexData?.[k].natureAttr || (1 << Utils.randInt(25, 1)); } } } diff --git a/src/system/modifier-data.ts b/src/system/modifier-data.ts index 6f169280da1..0f3e28fe11c 100644 --- a/src/system/modifier-data.ts +++ b/src/system/modifier-data.ts @@ -1,6 +1,6 @@ import BattleScene from "../battle-scene"; import { PersistentModifier } from "../modifier/modifier"; -import { GeneratedPersistentModifierType, ModifierTypeGenerator, getModifierTypeFuncById } from "../modifier/modifier-type"; +import { GeneratedPersistentModifierType, ModifierType, ModifierTypeGenerator, getModifierTypeFuncById } from "../modifier/modifier-type"; export default class ModifierData { private player: boolean; @@ -27,14 +27,14 @@ export default class ModifierData { this.className = sourceModifier ? sourceModifier.constructor.name : source.className; } - toModifier(scene: BattleScene, constructor: any): PersistentModifier { + toModifier(scene: BattleScene, constructor: any): PersistentModifier | null { const typeFunc = getModifierTypeFuncById(this.typeId); if (!typeFunc) { return null; } try { - let type = typeFunc(); + let type: ModifierType | null = typeFunc(); type.id = this.typeId; if (type instanceof ModifierTypeGenerator) { diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index ca072c9eec8..8f094379434 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -33,8 +33,8 @@ export default class PokemonData { public ivs: integer[]; public nature: Nature; public natureOverride: Nature | -1; - public moveset: PokemonMove[]; - public status: Status; + public moveset: (PokemonMove | null)[]; + public status: Status | null; public friendship: integer; public metLevel: integer; public metBiome: Biome | -1; @@ -117,7 +117,7 @@ export default class PokemonData { if (!forHistory) { this.status = source.status ? new Status(source.status.effect, source.status.turnCount, source.status.cureTurn) - : undefined; + : null; } this.summonData = new PokemonSummonData(); diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index af27613136c..3f9b906d1cf 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -79,6 +79,7 @@ export const SettingKeys = { Skip_Seen_Dialogues: "SKIP_SEEN_DIALOGUES", Battle_Style: "BATTLE_STYLE", Enable_Retries: "ENABLE_RETRIES", + Hide_IVs: "HIDE_IVS", Tutorials: "TUTORIALS", Touch_Controls: "TOUCH_CONTROLS", Vibration: "VIBRATION", @@ -250,6 +251,13 @@ export const Setting: Array = [ default: 0, type: SettingType.GENERAL }, + { + key: SettingKeys.Hide_IVs, + label: i18next.t("settings:hideIvs"), + options: OFF_ON, + default: 0, + type: SettingType.GENERAL + }, { key: SettingKeys.Tutorials, label: i18next.t("settings:tutorials"), @@ -618,6 +626,9 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Enable_Retries: scene.enableRetries = Setting[index].options[value].value === "On"; break; + case SettingKeys.Hide_IVs: + scene.hideIvs = Setting[index].options[value].value === "On"; + break; case SettingKeys.Skip_Seen_Dialogues: scene.skipSeenDialogues = Setting[index].options[value].value === "On"; break; @@ -763,6 +774,14 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): label: "한국어", handler: () => changeLocaleHandler("ko") }, + { + label: "日本語", + handler: () => changeLocaleHandler("ja") + }, + { + label: "Català", + handler: () => changeLocaleHandler("ca-ES") + }, { label: i18next.t("settings:back"), handler: () => cancelHandler() diff --git a/src/system/unlockables.ts b/src/system/unlockables.ts index d8e7d97edf6..983909373fd 100644 --- a/src/system/unlockables.ts +++ b/src/system/unlockables.ts @@ -1,9 +1,11 @@ +import i18next from "i18next"; import { GameMode, GameModes } from "../game-mode"; export enum Unlockables { ENDLESS_MODE, MINI_BLACK_HOLE, - SPLICED_ENDLESS_MODE + SPLICED_ENDLESS_MODE, + EVIOLITE } export function getUnlockableName(unlockable: Unlockables) { @@ -11,8 +13,10 @@ export function getUnlockableName(unlockable: Unlockables) { case Unlockables.ENDLESS_MODE: return `${GameMode.getModeName(GameModes.ENDLESS)} Mode`; case Unlockables.MINI_BLACK_HOLE: - return "Mini Black Hole"; + return i18next.t("modifierType:ModifierType.MINI_BLACK_HOLE.name"); case Unlockables.SPLICED_ENDLESS_MODE: return `${GameMode.getModeName(GameModes.SPLICED_ENDLESS)} Mode`; + case Unlockables.EVIOLITE: + return i18next.t("modifierType:ModifierType.EVIOLITE.name"); } } diff --git a/src/system/voucher.ts b/src/system/voucher.ts index 2238a95e690..0c71e3c0286 100644 --- a/src/system/voucher.ts +++ b/src/system/voucher.ts @@ -3,6 +3,7 @@ import i18next from "i18next"; import { Achv, AchvTier, achvs, getAchievementDescription } from "./achv"; import { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; +import { ConditionFn } from "#app/@types/common.js"; export enum VoucherType { REGULAR, @@ -16,15 +17,15 @@ export class Voucher { public voucherType: VoucherType; public description: string; - private conditionFunc: (scene: BattleScene, args: any[]) => boolean; + private conditionFunc: ConditionFn | undefined; - constructor(voucherType: VoucherType, description: string, conditionFunc?: (scene: BattleScene, args: any[]) => boolean) { + constructor(voucherType: VoucherType, description: string, conditionFunc?: ConditionFn) { this.description = description; this.voucherType = voucherType; this.conditionFunc = conditionFunc; } - validate(scene: BattleScene, args: any[]): boolean { + validate(scene: BattleScene, args?: any[]): boolean { return !this.conditionFunc || this.conditionFunc(scene, args); } @@ -105,7 +106,7 @@ export function initVouchers() { } const bossTrainerTypes = Object.keys(trainerConfigs) - .filter(tt => trainerConfigs[tt].isBoss && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL); + .filter(tt => trainerConfigs[tt].isBoss && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL && trainerConfigs[tt].hasVoucher); for (const trainerType of bossTrainerTypes) { const voucherType = trainerConfigs[trainerType].moneyMultiplier < 10 diff --git a/src/test/abilities/ability_timing.test.ts b/src/test/abilities/ability_timing.test.ts index 0004479ed8c..bb025d7fc53 100644 --- a/src/test/abilities/ability_timing.test.ts +++ b/src/test/abilities/ability_timing.test.ts @@ -1,13 +1,13 @@ import { CommandPhase, MessagePhase, TurnInitPhase } from "#app/phases"; import i18next, { initI18n } from "#app/plugins/i18n"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Ability Timing", () => { diff --git a/src/test/abilities/aura_break.test.ts b/src/test/abilities/aura_break.test.ts index 918905fc00a..a34475cb1ad 100644 --- a/src/test/abilities/aura_break.test.ts +++ b/src/test/abilities/aura_break.test.ts @@ -1,13 +1,13 @@ import { allMoves } from "#app/data/move.js"; import { MoveEffectPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Aura Break", () => { let phaserGame: Phaser.Game; diff --git a/src/test/abilities/battery.test.ts b/src/test/abilities/battery.test.ts index 9c3248f39c0..2345e63d987 100644 --- a/src/test/abilities/battery.test.ts +++ b/src/test/abilities/battery.test.ts @@ -1,13 +1,13 @@ import { allMoves } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Battery", () => { let phaserGame: Phaser.Game; diff --git a/src/test/abilities/battle_bond.test.ts b/src/test/abilities/battle_bond.test.ts index ce41f9b6603..1a5c71b4c15 100644 --- a/src/test/abilities/battle_bond.test.ts +++ b/src/test/abilities/battle_bond.test.ts @@ -46,12 +46,12 @@ describe("Abilities - BATTLE BOND", () => { await game.startBattle([Species.MAGIKARP, Species.GRENINJA]); const greninja = game.scene.getParty().find((p) => p.species.speciesId === Species.GRENINJA); - expect(greninja).not.toBe(undefined); - expect(greninja.formIndex).toBe(ashForm); + expect(greninja).toBeDefined(); + expect(greninja!.formIndex).toBe(ashForm); - greninja.hp = 0; - greninja.status = new Status(StatusEffect.FAINT); - expect(greninja.isFainted()).toBe(true); + greninja!.hp = 0; + greninja!.status = new Status(StatusEffect.FAINT); + expect(greninja!.isFainted()).toBe(true); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.doKillOpponents(); @@ -59,7 +59,7 @@ describe("Abilities - BATTLE BOND", () => { game.doSelectModifier(); await game.phaseInterceptor.to(QuietFormChangePhase); - expect(greninja.formIndex).toBe(baseForm); + expect(greninja!.formIndex).toBe(baseForm); }, TIMEOUT ); diff --git a/src/test/abilities/costar.test.ts b/src/test/abilities/costar.test.ts index 1d58d899565..ef3fb3a2ab0 100644 --- a/src/test/abilities/costar.test.ts +++ b/src/test/abilities/costar.test.ts @@ -5,9 +5,9 @@ import { Species } from "#app/enums/species.js"; import { CommandPhase, MessagePhase } from "#app/phases.js"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -42,8 +42,6 @@ describe("Abilities - COSTAR", () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]); let [leftPokemon, rightPokemon] = game.scene.getPlayerField(); - expect(leftPokemon).toBeDefined(); - expect(rightPokemon).toBeDefined(); game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT)); await game.phaseInterceptor.to(CommandPhase); @@ -73,8 +71,6 @@ describe("Abilities - COSTAR", () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]); let [leftPokemon, rightPokemon] = game.scene.getPlayerField(); - expect(leftPokemon).toBeDefined(); - expect(rightPokemon).toBeDefined(); expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index 3a6fd540d27..09a0dbf7f8a 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -47,11 +47,11 @@ describe("Abilities - DISGUISE", () => { const mimikyu = game.scene.getParty().find((p) => p.species.speciesId === Species.MIMIKYU); expect(mimikyu).not.toBe(undefined); - expect(mimikyu.formIndex).toBe(bustedForm); + expect(mimikyu!.formIndex).toBe(bustedForm); - mimikyu.hp = 0; - mimikyu.status = new Status(StatusEffect.FAINT); - expect(mimikyu.isFainted()).toBe(true); + mimikyu!.hp = 0; + mimikyu!.status = new Status(StatusEffect.FAINT); + expect(mimikyu!.isFainted()).toBe(true); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.doKillOpponents(); @@ -59,7 +59,7 @@ describe("Abilities - DISGUISE", () => { game.doSelectModifier(); await game.phaseInterceptor.to(QuietFormChangePhase); - expect(mimikyu.formIndex).toBe(baseForm); + expect(mimikyu!.formIndex).toBe(baseForm); }, TIMEOUT ); @@ -80,17 +80,17 @@ describe("Abilities - DISGUISE", () => { await game.startBattle([Species.MIMIKYU]); - const mimikyu = game.scene.getPlayerPokemon(); - const damage = (Math.floor(mimikyu.getMaxHp()/8)); + const mimikyu = game.scene.getPlayerPokemon()!; + const damage = (Math.floor(mimikyu!.getMaxHp()/8)); expect(mimikyu).not.toBe(undefined); - expect(mimikyu.formIndex).toBe(baseForm); + expect(mimikyu!.formIndex).toBe(baseForm); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.phaseInterceptor.to(TurnEndPhase); - expect(mimikyu.formIndex).toBe(bustedForm); - expect(game.scene.getEnemyPokemon().turnData.currDamageDealt).toBe(damage); + expect(mimikyu!.formIndex).toBe(bustedForm); + expect(game.scene.getEnemyPokemon()!.turnData.currDamageDealt).toBe(damage); }, TIMEOUT ); diff --git a/src/test/abilities/dry_skin.test.ts b/src/test/abilities/dry_skin.test.ts index 5434974c583..20b85eab767 100644 --- a/src/test/abilities/dry_skin.test.ts +++ b/src/test/abilities/dry_skin.test.ts @@ -1,12 +1,12 @@ import { Species } from "#app/enums/species.js"; import { TurnEndPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Dry Skin", () => { let phaserGame: Phaser.Game; @@ -38,7 +38,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); // first turn @@ -59,7 +59,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); enemy.hp = 1; @@ -82,8 +82,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); - expect(enemy).toBeDefined(); + const enemy = game.scene.getEnemyPokemon()!; const initialHP = 1000; enemy.hp = initialHP; @@ -109,7 +108,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); enemy.hp = 1; @@ -124,7 +123,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); enemy.hp = 1; @@ -140,7 +139,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); enemy.hp = 1; diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts new file mode 100644 index 00000000000..b64cbe2ef10 --- /dev/null +++ b/src/test/abilities/gulp_missile.test.ts @@ -0,0 +1,266 @@ +import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { + MoveEndPhase, + TurnEndPhase, + TurnStartPhase, +} from "#app/phases"; +import GameManager from "#app/test/utils/gameManager"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; +import { BattleStat } from "#app/data/battle-stat.js"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import { GulpMissileTag } from "#app/data/battler-tags.js"; +import Pokemon from "#app/field/pokemon.js"; + +describe("Abilities - Gulp Missile", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + const NORMAL_FORM = 0; + const GULPING_FORM = 1; + const GORGING_FORM = 2; + + /** + * Gets the effect damage of Gulp Missile + * See Gulp Missile {@link https://bulbapedia.bulbagarden.net/wiki/Gulp_Missile_(Ability)} + * @param {Pokemon} pokemon The pokemon taking the effect damage. + * @returns The effect damage of Gulp Missile + */ + const getEffectDamage = (pokemon: Pokemon): number => { + return Math.max(1, Math.floor(pokemon.getMaxHp() * 1/4)); + }; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .moveset([Moves.SURF, Moves.DIVE, Moves.SPLASH]) + .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(SPLASH_ONLY) + .enemyLevel(5); + }); + + it("changes to Gulping Form if HP is over half when Surf or Dive is used", async () => { + await game.startBattle([Species.CRAMORANT]); + const cramorant = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + await game.toNextTurn(); + game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.getHpRatio()).toBeGreaterThanOrEqual(.5); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + }); + + it("changes to Gorging Form if HP is under half when Surf or Dive is used", async () => { + await game.startBattle([Species.CRAMORANT]); + const cramorant = game.scene.getPlayerPokemon()!; + + vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.49); + expect(cramorant.getHpRatio()).toBe(.49); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined(); + expect(cramorant.formIndex).toBe(GORGING_FORM); + }); + + it("deals ¼ of the attacker's maximum HP when hit by a damaging attack", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + await game.startBattle([Species.CRAMORANT]); + + const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy, "damageAndUpdate"); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); + }); + + it("does not have any effect when hit by non-damaging attack", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.TAIL_WHIP)); + await game.startBattle([Species.CRAMORANT]); + + const cramorant = game.scene.getPlayerPokemon()!; + vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + }); + + it("lowers the attacker's Defense by 1 stage when hit in Gulping form", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + await game.startBattle([Species.CRAMORANT]); + + const cramorant = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy, "damageAndUpdate"); + vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); + expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(-1); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined(); + expect(cramorant.formIndex).toBe(NORMAL_FORM); + }); + + it("paralyzes the enemy when hit in Gorging form", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + await game.startBattle([Species.CRAMORANT]); + + const cramorant = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy, "damageAndUpdate"); + vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.45); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined(); + expect(cramorant.formIndex).toBe(GORGING_FORM); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); + expect(enemy.status?.effect).toBe(StatusEffect.PARALYSIS); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeUndefined(); + expect(cramorant.formIndex).toBe(NORMAL_FORM); + }); + + it("does not activate the ability when underwater", async () => { + game.override + .enemyMoveset(Array(4).fill(Moves.SURF)) + .enemySpecies(Species.REGIELEKI) + .enemyAbility(Abilities.BALL_FETCH) + .enemyLevel(5); + await game.startBattle([Species.CRAMORANT]); + + const cramorant = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + await game.toNextTurn(); + + // Turn 2 underwater, enemy moves first + game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.formIndex).toBe(NORMAL_FORM); + expect(cramorant.getTag(GulpMissileTag)).toBeUndefined(); + + // Turn 2 Cramorant comes out and changes form + await game.phaseInterceptor.to(TurnEndPhase); + expect(cramorant.formIndex).not.toBe(NORMAL_FORM); + expect(cramorant.getTag(GulpMissileTag)).toBeDefined(); + }); + + it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)).enemyAbility(Abilities.MAGIC_GUARD); + await game.startBattle([Species.CRAMORANT]); + + const cramorant = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + await game.phaseInterceptor.to(MoveEndPhase); + const enemyHpPreEffect = enemy.hp; + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(enemy.hp).toBe(enemyHpPreEffect); + expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(-1); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined(); + expect(cramorant.formIndex).toBe(NORMAL_FORM); + }); + + it("cannot be suppressed", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID)); + await game.startBattle([Species.CRAMORANT]); + + const cramorant = game.scene.getPlayerPokemon()!; + vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(cramorant.hasAbility(Abilities.GULP_MISSILE)).toBe(true); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + }); + + it("cannot be swapped with another ability", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.SKILL_SWAP)); + await game.startBattle([Species.CRAMORANT]); + + const cramorant = game.scene.getPlayerPokemon()!; + vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(cramorant.hasAbility(Abilities.GULP_MISSILE)).toBe(true); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + }); + + it("cannot be copied", async () => { + game.override.enemyAbility(Abilities.TRACE); + + await game.startBattle([Species.CRAMORANT]); + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.phaseInterceptor.to(TurnStartPhase); + + expect(game.scene.getEnemyPokemon()?.hasAbility(Abilities.GULP_MISSILE)).toBe(false); + }); +}); diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index 6d78846071d..80a71e54d0b 100644 --- a/src/test/abilities/hustle.test.ts +++ b/src/test/abilities/hustle.test.ts @@ -2,13 +2,13 @@ import { allMoves } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { Stat } from "#app/enums/stat.js"; import { DamagePhase, MoveEffectPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Hustle", () => { let phaserGame: Phaser.Game; @@ -38,14 +38,13 @@ describe("Abilities - Hustle", () => { it("increases the user's Attack stat by 50%", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; const atk = pikachu.stats[Stat.ATK]; vi.spyOn(pikachu, "getBattleStat"); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); expect(pikachu.getBattleStat).toHaveReturnedWith(atk * 1.5); @@ -53,7 +52,7 @@ describe("Abilities - Hustle", () => { it("lowers the accuracy of the user's physical moves by 20%", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; vi.spyOn(pikachu, "getAccuracyMultiplier"); @@ -65,7 +64,7 @@ describe("Abilities - Hustle", () => { it("does not affect non-physical moves", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; const spatk = pikachu.stats[Stat.SPATK]; vi.spyOn(pikachu, "getBattleStat"); @@ -83,8 +82,8 @@ describe("Abilities - Hustle", () => { game.override.enemyLevel(30); await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; vi.spyOn(pikachu, "getAccuracyMultiplier"); vi.spyOn(allMoves[Moves.FISSURE], "calculateBattleAccuracy"); diff --git a/src/test/abilities/ice_face.test.ts b/src/test/abilities/ice_face.test.ts index ed04aa8bac2..cdf8d5928ee 100644 --- a/src/test/abilities/ice_face.test.ts +++ b/src/test/abilities/ice_face.test.ts @@ -1,12 +1,7 @@ import { QuietFormChangePhase } from "#app/form-change-phase"; -import { - MoveEffectPhase, - MoveEndPhase, - TurnEndPhase, - TurnInitPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { MoveEffectPhase, MoveEndPhase, TurnEndPhase, TurnInitPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; @@ -45,11 +40,11 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.isFullHp()).toBe(true); expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); }); it("takes no damage from the first hit of multihit physical move and transforms to Noice", async () => { @@ -59,7 +54,7 @@ describe("Abilities - Ice Face", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.SURGING_STRIKES)); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); // First hit @@ -87,7 +82,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -101,7 +96,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -117,15 +112,15 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.isFullHp()).toBe(true); expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); await game.phaseInterceptor.to(MoveEndPhase); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBeNull(); expect(eiscue.formIndex).toBe(icefaceForm); }); @@ -138,9 +133,9 @@ describe("Abilities - Ice Face", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.SNOWSCAPE)); await game.phaseInterceptor.to(TurnEndPhase); - let eiscue = game.scene.getPlayerPokemon(); + let eiscue = game.scene.getPlayerPokemon()!; - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); expect(eiscue.formIndex).toBe(noiceForm); expect(eiscue.isFullHp()).toBe(true); @@ -150,7 +145,7 @@ describe("Abilities - Ice Face", () => { game.doSwitchPokemon(1); await game.phaseInterceptor.to(QuietFormChangePhase); - eiscue = game.scene.getPlayerPokemon(); + eiscue = game.scene.getPlayerPokemon()!; expect(eiscue.formIndex).toBe(icefaceForm); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); @@ -163,17 +158,17 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE]); game.doAttack(getMovePosition(game.scene, 0, Moves.HAIL)); - const eiscue = game.scene.getPlayerPokemon(); + const eiscue = game.scene.getPlayerPokemon()!; await game.phaseInterceptor.to(QuietFormChangePhase); expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); await game.phaseInterceptor.to(TurnEndPhase); expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); }); it("persists form change when switched out", async () => { @@ -184,9 +179,9 @@ describe("Abilities - Ice Face", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); await game.phaseInterceptor.to(TurnEndPhase); - let eiscue = game.scene.getPlayerPokemon(); + let eiscue = game.scene.getPlayerPokemon()!; - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); expect(eiscue.formIndex).toBe(noiceForm); expect(eiscue.isFullHp()).toBe(true); @@ -197,7 +192,7 @@ describe("Abilities - Ice Face", () => { eiscue = game.scene.getParty()[1]; expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); }); it("reverts to Ice Face on arena reset", async () => { @@ -210,10 +205,10 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE]); - const eiscue = game.scene.getPlayerPokemon(); + const eiscue = game.scene.getPlayerPokemon()!; expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); await game.doKillOpponents(); @@ -234,7 +229,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(TurnEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -250,7 +245,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(TurnEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -266,10 +261,10 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(TurnInitPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); - expect(game.scene.getPlayerPokemon().hasAbility(Abilities.TRACE)).toBe(true); + expect(game.scene.getPlayerPokemon()!.hasAbility(Abilities.TRACE)).toBe(true); }); }); diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index 39683b956ec..15d9e87d65b 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -41,8 +41,8 @@ describe("Abilities - Illusion", () => { it("create illusion at the start", async () => { await game.startBattle([Species.ZOROARK, Species.AXEW]); - const zoroark = game.scene.getPlayerPokemon(); - const zorua = game.scene.getEnemyPokemon(); + const zoroark = game.scene.getPlayerPokemon()!; + const zorua = game.scene.getEnemyPokemon()!; expect(zoroark.illusion.active).equals(true); expect(zorua.illusion.active).equals(true); @@ -56,8 +56,8 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to(TurnEndPhase); - const zoroark = game.scene.getPlayerPokemon(); - const zorua = game.scene.getEnemyPokemon(); + const zoroark = game.scene.getPlayerPokemon()!; + const zorua = game.scene.getEnemyPokemon()!; expect(zorua.illusion.active).equals(false); expect(zoroark.illusion.active).equals(false); @@ -67,7 +67,7 @@ describe("Abilities - Illusion", () => { vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NEUTRALIZING_GAS); await game.startBattle([Species.KOFFING]); - const zorua = game.scene.getEnemyPokemon(); + const zorua = game.scene.getEnemyPokemon()!; expect(zorua.illusion.active).equals(false); }); @@ -76,11 +76,11 @@ describe("Abilities - Illusion", () => { vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([Species.ZOROARK, Species.AXEW]); - const enemy = game.scene.getEnemyPokemon(); - const zoroark = game.scene.getPlayerPokemon(); + const enemy = game.scene.getEnemyPokemon()!; + const zoroark = game.scene.getPlayerPokemon()!; - const flameThwowerEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[0], false, true); - const psychicEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[1], false, true); + const flameThwowerEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[0]!, false, true); + const psychicEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[1]!, false, true); expect(psychicEffectiveness).above(flameThwowerEffectiveness); }); @@ -97,7 +97,7 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to(TurnEndPhase); - const zoroark = game.scene.getPlayerPokemon(); + const zoroark = game.scene.getPlayerPokemon()!; expect(zoroark.illusion.active).equals(true); }); @@ -107,7 +107,7 @@ describe("Abilities - Illusion", () => { await game.startBattle([Species.ABRA, Species.ZOROARK, Species.AXEW]); - const axew = game.scene.getParty().at(2); + const axew = game.scene.getParty().at(2)!; axew.shiny = true; axew.nickname = btoa(unescape(encodeURIComponent("axew nickname"))); axew.gender = Gender.FEMALE; @@ -117,7 +117,7 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to(TurnEndPhase); - const zoroark = game.scene.getPlayerPokemon(); + const zoroark = game.scene.getPlayerPokemon()!; expect(zoroark.name).equals("Axew"); expect(zoroark.getNameToRender()).equals("axew nickname"); expect(zoroark.getGender(false, true)).equals(Gender.FEMALE); diff --git a/src/test/abilities/intimidate.test.ts b/src/test/abilities/intimidate.test.ts index 3ed7a01b98e..2c2b68bc5df 100644 --- a/src/test/abilities/intimidate.test.ts +++ b/src/test/abilities/intimidate.test.ts @@ -1,22 +1,17 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import Overrides from "#app/overrides"; +import GameManager from "#test/utils/gameManager"; import { Mode } from "#app/ui/ui"; import { BattleStat } from "#app/data/battle-stat"; -import { generateStarter, getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { generateStarter, getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Status, StatusEffect } from "#app/data/status-effect"; import { GameModes, getGameMode } from "#app/game-mode"; -import { - CommandPhase, DamagePhase, EncounterPhase, - EnemyCommandPhase, SelectStarterPhase, - TurnInitPhase, -} from "#app/phases"; +import { CommandPhase, DamagePhase, EncounterPhase, EnemyCommandPhase, SelectStarterPhase, TurnInitPhase } from "#app/phases"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { removeEnemyHeldItems } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Intimidate", () => { let phaserGame: Phaser.Game; @@ -40,12 +35,11 @@ describe("Abilities - Intimidate", () => { game.override.enemyPassiveAbility(Abilities.HYDRATION); game.override.ability(Abilities.INTIMIDATE); game.override.startingWave(3); - game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); + game.override.enemyMoveset(SPLASH_ONLY); }); it("single - wild with switch", async () => { await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); game.onNextPrompt( "CheckSwitchPhase", Mode.CONFIRM, @@ -76,7 +70,6 @@ describe("Abilities - Intimidate", () => { it("single - boss should only trigger once then switch", async () => { game.override.startingWave(10); await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); game.onNextPrompt( "CheckSwitchPhase", Mode.CONFIRM, @@ -106,7 +99,6 @@ describe("Abilities - Intimidate", () => { it("single - trainer should only trigger once with switch", async () => { game.override.startingWave(5); await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); game.onNextPrompt( "CheckSwitchPhase", Mode.CONFIRM, @@ -137,7 +129,6 @@ describe("Abilities - Intimidate", () => { game.override.battleType("double"); game.override.startingWave(5); await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); game.onNextPrompt( "CheckSwitchPhase", Mode.CONFIRM, @@ -164,7 +155,6 @@ describe("Abilities - Intimidate", () => { game.override.battleType("double"); game.override.startingWave(3); await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); game.onNextPrompt( "CheckSwitchPhase", Mode.CONFIRM, @@ -191,7 +181,6 @@ describe("Abilities - Intimidate", () => { game.override.battleType("double"); game.override.startingWave(10); await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); game.onNextPrompt( "CheckSwitchPhase", Mode.CONFIRM, @@ -218,7 +207,6 @@ describe("Abilities - Intimidate", () => { game.override.startingWave(2); game.override.moveset([Moves.AERIAL_ACE]); await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -245,7 +233,6 @@ describe("Abilities - Intimidate", () => { game.override.startingWave(2); game.override.moveset([Moves.SPLASH]); await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -271,7 +258,6 @@ describe("Abilities - Intimidate", () => { game.override.enemyMoveset([Moves.VOLT_SWITCH, Moves.VOLT_SWITCH, Moves.VOLT_SWITCH, Moves.VOLT_SWITCH]); game.override.startingWave(5); await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -308,10 +294,9 @@ describe("Abilities - Intimidate", () => { it("single - trainer should only trigger once whatever turn we are", async () => { game.override.moveset([Moves.SPLASH]); - game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); + game.override.enemyMoveset(SPLASH_ONLY); game.override.startingWave(5); await game.startBattle([Species.MIGHTYENA, Species.POOCHYENA]); - removeEnemyHeldItems(game.scene); let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -349,9 +334,7 @@ describe("Abilities - Intimidate", () => { it("double - wild vs only 1 on player side", async () => { game.override.battleType("double"); game.override.startingWave(3); - vi.spyOn(Overrides, "OPP_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{ name: "COIN_CASE" }]); await game.runToSummon([Species.MIGHTYENA]); - removeEnemyHeldItems(game.scene); await game.phaseInterceptor.to(CommandPhase, false); const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); @@ -378,7 +361,6 @@ describe("Abilities - Intimidate", () => { }); await game.phaseInterceptor.run(EncounterPhase); - removeEnemyHeldItems(game.scene); await game.phaseInterceptor.to(CommandPhase, false); const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; diff --git a/src/test/abilities/intrepid_sword.test.ts b/src/test/abilities/intrepid_sword.test.ts index 5292bcd7c82..bc83c9bb44b 100644 --- a/src/test/abilities/intrepid_sword.test.ts +++ b/src/test/abilities/intrepid_sword.test.ts @@ -1,8 +1,6 @@ import { BattleStat } from "#app/data/battle-stat"; -import { - CommandPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; +import { CommandPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; import Phaser from "phaser"; diff --git a/src/test/abilities/libero.test.ts b/src/test/abilities/libero.test.ts index 58e67a6ebe6..6046df98243 100644 --- a/src/test/abilities/libero.test.ts +++ b/src/test/abilities/libero.test.ts @@ -2,7 +2,7 @@ import { allMoves } from "#app/data/move.js"; import { Type } from "#app/data/type.js"; import { Weather, WeatherType } from "#app/data/weather.js"; import { PlayerPokemon } from "#app/field/pokemon.js"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; +import { TurnEndPhase } from "#app/phases.js"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; @@ -10,9 +10,9 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -46,7 +46,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -64,7 +64,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP, Species.BULBASAUR]); - let leadPokemon = game.scene.getPlayerPokemon(); + let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -86,7 +86,7 @@ describe("Abilities - Protean", () => { game.doSwitchPokemon(1); await game.toNextTurn(); - leadPokemon = game.scene.getPlayerPokemon(); + leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -104,7 +104,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); @@ -128,7 +128,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -150,7 +150,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; @@ -169,7 +169,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.DIG)); @@ -188,15 +188,14 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false); + await mockHitCheck(game, false); await game.phaseInterceptor.to(TurnEndPhase); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon.isFullHp()).toBe(true); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, @@ -211,7 +210,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -230,7 +229,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -248,7 +247,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [allMoves[Moves.SPLASH].defaultType]; @@ -267,7 +266,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); @@ -287,7 +286,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); @@ -305,7 +304,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); @@ -324,7 +323,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TRICK_OR_TREAT)); @@ -342,7 +341,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); diff --git a/src/test/abilities/magic_guard.test.ts b/src/test/abilities/magic_guard.test.ts index 60a3cda3b08..f138ef77219 100644 --- a/src/test/abilities/magic_guard.test.ts +++ b/src/test/abilities/magic_guard.test.ts @@ -1,17 +1,17 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; +import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; -import { TurnEndPhase, MoveEffectPhase } from "#app/phases"; +import { TurnEndPhase } from "#app/phases"; import { Moves } from "#enums/moves"; import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagSide, getArenaTag } from "#app/data/arena-tag"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { WeatherType } from "#app/data/weather.js"; import { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; // 20 sec timeout @@ -33,16 +33,15 @@ describe("Abilities - Magic Guard", () => { game = new GameManager(phaserGame); /** Player Pokemon overrides */ - vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.MAGIC_GUARD); - vi.spyOn(overrides, "PASSIVE_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.UNNERVE); - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH]); - vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100); + game.override.ability(Abilities.MAGIC_GUARD); + game.override.moveset([Moves.SPLASH]); + game.override.startingLevel(100); /** Enemy Pokemon overrides */ - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); - vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100); + game.override.enemySpecies(Species.SNORLAX); + game.override.enemyAbility(Abilities.INSOMNIA); + game.override.enemyMoveset(SPLASH_ONLY); + game.override.enemyLevel(100); }); //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Magic_Guard_(Ability) @@ -50,14 +49,13 @@ describe("Abilities - Magic Guard", () => { it( "ability should prevent damage caused by weather", async () => { - vi.spyOn(overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(WeatherType.SANDSTORM); + game.override.weather(WeatherType.SANDSTORM); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -78,15 +76,11 @@ describe("Abilities - Magic Guard", () => { "ability should prevent damage caused by status effects but other non-damage effects still apply", async () => { //Toxic keeps track of the turn counters -> important that Magic Guard keeps track of post-Toxic turns - vi.spyOn(overrides, "STATUS_OVERRIDE", "get").mockReturnValue(StatusEffect.POISON); + game.override.statusEffect(StatusEffect.POISON); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); - - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -98,23 +92,19 @@ describe("Abilities - Magic Guard", () => { * - The Pokemon's CatchRateMultiplier should be 1.5 */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(getStatusEffectCatchRateMultiplier(leadPokemon.status.effect)).toBe(1.5); + expect(getStatusEffectCatchRateMultiplier(leadPokemon.status!.effect)).toBe(1.5); }, TIMEOUT ); it( "ability effect should not persist when the ability is replaced", async () => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.WORRY_SEED,Moves.WORRY_SEED,Moves.WORRY_SEED,Moves.WORRY_SEED]); - vi.spyOn(overrides, "STATUS_OVERRIDE", "get").mockReturnValue(StatusEffect.POISON); + game.override.enemyMoveset([Moves.WORRY_SEED,Moves.WORRY_SEED,Moves.WORRY_SEED,Moves.WORRY_SEED]); + game.override.statusEffect(StatusEffect.POISON); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); - - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -131,18 +121,14 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard prevents damage caused by burn but other non-damaging effects are still applied", async () => { - vi.spyOn(overrides, "OPP_STATUS_OVERRIDE", "get").mockReturnValue(StatusEffect.BURN); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.MAGIC_GUARD); + game.override.enemyStatusEffect(StatusEffect.BURN); + game.override.enemyAbility(Abilities.MAGIC_GUARD); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect (leadPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; await game.phaseInterceptor.to(TurnEndPhase); @@ -153,26 +139,22 @@ describe("Abilities - Magic Guard", () => { * - The enemy Pokemon's hypothetical CatchRateMultiplier should be 1.5 */ expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status.effect)).toBe(1.5); + expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status!.effect)).toBe(1.5); }, TIMEOUT ); it("Magic Guard prevents damage caused by toxic but other non-damaging effects are still applied", async () => { - vi.spyOn(overrides, "OPP_STATUS_OVERRIDE", "get").mockReturnValue(StatusEffect.TOXIC); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.MAGIC_GUARD); + game.override.enemyStatusEffect(StatusEffect.TOXIC); + game.override.enemyAbility(Abilities.MAGIC_GUARD); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect (leadPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; - const toxicStartCounter = enemyPokemon.status.turnCount; + const toxicStartCounter = enemyPokemon.status!.turnCount; //should be 0 await game.phaseInterceptor.to(TurnEndPhase); @@ -184,25 +166,23 @@ describe("Abilities - Magic Guard", () => { * - The enemy Pokemon's hypothetical CatchRateMultiplier should be 1.5 */ expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(enemyPokemon.status.turnCount).toBeGreaterThan(toxicStartCounter); - expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status.effect)).toBe(1.5); + expect(enemyPokemon.status!.turnCount).toBeGreaterThan(toxicStartCounter); + expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status!.effect)).toBe(1.5); }, TIMEOUT ); it("Magic Guard prevents damage caused by entry hazards", async () => { //Adds and applies Spikes to both sides of the arena - const newTag = getArenaTag(ArenaTagType.SPIKES, 5, Moves.SPIKES, 0, 0, ArenaTagSide.BOTH); + const newTag = getArenaTag(ArenaTagType.SPIKES, 5, Moves.SPIKES, 0, 0, ArenaTagSide.BOTH)!; game.scene.arena.tags.push(newTag); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; await game.phaseInterceptor.to(TurnEndPhase); @@ -218,19 +198,17 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard does not prevent poison from Toxic Spikes", async () => { //Adds and applies Spikes to both sides of the arena - const playerTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, Moves.TOXIC_SPIKES, 0, 0, ArenaTagSide.PLAYER); - const enemyTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, Moves.TOXIC_SPIKES, 0, 0, ArenaTagSide.ENEMY); + const playerTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, Moves.TOXIC_SPIKES, 0, 0, ArenaTagSide.PLAYER)!; + const enemyTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, Moves.TOXIC_SPIKES, 0, 0, ArenaTagSide.ENEMY)!; game.scene.arena.tags.push(playerTag); game.scene.arena.tags.push(enemyTag); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; await game.phaseInterceptor.to(TurnEndPhase); @@ -240,8 +218,8 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (with Magic Guard) has not taken damage from poison * - The enemy Pokemon (without Magic Guard) has taken damage from poison */ - expect(leadPokemon.status.effect).toBe(StatusEffect.POISON); - expect(enemyPokemon.status.effect).toBe(StatusEffect.POISON); + expect(leadPokemon.status!.effect).toBe(StatusEffect.POISON); + expect(enemyPokemon.status!.effect).toBe(StatusEffect.POISON); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); }, TIMEOUT @@ -250,16 +228,14 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard prevents against damage from volatile status effects", async () => { await game.startBattle([Species.DUSKULL]); - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.CURSE]); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.MAGIC_GUARD); + game.override.moveset([Moves.CURSE]); + game.override.enemyAbility(Abilities.MAGIC_GUARD); - const leadPokemon = game.scene.getPlayerPokemon(); - expect (leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; await game.phaseInterceptor.to(TurnEndPhase); @@ -276,15 +252,13 @@ describe("Abilities - Magic Guard", () => { ); it("Magic Guard prevents crash damage", async () => { - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.HIGH_JUMP_KICK]); + game.override.moveset([Moves.HIGH_JUMP_KICK]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false); + await mockHitCheck(game, false); await game.phaseInterceptor.to(TurnEndPhase); @@ -297,11 +271,10 @@ describe("Abilities - Magic Guard", () => { ); it("Magic Guard prevents damage from recoil", async () => { - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TAKE_DOWN]); + game.override.moveset([Moves.TAKE_DOWN]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.TAKE_DOWN)); @@ -316,11 +289,10 @@ describe("Abilities - Magic Guard", () => { ); it("Magic Guard does not prevent damage from Struggle's recoil", async () => { - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.STRUGGLE]); + game.override.moveset([Moves.STRUGGLE]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); @@ -336,11 +308,10 @@ describe("Abilities - Magic Guard", () => { //This tests different move attributes than the recoil tests above it("Magic Guard prevents self-damage from attacking moves", async () => { - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.STEEL_BEAM]); + game.override.moveset([Moves.STEEL_BEAM]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.STEEL_BEAM)); @@ -365,11 +336,10 @@ describe("Abilities - Magic Guard", () => { */ it("Magic Guard does not prevent self-damage from non-attacking moves", async () => { - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.BELLY_DRUM]); + game.override.moveset([Moves.BELLY_DRUM]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); @@ -385,15 +355,14 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard prevents damage from abilities with PostTurnHurtIfSleepingAbAttr", async() => { //Tests the ability Bad Dreams - vi.spyOn(overrides, "STATUS_OVERRIDE", "get").mockReturnValue(StatusEffect.SLEEP); + game.override.statusEffect(StatusEffect.SLEEP); //enemy pokemon is given Spore just in case player pokemon somehow awakens during test - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPORE, Moves.SPORE, Moves.SPORE, Moves.SPORE]); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.BAD_DREAMS); + game.override.enemyMoveset([Moves.SPORE, Moves.SPORE, Moves.SPORE, Moves.SPORE]); + game.override.enemyAbility(Abilities.BAD_DREAMS); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -405,22 +374,20 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon is asleep */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(leadPokemon.status.effect).toBe(StatusEffect.SLEEP); + expect(leadPokemon.status!.effect).toBe(StatusEffect.SLEEP); }, TIMEOUT ); it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async() => { //Tests the abilities Innards Out/Aftermath - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE]); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.AFTERMATH); + game.override.moveset([Moves.TACKLE]); + game.override.enemyAbility(Abilities.AFTERMATH); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; enemyPokemon.hp = 1; game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -438,16 +405,14 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async() => { //Tests the abilities Iron Barbs/Rough Skin - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE]); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.IRON_BARBS); + game.override.moveset([Moves.TACKLE]); + game.override.enemyAbility(Abilities.IRON_BARBS); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); await game.phaseInterceptor.to(TurnEndPhase); @@ -464,16 +429,14 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async() => { //Tests the ability Liquid Ooze - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.ABSORB]); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.LIQUID_OOZE); + game.override.moveset([Moves.ABSORB]); + game.override.enemyAbility(Abilities.LIQUID_OOZE); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.ABSORB)); await game.phaseInterceptor.to(TurnEndPhase); @@ -490,12 +453,11 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async() => { //Tests the abilities Solar Power/Dry Skin - vi.spyOn(overrides, "PASSIVE_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SOLAR_POWER); - vi.spyOn(overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(WeatherType.SUNNY); + game.override.passiveAbility(Abilities.SOLAR_POWER); + game.override.weather(WeatherType.SUNNY); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/moxie.test.ts b/src/test/abilities/moxie.test.ts index 469a4e2947f..f99068dea41 100644 --- a/src/test/abilities/moxie.test.ts +++ b/src/test/abilities/moxie.test.ts @@ -1,12 +1,8 @@ import { BattleStat } from "#app/data/battle-stat"; import { Stat } from "#app/data/pokemon-stat"; -import { - CommandPhase, - EnemyCommandPhase, - VictoryPhase -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, EnemyCommandPhase, VictoryPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index bc7be2fae96..e5f0f969d10 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -7,10 +7,10 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -47,10 +47,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); let enemyStartingHp = enemyPokemon.hp; @@ -80,10 +80,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.POWER_UP_PUNCH)); @@ -102,10 +102,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); @@ -122,17 +122,14 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(BerryPhase, false); @@ -147,10 +144,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SELF_DESTRUCT)); @@ -168,16 +165,14 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase, false); @@ -192,10 +187,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; @@ -215,10 +210,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const playerStartingHp = leadPokemon.hp; @@ -268,10 +263,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); @@ -288,10 +283,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.PIDGEOT]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.MIND_BLOWN)); @@ -314,10 +309,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); @@ -342,10 +337,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -364,18 +359,16 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -395,18 +388,16 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -425,16 +416,14 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -455,16 +444,14 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -487,16 +474,14 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -516,16 +501,14 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(MoveEffectPhase); expect(leadPokemon.turnData.hitCount).toBe(2); @@ -542,16 +525,14 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP)); - - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + await mockHitCheck(game, true); await game.phaseInterceptor.to(DamagePhase); @@ -572,10 +553,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -594,10 +575,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); diff --git a/src/test/abilities/pastel_veil.test.ts b/src/test/abilities/pastel_veil.test.ts index f19b395677f..e3d52a720b3 100644 --- a/src/test/abilities/pastel_veil.test.ts +++ b/src/test/abilities/pastel_veil.test.ts @@ -1,14 +1,10 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; +import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; -import { - CommandPhase, - TurnEndPhase, -} from "#app/phases"; +import { CommandPhase, TurnEndPhase } from "#app/phases"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { StatusEffect } from "#app/data/status-effect.js"; import { allAbilities } from "#app/data/ability.js"; import { Abilities } from "#app/enums/abilities.js"; @@ -30,11 +26,11 @@ describe("Abilities - Pastel Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("double"); - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH]); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.BALL_FETCH); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TOXIC_THREAD, Moves.TOXIC_THREAD, Moves.TOXIC_THREAD, Moves.TOXIC_THREAD]); + game.override.battleType("double"); + game.override.moveset([Moves.SPLASH]); + game.override.enemyAbility(Abilities.BALL_FETCH); + game.override.enemySpecies(Species.MAGIKARP); + game.override.enemyMoveset([Moves.TOXIC_THREAD, Moves.TOXIC_THREAD, Moves.TOXIC_THREAD, Moves.TOXIC_THREAD]); }); it("prevents the user and its allies from being afflicted by poison", async () => { @@ -55,7 +51,7 @@ describe("Abilities - Pastel Veil", () => { it("it heals the poisoned status condition of allies if user is sent out into battle", async () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.GALAR_PONYTA]); - const ponyta = game.scene.getParty().find(p => p.species.speciesId === Species.GALAR_PONYTA); + const ponyta = game.scene.getParty().find(p => p.species.speciesId === Species.GALAR_PONYTA)!; vi.spyOn(ponyta, "getAbility").mockReturnValue(allAbilities[Abilities.PASTEL_VEIL]); @@ -70,7 +66,7 @@ describe("Abilities - Pastel Veil", () => { const poisonedMon = game.scene.getPlayerField().find(p => p.status?.effect === StatusEffect.POISON); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, (poisonedMon.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); + game.doAttack(getMovePosition(game.scene, (poisonedMon!.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); game.doSwitchPokemon(2); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/power_construct.test.ts b/src/test/abilities/power_construct.test.ts index 769499c0e53..dd8fd836e51 100644 --- a/src/test/abilities/power_construct.test.ts +++ b/src/test/abilities/power_construct.test.ts @@ -47,11 +47,11 @@ describe("Abilities - POWER CONSTRUCT", () => { const zygarde = game.scene.getParty().find((p) => p.species.speciesId === Species.ZYGARDE); expect(zygarde).not.toBe(undefined); - expect(zygarde.formIndex).toBe(completeForm); + expect(zygarde!.formIndex).toBe(completeForm); - zygarde.hp = 0; - zygarde.status = new Status(StatusEffect.FAINT); - expect(zygarde.isFainted()).toBe(true); + zygarde!.hp = 0; + zygarde!.status = new Status(StatusEffect.FAINT); + expect(zygarde!.isFainted()).toBe(true); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.doKillOpponents(); @@ -59,7 +59,7 @@ describe("Abilities - POWER CONSTRUCT", () => { game.doSelectModifier(); await game.phaseInterceptor.to(QuietFormChangePhase); - expect(zygarde.formIndex).toBe(baseForm); + expect(zygarde!.formIndex).toBe(baseForm); }, TIMEOUT ); diff --git a/src/test/abilities/power_spot.test.ts b/src/test/abilities/power_spot.test.ts index 018002fe541..368f8a48110 100644 --- a/src/test/abilities/power_spot.test.ts +++ b/src/test/abilities/power_spot.test.ts @@ -1,13 +1,13 @@ import { allMoves } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Power Spot", () => { let phaserGame: Phaser.Game; @@ -40,7 +40,7 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); - await game.startBattle([Species.PIKACHU, Species.STONJOURNER]); + await game.startBattle([Species.REGIELEKI, Species.STONJOURNER]); game.doAttack(getMovePosition(game.scene, 0, Moves.DAZZLING_GLEAM)); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); await game.phaseInterceptor.to(MoveEffectPhase); @@ -54,7 +54,7 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); - await game.startBattle([Species.PIKACHU, Species.STONJOURNER]); + await game.startBattle([Species.REGIELEKI, Species.STONJOURNER]); game.doAttack(getMovePosition(game.scene, 0, Moves.BREAKING_SWIPE)); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); await game.phaseInterceptor.to(MoveEffectPhase); @@ -68,7 +68,7 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); - await game.startBattle([Species.STONJOURNER, Species.PIKACHU]); + await game.startBattle([Species.STONJOURNER, Species.REGIELEKI]); game.doAttack(getMovePosition(game.scene, 0, Moves.BREAKING_SWIPE)); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/protean.test.ts b/src/test/abilities/protean.test.ts index d02c87662cf..8022f73255f 100644 --- a/src/test/abilities/protean.test.ts +++ b/src/test/abilities/protean.test.ts @@ -2,7 +2,7 @@ import { allMoves } from "#app/data/move.js"; import { Type } from "#app/data/type.js"; import { Weather, WeatherType } from "#app/data/weather.js"; import { PlayerPokemon } from "#app/field/pokemon.js"; -import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; +import { TurnEndPhase } from "#app/phases.js"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; @@ -10,9 +10,9 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -46,7 +46,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -64,7 +64,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP, Species.BULBASAUR]); - let leadPokemon = game.scene.getPlayerPokemon(); + let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -86,7 +86,7 @@ describe("Abilities - Protean", () => { game.doSwitchPokemon(1); await game.toNextTurn(); - leadPokemon = game.scene.getPlayerPokemon(); + leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -104,7 +104,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); @@ -128,7 +128,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -150,7 +150,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; @@ -169,7 +169,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.DIG)); @@ -188,15 +188,14 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false); + await mockHitCheck(game, false); await game.phaseInterceptor.to(TurnEndPhase); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon.isFullHp()).toBe(true); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, @@ -211,7 +210,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -230,7 +229,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -248,7 +247,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [allMoves[Moves.SPLASH].defaultType]; @@ -267,7 +266,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); @@ -287,7 +286,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); @@ -305,7 +304,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); @@ -324,7 +323,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TRICK_OR_TREAT)); @@ -342,7 +341,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); diff --git a/src/test/abilities/quick_draw.test.ts b/src/test/abilities/quick_draw.test.ts index e6177639bac..75bb9ec6a0a 100644 --- a/src/test/abilities/quick_draw.test.ts +++ b/src/test/abilities/quick_draw.test.ts @@ -1,7 +1,7 @@ import { allAbilities, BypassSpeedChanceAbAttr } from "#app/data/ability"; import { FaintPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -41,8 +41,8 @@ describe("Abilities - Quick Draw", () => { test("makes pokemon going first in its priority bracket", async () => { await game.startBattle(); - const pokemon = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; pokemon.hp = 1; enemy.hp = 1; @@ -61,8 +61,8 @@ describe("Abilities - Quick Draw", () => { }, async () => { await game.startBattle(); - const pokemon = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; pokemon.hp = 1; enemy.hp = 1; @@ -81,8 +81,8 @@ describe("Abilities - Quick Draw", () => { await game.startBattle(); - const pokemon = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; pokemon.hp = 1; enemy.hp = 1; diff --git a/src/test/abilities/sand_spit.test.ts b/src/test/abilities/sand_spit.test.ts new file mode 100644 index 00000000000..d3d1a8e0028 --- /dev/null +++ b/src/test/abilities/sand_spit.test.ts @@ -0,0 +1,57 @@ +import GameManager from "#test/utils/gameManager"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { WeatherType } from "#app/enums/weather-type.js"; + + +describe("Ability Timing", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override.battleType("single"); + game.override.disableCrits(); + + game.override.enemySpecies(Species.MAGIKARP); + game.override.enemyAbility(Abilities.BALL_FETCH); + + game.override.starterSpecies(Species.SILICOBRA); + game.override.ability(Abilities.SAND_SPIT); + game.override.moveset([Moves.SPLASH, Moves.COIL]); + }); + + it("should trigger when hit with damaging move", async() => { + game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + await game.startBattle(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.toNextTurn(); + + expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SANDSTORM); + }, 20000); + + it("should not trigger when targetted with status moves", async() => { + game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + await game.startBattle(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.COIL)); + await game.toNextTurn(); + + expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.SANDSTORM); + }, 20000); +}); diff --git a/src/test/abilities/sand_veil.test.ts b/src/test/abilities/sand_veil.test.ts index 4394df3ce97..6aab362634a 100644 --- a/src/test/abilities/sand_veil.test.ts +++ b/src/test/abilities/sand_veil.test.ts @@ -7,8 +7,8 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; @@ -45,10 +45,6 @@ describe("Abilities - Sand Veil", () => { await game.startBattle([Species.SNORLAX, Species.BLISSEY]); const leadPokemon = game.scene.getPlayerField(); - leadPokemon.forEach(p => expect(p).toBeDefined()); - - const enemyPokemon = game.scene.getEnemyField(); - enemyPokemon.forEach(p => expect(p).toBeDefined()); vi.spyOn(leadPokemon[0], "getAbility").mockReturnValue(allAbilities[Abilities.SAND_VEIL]); diff --git a/src/test/abilities/sap_sipper.test.ts b/src/test/abilities/sap_sipper.test.ts index 2ed1df05652..6fbe57978e9 100644 --- a/src/test/abilities/sap_sipper.test.ts +++ b/src/test/abilities/sap_sipper.test.ts @@ -1,10 +1,8 @@ import { BattleStat } from "#app/data/battle-stat.js"; import { TerrainType } from "#app/data/terrain.js"; -import { - MoveEndPhase, TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { MoveEndPhase, TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; @@ -89,7 +87,7 @@ describe("Abilities - Sap Sipper", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.terrain).toBeDefined(); - expect(game.scene.arena.terrain.terrainType).toBe(TerrainType.GRASSY); + expect(game.scene.arena.terrain!.terrainType).toBe(TerrainType.GRASSY); expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(0); }); diff --git a/src/test/abilities/schooling.test.ts b/src/test/abilities/schooling.test.ts index 7671534a549..e55b7795006 100644 --- a/src/test/abilities/schooling.test.ts +++ b/src/test/abilities/schooling.test.ts @@ -45,7 +45,7 @@ describe("Abilities - SCHOOLING", () => { await game.startBattle([Species.MAGIKARP, Species.WISHIWASHI]); - const wishiwashi = game.scene.getParty().find((p) => p.species.speciesId === Species.WISHIWASHI); + const wishiwashi = game.scene.getParty().find((p) => p.species.speciesId === Species.WISHIWASHI)!; expect(wishiwashi).not.toBe(undefined); expect(wishiwashi.formIndex).toBe(schoolForm); diff --git a/src/test/abilities/screen_cleaner.test.ts b/src/test/abilities/screen_cleaner.test.ts index 2d2dc70b907..a73f56dd3eb 100644 --- a/src/test/abilities/screen_cleaner.test.ts +++ b/src/test/abilities/screen_cleaner.test.ts @@ -1,7 +1,7 @@ import { ArenaTagType } from "#app/enums/arena-tag-type.js"; import { PostSummonPhase, TurnEndPhase, } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/test/abilities/serene_grace.test.ts b/src/test/abilities/serene_grace.test.ts index 13da444454e..59e9ff723da 100644 --- a/src/test/abilities/serene_grace.test.ts +++ b/src/test/abilities/serene_grace.test.ts @@ -1,11 +1,8 @@ import { applyAbAttrs, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import { - CommandPhase, - MoveEffectPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, MoveEffectPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import * as Utils from "#app/utils"; @@ -14,6 +11,8 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { mockTurnOrder } from "../utils/testUtils"; +import { BattlerIndex } from "#app/battle.js"; describe("Abilities - Serene Grace", () => { @@ -48,7 +47,6 @@ describe("Abilities - Serene Grace", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; - game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1; expect(game.scene.getParty()[0].formIndex).toBe(0); game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { @@ -59,6 +57,7 @@ describe("Abilities - Serene Grace", () => { (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); + await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); // Check chance of Air Slash without Serene Grace @@ -67,8 +66,8 @@ describe("Abilities - Serene Grace", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - console.log(move.chance + " Their ability is " + phase.getUserPokemon().getAbility().name); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); + console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); expect(chance.value).toBe(30); }, 20000); @@ -81,7 +80,6 @@ describe("Abilities - Serene Grace", () => { ]); game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; - game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1; expect(game.scene.getParty()[0].formIndex).toBe(0); game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { @@ -92,6 +90,7 @@ describe("Abilities - Serene Grace", () => { (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); + await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); // Check chance of Air Slash with Serene Grace @@ -100,7 +99,7 @@ describe("Abilities - Serene Grace", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); expect(chance.value).toBe(60); }, 20000); diff --git a/src/test/abilities/sheer_force.test.ts b/src/test/abilities/sheer_force.test.ts index 1e7657611a1..35353bc7000 100644 --- a/src/test/abilities/sheer_force.test.ts +++ b/src/test/abilities/sheer_force.test.ts @@ -1,11 +1,8 @@ import { applyAbAttrs, applyPostDefendAbAttrs, applyPreAttackAbAttrs, MoveEffectChanceMultiplierAbAttr, MovePowerBoostAbAttr, PostDefendTypeChangeAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import { - CommandPhase, - MoveEffectPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, MoveEffectPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import * as Utils from "#app/utils"; @@ -14,6 +11,8 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { mockTurnOrder } from "../utils/testUtils"; +import { BattlerIndex } from "#app/battle.js"; describe("Abilities - Sheer Force", () => { @@ -49,7 +48,6 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; - game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1; expect(game.scene.getParty()[0].formIndex).toBe(0); game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { @@ -60,6 +58,7 @@ describe("Abilities - Sheer Force", () => { (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); + await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -70,8 +69,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); expect(chance.value).toBe(0); expect(power.value).toBe(move.power * 5461/4096); @@ -88,7 +87,6 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; - game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1; expect(game.scene.getParty()[0].formIndex).toBe(0); game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { @@ -99,6 +97,7 @@ describe("Abilities - Sheer Force", () => { (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); + await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -109,8 +108,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); expect(chance.value).toBe(-1); expect(power.value).toBe(move.power); @@ -127,7 +126,6 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; - game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1; expect(game.scene.getParty()[0].formIndex).toBe(0); game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { @@ -138,6 +136,7 @@ describe("Abilities - Sheer Force", () => { (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); + await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -148,8 +147,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); expect(chance.value).toBe(-1); expect(power.value).toBe(move.power); @@ -168,7 +167,6 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; - game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1; expect(game.scene.getParty()[0].formIndex).toBe(0); game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { @@ -179,6 +177,7 @@ describe("Abilities - Sheer Force", () => { (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); + await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -188,8 +187,8 @@ describe("Abilities - Sheer Force", () => { //Disable color change due to being hit by Sheer Force const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - const user = phase.getUserPokemon(); - const target = phase.getTarget(); + const user = phase.getUserPokemon()!; + const target = phase.getTarget()!; const opponentType = target.getTypes()[0]; applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, chance, move, target, false); diff --git a/src/test/abilities/shield_dust.test.ts b/src/test/abilities/shield_dust.test.ts index 35e4015e8bc..ded70eccb36 100644 --- a/src/test/abilities/shield_dust.test.ts +++ b/src/test/abilities/shield_dust.test.ts @@ -1,11 +1,8 @@ import { applyAbAttrs, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import { - CommandPhase, - MoveEffectPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, MoveEffectPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import * as Utils from "#app/utils"; @@ -14,6 +11,8 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { BattlerIndex } from "#app/battle.js"; +import { mockTurnOrder } from "../utils/testUtils"; describe("Abilities - Shield Dust", () => { @@ -49,7 +48,6 @@ describe("Abilities - Shield Dust", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; - game.scene.getEnemyParty()[0].stats[Stat.SPD] = 1; expect(game.scene.getParty()[0].formIndex).toBe(0); game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { @@ -60,6 +58,7 @@ describe("Abilities - Shield Dust", () => { (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); + await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); // Shield Dust negates secondary effect @@ -68,8 +67,8 @@ describe("Abilities - Shield Dust", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); - applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget(),phase.getUserPokemon(),null,null, chance); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null!, null!, chance); expect(chance.value).toBe(0); }, 20000); diff --git a/src/test/abilities/shields_down.test.ts b/src/test/abilities/shields_down.test.ts index 64904c80032..4d85e8aa47c 100644 --- a/src/test/abilities/shields_down.test.ts +++ b/src/test/abilities/shields_down.test.ts @@ -45,7 +45,7 @@ describe("Abilities - SHIELDS DOWN", () => { await game.startBattle([Species.MAGIKARP, Species.MINIOR]); - const minior = game.scene.getParty().find((p) => p.species.speciesId === Species.MINIOR); + const minior = game.scene.getParty().find((p) => p.species.speciesId === Species.MINIOR)!; expect(minior).not.toBe(undefined); expect(minior.formIndex).toBe(coreForm); diff --git a/src/test/abilities/steely_spirit.test.ts b/src/test/abilities/steely_spirit.test.ts index 7ac78eb11fa..5d5514bc3a1 100644 --- a/src/test/abilities/steely_spirit.test.ts +++ b/src/test/abilities/steely_spirit.test.ts @@ -2,13 +2,13 @@ import { allAbilities } from "#app/data/ability.js"; import { allMoves } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { MoveEffectPhase, SelectTargetPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Steely Spirit", () => { let phaserGame: Phaser.Game; @@ -40,7 +40,7 @@ describe("Abilities - Steely Spirit", () => { it("increases Steel-type moves' power used by the user and its allies by 50%", async () => { await game.startBattle([Species.PIKACHU, Species.SHUCKLE]); const boostSource = game.scene.getPlayerField()[1]; - const enemyToCheck = game.scene.getEnemyPokemon(); + const enemyToCheck = game.scene.getEnemyPokemon()!; vi.spyOn(boostSource, "getAbility").mockReturnValue(allAbilities[Abilities.STEELY_SPIRIT]); @@ -57,7 +57,7 @@ describe("Abilities - Steely Spirit", () => { it("stacks if multiple users with this ability are on the field.", async () => { await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const enemyToCheck = game.scene.getEnemyPokemon(); + const enemyToCheck = game.scene.getEnemyPokemon()!; game.scene.getPlayerField().forEach(p => { vi.spyOn(p, "getAbility").mockReturnValue(allAbilities[Abilities.STEELY_SPIRIT]); @@ -79,7 +79,7 @@ describe("Abilities - Steely Spirit", () => { it("does not take effect when suppressed", async () => { await game.startBattle([Species.PIKACHU, Species.SHUCKLE]); const boostSource = game.scene.getPlayerField()[1]; - const enemyToCheck = game.scene.getEnemyPokemon(); + const enemyToCheck = game.scene.getEnemyPokemon()!; vi.spyOn(boostSource, "getAbility").mockReturnValue(allAbilities[Abilities.STEELY_SPIRIT]); expect(boostSource.hasAbility(Abilities.STEELY_SPIRIT)).toBe(true); diff --git a/src/test/abilities/sturdy.test.ts b/src/test/abilities/sturdy.test.ts index 9bfb3bd0085..4caa7b0bd14 100644 --- a/src/test/abilities/sturdy.test.ts +++ b/src/test/abilities/sturdy.test.ts @@ -1,10 +1,7 @@ import { EnemyPokemon } from "#app/field/pokemon.js"; -import { - DamagePhase, - MoveEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { DamagePhase, MoveEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/test/abilities/sweet_veil.test.ts b/src/test/abilities/sweet_veil.test.ts index 5a8022958ad..5af822da061 100644 --- a/src/test/abilities/sweet_veil.test.ts +++ b/src/test/abilities/sweet_veil.test.ts @@ -1,19 +1,14 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; +import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; -import { - CommandPhase, - MoveEffectPhase, - MovePhase, - TurnEndPhase, -} from "#app/phases"; +import { CommandPhase, MovePhase, TurnEndPhase } from "#app/phases"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { Abilities } from "#app/enums/abilities.js"; import { BattlerIndex } from "#app/battle.js"; +import { mockHitCheck, SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Sweet Veil", () => { let phaserGame: Phaser.Game; @@ -31,11 +26,11 @@ describe("Abilities - Sweet Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("double"); - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.REST]); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.BALL_FETCH); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.POWDER, Moves.POWDER, Moves.POWDER, Moves.POWDER]); + game.override.battleType("double"); + game.override.moveset([Moves.SPLASH, Moves.REST]); + game.override.enemySpecies(Species.MAGIKARP); + game.override.enemyAbility(Abilities.BALL_FETCH); + game.override.enemyMoveset([Moves.POWDER, Moves.POWDER, Moves.POWDER, Moves.POWDER]); }); it("prevents the user and its allies from falling asleep", async () => { @@ -50,7 +45,7 @@ describe("Abilities - Sweet Veil", () => { }); it("causes Rest to fail when used by the user or its allies", async () => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); + game.override.enemyMoveset(SPLASH_ONLY); await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -62,7 +57,7 @@ describe("Abilities - Sweet Veil", () => { }); it("causes Yawn to fail if used on the user or its allies", async () => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.YAWN, Moves.YAWN, Moves.YAWN, Moves.YAWN]); + game.override.enemyMoveset([Moves.YAWN, Moves.YAWN, Moves.YAWN, Moves.YAWN]); await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -74,10 +69,10 @@ describe("Abilities - Sweet Veil", () => { }); it("prevents the user and its allies already drowsy due to Yawn from falling asleep.", async () => { - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.PIKACHU); - vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(5); - vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(5); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.YAWN, Moves.YAWN, Moves.YAWN, Moves.YAWN]); + game.override.enemySpecies(Species.PIKACHU); + game.override.enemyLevel(5); + game.override.startingLevel(5); + game.override.enemyMoveset([Moves.YAWN, Moves.YAWN, Moves.YAWN, Moves.YAWN]); await game.startBattle([Species.SHUCKLE, Species.SHUCKLE, Species.SWIRLIX]); @@ -85,19 +80,17 @@ describe("Abilities - Sweet Veil", () => { game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); // First pokemon move - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true); + await mockHitCheck(game, true); // Second pokemon move await game.phaseInterceptor.to(MovePhase, false); - await game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(true); + await mockHitCheck(game, true); expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true); await game.phaseInterceptor.to(TurnEndPhase); - const drowsyMon = game.scene.getPlayerField().find(p => !!p.getTag(BattlerTagType.DROWSY)); + const drowsyMon = game.scene.getPlayerField().find(p => !!p.getTag(BattlerTagType.DROWSY))!; await game.phaseInterceptor.to(CommandPhase); game.doAttack(getMovePosition(game.scene, (drowsyMon.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); diff --git a/src/test/abilities/unseen_fist.test.ts b/src/test/abilities/unseen_fist.test.ts index 2156c5eb588..a6cad8b03ce 100644 --- a/src/test/abilities/unseen_fist.test.ts +++ b/src/test/abilities/unseen_fist.test.ts @@ -4,8 +4,8 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; @@ -72,10 +72,10 @@ async function testUnseenFistHitResult(game: GameManager, attackMove: Moves, pro await game.startBattle(); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; diff --git a/src/test/abilities/volt_absorb.test.ts b/src/test/abilities/volt_absorb.test.ts index 419c3a612d5..985459e133b 100644 --- a/src/test/abilities/volt_absorb.test.ts +++ b/src/test/abilities/volt_absorb.test.ts @@ -1,9 +1,7 @@ import { BattleStat } from "#app/data/battle-stat.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; diff --git a/src/test/abilities/wind_power.test.ts b/src/test/abilities/wind_power.test.ts index cfdb2e745fe..670544a89ef 100644 --- a/src/test/abilities/wind_power.test.ts +++ b/src/test/abilities/wind_power.test.ts @@ -1,15 +1,13 @@ import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wind Power", () => { let phaserGame: Phaser.Game; @@ -36,7 +34,7 @@ describe("Abilities - Wind Power", () => { it("it becomes charged when hit by wind moves", async () => { await game.startBattle([Species.MAGIKARP]); - const shiftry = game.scene.getEnemyPokemon(); + const shiftry = game.scene.getEnemyPokemon()!; expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -51,7 +49,7 @@ describe("Abilities - Wind Power", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon(); + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -66,8 +64,8 @@ describe("Abilities - Wind Power", () => { game.override.ability(Abilities.WIND_POWER); await game.startBattle([Species.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon(); - const shiftry = game.scene.getPlayerPokemon(); + const magikarp = game.scene.getEnemyPokemon()!; + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); expect(magikarp.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -84,7 +82,7 @@ describe("Abilities - Wind Power", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon(); + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); diff --git a/src/test/abilities/wind_rider.test.ts b/src/test/abilities/wind_rider.test.ts index 88da929fbf1..e27349efe41 100644 --- a/src/test/abilities/wind_rider.test.ts +++ b/src/test/abilities/wind_rider.test.ts @@ -1,15 +1,13 @@ import { BattleStat } from "#app/data/battle-stat.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wind Rider", () => { let phaserGame: Phaser.Game; @@ -36,7 +34,7 @@ describe("Abilities - Wind Rider", () => { it("takes no damage from wind moves and its Attack is increased by one stage when hit by one", async () => { await game.startBattle([Species.MAGIKARP]); - const shiftry = game.scene.getEnemyPokemon(); + const shiftry = game.scene.getEnemyPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -53,7 +51,7 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon(); + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -69,8 +67,8 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon(); - const shiftry = game.scene.getPlayerPokemon(); + const magikarp = game.scene.getEnemyPokemon()!; + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -87,8 +85,8 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon(); - const shiftry = game.scene.getPlayerPokemon(); + const magikarp = game.scene.getEnemyPokemon()!; + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -105,7 +103,7 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon(); + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(shiftry.isFullHp()).toBe(true); diff --git a/src/test/abilities/wonder_skin.test.ts b/src/test/abilities/wonder_skin.test.ts index bc964134e45..a2815152df6 100644 --- a/src/test/abilities/wonder_skin.test.ts +++ b/src/test/abilities/wonder_skin.test.ts @@ -1,14 +1,14 @@ import { allAbilities } from "#app/data/ability.js"; import { allMoves } from "#app/data/move.js"; import { MoveEffectPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wonder Skin", () => { let phaserGame: Phaser.Game; diff --git a/src/test/abilities/zen_mode.test.ts b/src/test/abilities/zen_mode.test.ts index 8f4c8b42af5..fc0bf282078 100644 --- a/src/test/abilities/zen_mode.test.ts +++ b/src/test/abilities/zen_mode.test.ts @@ -1,20 +1,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { Status, StatusEffect } from "#app/data/status-effect.js"; import { QuietFormChangePhase } from "#app/form-change-phase"; -import { - CommandPhase, - DamagePhase, - EnemyCommandPhase, - MessagePhase, - PostSummonPhase, - SwitchPhase, - SwitchSummonPhase, - TurnEndPhase, - TurnInitPhase, - TurnStartPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, DamagePhase, EnemyCommandPhase, MessagePhase, PostSummonPhase, SwitchPhase, SwitchSummonPhase, TurnEndPhase, TurnInitPhase, TurnStartPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; @@ -22,6 +11,8 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { BattlerIndex } from "#app/battle.js"; +import { mockTurnOrder } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -56,7 +47,6 @@ describe("Abilities - ZEN MODE", () => { async () => { const moveToUse = Moves.SPLASH; await game.startBattle([Species.DARMANITAN]); - game.scene.getParty()[0].stats[Stat.SPD] = 1; game.scene.getParty()[0].stats[Stat.HP] = 100; game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); @@ -68,7 +58,8 @@ describe("Abilities - ZEN MODE", () => { const movePosition = getMovePosition(game.scene, 0, moveToUse); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); - await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(DamagePhase, false); + await mockTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to(DamagePhase, false); // await game.phaseInterceptor.runFrom(DamagePhase).to(DamagePhase, false); const damagePhase = game.scene.getCurrentPhase() as DamagePhase; damagePhase.updateAmount(40); @@ -84,7 +75,6 @@ describe("Abilities - ZEN MODE", () => { async () => { const moveToUse = Moves.SPLASH; await game.startBattle([Species.DARMANITAN]); - game.scene.getParty()[0].stats[Stat.SPD] = 1; game.scene.getParty()[0].stats[Stat.HP] = 1000; game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); @@ -96,7 +86,8 @@ describe("Abilities - ZEN MODE", () => { const movePosition = getMovePosition(game.scene, 0, moveToUse); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); - await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(QuietFormChangePhase); + await mockTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to(QuietFormChangePhase); await game.phaseInterceptor.to(TurnInitPhase, false); expect(game.scene.getParty()[0].hp).not.toBe(100); expect(game.scene.getParty()[0].formIndex).not.toBe(0); @@ -109,7 +100,6 @@ describe("Abilities - ZEN MODE", () => { async () => { const moveToUse = Moves.SPLASH; await game.startBattle([Species.DARMANITAN, Species.CHARIZARD]); - game.scene.getParty()[0].stats[Stat.SPD] = 1; game.scene.getParty()[0].stats[Stat.HP] = 1000; game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); @@ -121,7 +111,8 @@ describe("Abilities - ZEN MODE", () => { const movePosition = getMovePosition(game.scene, 0, moveToUse); (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); - await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(DamagePhase, false); + await mockTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to(DamagePhase, false); // await game.phaseInterceptor.runFrom(DamagePhase).to(DamagePhase, false); const damagePhase = game.scene.getCurrentPhase() as DamagePhase; damagePhase.updateAmount(80); @@ -159,7 +150,7 @@ describe("Abilities - ZEN MODE", () => { await game.startBattle([Species.MAGIKARP, Species.DARMANITAN]); - const darmanitan = game.scene.getParty().find((p) => p.species.speciesId === Species.DARMANITAN); + const darmanitan = game.scene.getParty().find((p) => p.species.speciesId === Species.DARMANITAN)!; expect(darmanitan).not.toBe(undefined); expect(darmanitan.formIndex).toBe(zenForm); diff --git a/src/test/abilities/zero_to_hero.test.ts b/src/test/abilities/zero_to_hero.test.ts index 409fb35a78c..c58761ce621 100644 --- a/src/test/abilities/zero_to_hero.test.ts +++ b/src/test/abilities/zero_to_hero.test.ts @@ -45,7 +45,7 @@ describe("Abilities - ZERO TO HERO", () => { await game.startBattle([Species.MAGIKARP, Species.PALAFIN]); - const palafin = game.scene.getParty().find((p) => p.species.speciesId === Species.PALAFIN); + const palafin = game.scene.getParty().find((p) => p.species.speciesId === Species.PALAFIN)!; expect(palafin).not.toBe(undefined); expect(palafin.formIndex).toBe(heroForm); diff --git a/src/test/account.spec.ts b/src/test/account.spec.ts index 28e48ce9933..d5d0458c7e8 100644 --- a/src/test/account.spec.ts +++ b/src/test/account.spec.ts @@ -8,21 +8,21 @@ describe("account", () => { it("should set loggedInUser to Guest and lastSessionSlot to -1", () => { initLoggedInUser(); - expect(loggedInUser.username).toBe("Guest"); - expect(loggedInUser.lastSessionSlot).toBe(-1); + expect(loggedInUser!.username).toBe("Guest"); + expect(loggedInUser!.lastSessionSlot).toBe(-1); }); }); describe("updateUserInfo", () => { - it("should set loggedInUser to Guest if bypassLogin is true", async () => { + it("should set loggedInUser! to Guest if bypassLogin is true", async () => { vi.spyOn(battleScene, "bypassLogin", "get").mockReturnValue(true); const [success, status] = await updateUserInfo(); expect(success).toBe(true); expect(status).toBe(200); - expect(loggedInUser.username).toBe("Guest"); - expect(loggedInUser.lastSessionSlot).toBe(-1); + expect(loggedInUser!.username).toBe("Guest"); + expect(loggedInUser!.lastSessionSlot).toBe(-1); }); it("should fetch user info from the API if bypassLogin is false", async () => { @@ -43,8 +43,8 @@ describe("account", () => { expect(success).toBe(true); expect(status).toBe(200); - expect(loggedInUser.username).toBe("test"); - expect(loggedInUser.lastSessionSlot).toBe(99); + expect(loggedInUser!.username).toBe("test"); + expect(loggedInUser!.lastSessionSlot).toBe(99); }); it("should handle resolved API errors", async () => { diff --git a/src/test/achievements/achievement.test.ts b/src/test/achievements/achievement.test.ts index 83d24d1f0ee..5cd9c4d4094 100644 --- a/src/test/achievements/achievement.test.ts +++ b/src/test/achievements/achievement.test.ts @@ -1,14 +1,14 @@ import { TurnHeldItemTransferModifier } from "#app/modifier/modifier.js"; import { Achv, AchvTier, DamageAchv, HealAchv, LevelAchv, ModifierAchv, MoneyAchv, RibbonAchv, achvs } from "#app/system/achv"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { IntegerHolder, NumberHolder } from "#app/utils.js"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import BattleScene from "../../battle-scene"; describe("check some Achievement related stuff", () => { it ("should check Achievement creation", () => { - const ach = new MoneyAchv("", "Achievement", 1000, null, 100); + const ach = new MoneyAchv("", "Achievement", 1000, null!, 100); expect(ach.name).toBe("Achievement"); }); }); @@ -58,7 +58,7 @@ describe("Achv", () => { }); it("should validate the achievement based on the condition function", () => { - const conditionFunc = jest.fn((scene: BattleScene, args: any[]) => args[0] === 10); + const conditionFunc = vi.fn((scene: BattleScene, args: any[]) => args[0] === 10); const achv = new Achv("", "Test Achievement", "Test Description", "test_icon", 10, conditionFunc); expect(achv.validate(new BattleScene(), [5])).toBe(false); @@ -196,7 +196,7 @@ describe("ModifierAchv", () => { it("should validate the achievement based on the modifier function", () => { const modifierAchv = new ModifierAchv("", "Test Modifier Achievement", "Test Description", "modifier_icon", 10, () => true); const scene = new BattleScene(); - const modifier = new TurnHeldItemTransferModifier(null, 3, 1); + const modifier = new TurnHeldItemTransferModifier(null!, 3, 1); expect(modifierAchv.validate(scene, [modifier])).toBe(true); }); diff --git a/src/test/arena/arena_gravity.test.ts b/src/test/arena/arena_gravity.test.ts index f89846dd999..66d6994fb80 100644 --- a/src/test/arena/arena_gravity.test.ts +++ b/src/test/arena/arena_gravity.test.ts @@ -1,12 +1,9 @@ import { allMoves } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import { - MoveEffectPhase, - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { MoveEffectPhase, TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; diff --git a/src/test/arena/weather_fog.test.ts b/src/test/arena/weather_fog.test.ts index 9b0630f5c2b..e5718b73a3c 100644 --- a/src/test/arena/weather_fog.test.ts +++ b/src/test/arena/weather_fog.test.ts @@ -1,11 +1,9 @@ import { allMoves } from "#app/data/move.js"; import { WeatherType } from "#app/data/weather.js"; import { Abilities } from "#app/enums/abilities.js"; -import { - MoveEffectPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { MoveEffectPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; diff --git a/src/test/arena/weather_strong_winds.test.ts b/src/test/arena/weather_strong_winds.test.ts index 6c977401f20..d9f626cfb83 100644 --- a/src/test/arena/weather_strong_winds.test.ts +++ b/src/test/arena/weather_strong_winds.test.ts @@ -1,9 +1,7 @@ import { allMoves } from "#app/data/move.js"; -import { - TurnStartPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnStartPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -37,8 +35,8 @@ describe("Weather - Strong Winds", () => { game.override.enemySpecies(Species.RAYQUAZA); await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); @@ -48,8 +46,8 @@ describe("Weather - Strong Winds", () => { it("electric type move is neutral for flying type pokemon", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); @@ -59,8 +57,8 @@ describe("Weather - Strong Winds", () => { it("ice type move is neutral for flying type pokemon", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); @@ -70,8 +68,8 @@ describe("Weather - Strong Winds", () => { it("rock type move is neutral for flying type pokemon", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.ROCK_SLIDE)); diff --git a/src/test/battle-stat.spec.ts b/src/test/battle-stat.spec.ts index 46f25f66bcd..775dd40ff34 100644 --- a/src/test/battle-stat.spec.ts +++ b/src/test/battle-stat.spec.ts @@ -1,8 +1,4 @@ -import { - BattleStat, - getBattleStatLevelChangeDescription, - getBattleStatName, -} from "#app/data/battle-stat.js"; +import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat.js"; import { describe, expect, it } from "vitest"; import { arrayOfRange, mockI18next } from "./utils/testUtils"; diff --git a/src/test/battle/battle-order.test.ts b/src/test/battle/battle-order.test.ts index 14ff1bcec0d..6aa919186b4 100644 --- a/src/test/battle/battle-order.test.ts +++ b/src/test/battle/battle-order.test.ts @@ -1,10 +1,7 @@ import { Stat } from "#app/data/pokemon-stat"; -import { - CommandPhase, EnemyCommandPhase, SelectTargetPhase, - TurnStartPhase -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, EnemyCommandPhase, SelectTargetPhase, TurnStartPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import TargetSelectUiHandler from "#app/ui/target-select-ui-handler"; import { Mode } from "#app/ui/ui"; diff --git a/src/test/battle/battle.test.ts b/src/test/battle/battle.test.ts index a91cd1e8352..a4713f90506 100644 --- a/src/test/battle/battle.test.ts +++ b/src/test/battle/battle.test.ts @@ -1,15 +1,19 @@ import { allSpecies } from "#app/data/pokemon-species"; +import { TempBattleStat } from "#app/data/temp-battle-stat.js"; import { GameModes } from "#app/game-mode"; import { getGameMode } from "#app/game-mode.js"; import { + BattleEndPhase, CommandPhase, DamagePhase, EncounterPhase, EnemyCommandPhase, LoginPhase, + NextEncounterPhase, SelectGenderPhase, SelectModifierPhase, SelectStarterPhase, SummonPhase, + SwitchPhase, TitlePhase, TurnInitPhase, VictoryPhase, } from "#app/phases"; @@ -23,6 +27,7 @@ import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; describe("Test Battle Phase", () => { let phaserGame: Phaser.Game; @@ -85,7 +90,7 @@ describe("Test Battle Phase", () => { it("newGame one-liner", async() => { await game.startBattle(); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("do attack wave 3 - single battle - regular - OHKO", async() => { @@ -213,7 +218,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("1vs1", async() => { @@ -225,7 +230,7 @@ describe("Test Battle Phase", () => { Species.BLASTOISE, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("2vs2", async() => { @@ -239,7 +244,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("4vs2", async() => { @@ -255,7 +260,7 @@ describe("Test Battle Phase", () => { Species.GABITE, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("kill opponent pokemon", async() => { @@ -323,5 +328,31 @@ describe("Test Battle Phase", () => { await game.toNextWave(); expect(game.scene.currentBattle.waveIndex).toBeGreaterThan(waveIndex); }, 20000); + + it("does not force switch if active pokemon faints at same time as enemy mon and is revived in post-battle", async () => { + const moveToUse = Moves.TAKE_DOWN; + game.override + .battleType("single") + .starterSpecies(Species.SAWK) + .enemySpecies(Species.RATTATA) + .startingWave(1) + .startingLevel(100) + .moveset([moveToUse]) + .enemyMoveset(SPLASH_ONLY) + .startingHeldItems([{ name: "TEMP_STAT_BOOSTER", type: TempBattleStat.ACC }]); + + await game.startBattle(); + game.scene.getPlayerPokemon()!.hp = 1; + game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + + await game.phaseInterceptor.to(BattleEndPhase); + game.doRevivePokemon(0); // pretend max revive was picked + game.doSelectModifier(); + + game.onNextPrompt("SwitchPhase", Mode.PARTY, () => { + expect.fail("Switch was forced"); + }, () => game.isCurrentPhase(NextEncounterPhase)); + await game.phaseInterceptor.to(SwitchPhase); + }, 20000); }); diff --git a/src/test/battle/double_battle.test.ts b/src/test/battle/double_battle.test.ts index e2d8dee562c..fb73fd70099 100644 --- a/src/test/battle/double_battle.test.ts +++ b/src/test/battle/double_battle.test.ts @@ -1,14 +1,11 @@ -import { - BattleEndPhase, - TurnInitPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition, } from "#app/test/utils/gameManagerUtils"; +import { BattleEndPhase, TurnInitPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition, } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import { Status, StatusEffect } from "#app/data/status-effect.js"; describe("Test Battle Phase", () => { @@ -43,8 +40,6 @@ describe("Test Battle Phase", () => { game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); for (const pokemon of game.scene.getPlayerField()) { - expect(pokemon).toBeDefined(); - pokemon.hp = 0; pokemon.status = new Status(StatusEffect.FAINT); expect(pokemon.isFainted()).toBe(true); diff --git a/src/test/battle/error-handling.test.ts b/src/test/battle/error-handling.test.ts index a823887b4f8..f244b57ce1b 100644 --- a/src/test/battle/error-handling.test.ts +++ b/src/test/battle/error-handling.test.ts @@ -1,4 +1,4 @@ -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/test/battle/special_battle.test.ts b/src/test/battle/special_battle.test.ts index e4243ea1418..6130df703f5 100644 --- a/src/test/battle/special_battle.test.ts +++ b/src/test/battle/special_battle.test.ts @@ -1,7 +1,5 @@ -import { - CommandPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; +import { CommandPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; @@ -42,7 +40,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 boss", async() => { @@ -54,7 +52,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async() => { @@ -66,7 +64,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 trainer", async() => { @@ -78,7 +76,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 rival", async() => { @@ -90,7 +88,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 rival", async() => { @@ -102,7 +100,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 1vs1 trainer", async() => { @@ -113,7 +111,7 @@ describe("Test Battle Phase", () => { Species.BLASTOISE, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async() => { @@ -125,7 +123,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 4vs2 trainer", async() => { @@ -139,7 +137,7 @@ describe("Test Battle Phase", () => { Species.GABITE, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); }); diff --git a/src/test/battlerTags/octolock.test.ts b/src/test/battlerTags/octolock.test.ts index 4c1c58d4345..369a84e21fa 100644 --- a/src/test/battlerTags/octolock.test.ts +++ b/src/test/battlerTags/octolock.test.ts @@ -6,7 +6,7 @@ import { StatChangePhase } from "#app/phases.js"; import { BattleStat } from "#app/data/battle-stat.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -jest.mock("#app/battle-scene.js"); +vi.mock("#app/battle-scene.js"); describe("BattlerTag - OctolockTag", () => { describe("lapse behavior", () => { @@ -49,7 +49,7 @@ describe("BattlerTag - OctolockTag", () => { it("cannot be added to pokemon who are octolocked", { timeout: 2000 }, async => { const mockPokemon = { - getTag: vi.fn().mockReturnValue(new BattlerTag(null, null, null, null)) as Pokemon["getTag"], + getTag: vi.fn().mockReturnValue(new BattlerTag(null!, null!, null!, null!)) as Pokemon["getTag"], } as Pokemon; const subject = new OctolockTag(1); diff --git a/src/test/battlerTags/stockpiling.test.ts b/src/test/battlerTags/stockpiling.test.ts index 5cf07c47ce1..005f1e1593c 100644 --- a/src/test/battlerTags/stockpiling.test.ts +++ b/src/test/battlerTags/stockpiling.test.ts @@ -27,7 +27,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["levels"]).toEqual(1); expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); }); subject.onAdd(mockPokemon); @@ -54,7 +54,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["levels"]).toEqual(1); expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); }); subject.onAdd(mockPokemon); @@ -79,7 +79,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["levels"]).toEqual(1); expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); }); subject.onOverlap(mockPokemon); @@ -109,7 +109,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); // def doesn't change - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.SPDEF], [1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.SPDEF], [1]); }); subject.onAdd(mockPokemon); @@ -121,7 +121,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); // def doesn't change - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.SPDEF], [1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.SPDEF], [1]); }); subject.onOverlap(mockPokemon); diff --git a/src/test/eggs/egg.test.ts b/src/test/eggs/egg.test.ts index 91d1153edfc..0bc2972e2dc 100644 --- a/src/test/eggs/egg.test.ts +++ b/src/test/eggs/egg.test.ts @@ -1,4 +1,4 @@ -import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import BattleScene from "../../battle-scene"; import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg.js"; import { Species } from "#enums/species"; @@ -6,7 +6,7 @@ import Phaser from "phaser"; import { EggSourceType } from "#app/enums/egg-source-types.js"; import { EggTier } from "#app/enums/egg-type.js"; import { VariantTier } from "#app/enums/variant-tiers.js"; -import GameManager from "../utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import EggData from "#app/system/egg-data.js"; import * as Utils from "#app/utils.js"; diff --git a/src/test/evolution.test.ts b/src/test/evolution.test.ts index 70682666f32..b1764a67ad7 100644 --- a/src/test/evolution.test.ts +++ b/src/test/evolution.test.ts @@ -1,7 +1,7 @@ -import { pokemonEvolutions } from "#app/data/pokemon-evolutions.js"; +import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } from "#app/data/pokemon-evolutions.js"; import { Abilities } from "#app/enums/abilities.js"; import { Species } from "#app/enums/species.js"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -64,7 +64,7 @@ describe("Evolution", () => { it("should handle illegal abilityIndex values", async () => { await game.runToSummon([Species.SQUIRTLE]); - const squirtle = game.scene.getPlayerPokemon(); + const squirtle = game.scene.getPlayerPokemon()!; squirtle.abilityIndex = 5; squirtle.evolve(pokemonEvolutions[Species.SQUIRTLE][0], squirtle.getSpeciesForm()); @@ -74,7 +74,7 @@ describe("Evolution", () => { it("should handle nincada's unique evolution", async () => { await game.runToSummon([Species.NINCADA]); - const nincada = game.scene.getPlayerPokemon(); + const nincada = game.scene.getPlayerPokemon()!; nincada.abilityIndex = 2; nincada.evolve(pokemonEvolutions[Species.NINCADA][0], nincada.getSpeciesForm()); @@ -83,4 +83,10 @@ describe("Evolution", () => { expect(ninjask.abilityIndex).toBe(2); expect(shedinja.abilityIndex).toBe(1); }, TIMEOUT); + + it("should set wild delay to NONE by default", () => { + const speciesFormEvo = new SpeciesFormEvolution(Species.ABRA, null, null, 1000, null, null); + + expect(speciesFormEvo.wildDelay).toBe(SpeciesWildEvolutionDelay.NONE); + }); }); diff --git a/src/test/evolutions/evolutions.test.ts b/src/test/evolutions/evolutions.test.ts index 4e38e72bb7c..af43e91b059 100644 --- a/src/test/evolutions/evolutions.test.ts +++ b/src/test/evolutions/evolutions.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; import * as Utils from "#app/utils"; @@ -34,14 +34,14 @@ describe("Evolution tests", () => { * 1, 2 or 3, it's a 4 family maushold */ await game.startBattle([Species.TANDEMAUS]); // starts us off with a tandemaus - const playerPokemon = game.scene.getPlayerPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.level = 25; // tandemaus evolves at level 25 vi.spyOn(Utils, "randSeedInt").mockReturnValue(0); // setting the random generator to be 0 to force a three family maushold - const threeForm = playerPokemon.getEvolution(); + const threeForm = playerPokemon.getEvolution()!; expect(threeForm.evoFormKey).toBe("three"); // as per pokemon-forms, the evoFormKey for 3 family mausholds is "three" for (let f = 1; f < 4; f++) { vi.spyOn(Utils, "randSeedInt").mockReturnValue(f); // setting the random generator to 1, 2 and 3 to force 4 family mausholds - const fourForm = playerPokemon.getEvolution(); + const fourForm = playerPokemon.getEvolution()!; expect(fourForm.evoFormKey).toBe(null); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is null } }, 5000); diff --git a/src/test/field/pokemon.test.ts b/src/test/field/pokemon.test.ts new file mode 100644 index 00000000000..2220a6f613e --- /dev/null +++ b/src/test/field/pokemon.test.ts @@ -0,0 +1,31 @@ +import { Species } from "#app/enums/species.js"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import GameManager from "../utils/gameManager"; + +describe("Spec - Pokemon", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + }); + + it("should not crash when trying to set status of undefined", async () => { + await game.runToSummon([Species.ABRA]); + + const pkm = game.scene.getPlayerPokemon()!; + expect(pkm).toBeDefined(); + + expect(pkm.trySetStatus(undefined)).toBe(true); + }); +}); diff --git a/src/test/final_boss.test.ts b/src/test/final_boss.test.ts index bc950e45767..c087bd5ba82 100644 --- a/src/test/final_boss.test.ts +++ b/src/test/final_boss.test.ts @@ -35,7 +35,7 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(Biome.END); - expect(game.scene.getEnemyPokemon().species.speciesId).toBe(Species.ETERNATUS); + expect(game.scene.getEnemyPokemon()!.species.speciesId).toBe(Species.ETERNATUS); }); it("should NOT spawn Eternatus before wave 200 in END biome", async () => { @@ -44,7 +44,7 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).not.toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(Biome.END); - expect(game.scene.getEnemyPokemon().species.speciesId).not.toBe(Species.ETERNATUS); + expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(Species.ETERNATUS); }); it("should NOT spawn Eternatus outside of END biome", async () => { @@ -53,7 +53,7 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).not.toBe(Biome.END); - expect(game.scene.getEnemyPokemon().species.speciesId).not.toBe(Species.ETERNATUS); + expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(Species.ETERNATUS); }); it.todo("should change form on direct hit down to last boss fragment", () => {}); diff --git a/src/test/game-mode.test.ts b/src/test/game-mode.test.ts index 04376c20361..4a1960a05ff 100644 --- a/src/test/game-mode.test.ts +++ b/src/test/game-mode.test.ts @@ -1,13 +1,5 @@ import { GameMode, GameModes, getGameMode } from "#app/game-mode.js"; -import { - afterEach, - beforeAll, - beforeEach, - describe, - expect, - it, - vi, -} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import GameManager from "./utils/gameManager"; import * as Utils from "../utils"; describe("game-mode", () => { diff --git a/src/test/imports.test.ts b/src/test/imports.test.ts index 5c0272e191d..69c145236bc 100644 --- a/src/test/imports.test.ts +++ b/src/test/imports.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it} from "vitest"; -import {initStatsKeys} from "#app/ui/game-stats-ui-handler"; +import { describe, expect, it } from "vitest"; +import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; async function importModule() { try { diff --git a/src/test/inputs/inputs.test.ts b/src/test/inputs/inputs.test.ts index 5acee145011..7182ac2c02c 100644 --- a/src/test/inputs/inputs.test.ts +++ b/src/test/inputs/inputs.test.ts @@ -1,9 +1,9 @@ -import {afterEach, beforeAll, beforeEach, describe, expect, it} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; -import InputsHandler from "#app/test/utils/inputsHandler"; +import InputsHandler from "#test/utils/inputsHandler"; describe("Inputs", () => { diff --git a/src/test/internals.test.ts b/src/test/internals.test.ts index a54b8b01544..d186849830d 100644 --- a/src/test/internals.test.ts +++ b/src/test/internals.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Species } from "#app/enums/species.js"; import { Abilities } from "#app/enums/abilities.js"; @@ -24,7 +24,7 @@ describe("Internals", () => { it("should provide Eevee with 3 defined abilities", async () => { await game.runToSummon([Species.EEVEE]); - const eevee = game.scene.getPlayerPokemon(); + const eevee = game.scene.getPlayerPokemon()!; expect(eevee.getSpeciesForm().getAbilityCount()).toBe(3); @@ -35,7 +35,7 @@ describe("Internals", () => { it("should set Eeeve abilityIndex between 0-2", async () => { await game.runToSummon([Species.EEVEE]); - const eevee = game.scene.getPlayerPokemon(); + const eevee = game.scene.getPlayerPokemon()!; expect(eevee.abilityIndex).toBeGreaterThanOrEqual(0); expect(eevee.abilityIndex).toBeLessThanOrEqual(2); diff --git a/src/test/items/eviolite.test.ts b/src/test/items/eviolite.test.ts index 5b18cc3a249..0fe90866de8 100644 --- a/src/test/items/eviolite.test.ts +++ b/src/test/items/eviolite.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#app/data/pokemon-stat"; import { EvolutionStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; import Phase from "phaser"; diff --git a/src/test/items/exp_booster.test.ts b/src/test/items/exp_booster.test.ts index e80b12d7fc9..2b700c92086 100644 --- a/src/test/items/exp_booster.test.ts +++ b/src/test/items/exp_booster.test.ts @@ -1,6 +1,6 @@ import { Abilities } from "#app/enums/abilities.js"; import { PokemonExpBoosterModifier } from "#app/modifier/modifier.js"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -27,13 +27,14 @@ describe("EXP Modifier Items", () => { game.override.battleType("single"); }); - it("EXP booster items stack additively", async() => { - game.override.startingHeldItems([{name: "LUCKY_EGG"}, {name: "GOLDEN_EGG"}]); + it("EXP booster items stack multiplicatively", async() => { + game.override.startingHeldItems([{name: "LUCKY_EGG", count: 3}, {name: "GOLDEN_EGG"}]); await game.startBattle(); - const partyMember = game.scene.getPlayerPokemon(); - const modifierBonusExp = new Utils.NumberHolder(1); - partyMember.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, modifierBonusExp); - expect(modifierBonusExp.value).toBe(2.4); + const partyMember = game.scene.getPlayerPokemon()!; + partyMember.exp = 100; + const expHolder = new Utils.NumberHolder(partyMember.exp); + partyMember.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, expHolder); + expect(expHolder.value).toBe(440); }, 20000); }); diff --git a/src/test/items/grip_claw.test.ts b/src/test/items/grip_claw.test.ts index 495bc5de223..40ef81fed73 100644 --- a/src/test/items/grip_claw.test.ts +++ b/src/test/items/grip_claw.test.ts @@ -5,10 +5,10 @@ import { BerryType } from "#app/enums/berry-type.js"; import { Moves } from "#app/enums/moves.js"; import { Species } from "#app/enums/species.js"; import { CommandPhase, MoveEndPhase, SelectTargetPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { getMovePosition } from "../utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; // 20 seconds @@ -54,11 +54,7 @@ describe("Items - Grip Claw", () => { async () => { await game.startBattle([Species.PANSEAR, Species.ROWLET, Species.PANPOUR, Species.PANSAGE, Species.CHARMANDER, Species.SQUIRTLE]); - const playerPokemon = game.scene.getPlayerField(); - playerPokemon.forEach(p => expect(p).toBeDefined()); - const enemyPokemon = game.scene.getEnemyField(); - enemyPokemon.forEach(p => expect(p).toBeDefined()); const enemyHeldItemCt = enemyPokemon.map(p => p.getHeldItems.length); diff --git a/src/test/items/leek.test.ts b/src/test/items/leek.test.ts index 73444a54c30..7ac453f797a 100644 --- a/src/test/items/leek.test.ts +++ b/src/test/items/leek.test.ts @@ -1,13 +1,14 @@ import { BattlerIndex } from "#app/battle"; import { CritBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { MoveEffectPhase, TurnStartPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; +import { MoveEffectPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { mockTurnOrder } from "#test/utils/testUtils"; describe("Items - Leek", () => { let phaserGame: Phaser.Game; @@ -43,9 +44,7 @@ describe("Items - Leek", () => { game.doAttack(0); - await game.phaseInterceptor.to(TurnStartPhase, false); - - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase); @@ -57,7 +56,7 @@ describe("Items - Leek", () => { Species.FARFETCHD ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); @@ -77,7 +76,7 @@ describe("Items - Leek", () => { Species.GALAR_FARFETCHD ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); @@ -97,7 +96,7 @@ describe("Items - Leek", () => { Species.SIRFETCHD ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); @@ -187,7 +186,7 @@ describe("Items - Leek", () => { Species.PIKACHU ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); diff --git a/src/test/items/leftovers.test.ts b/src/test/items/leftovers.test.ts index 8c9195b07af..e791c4426a1 100644 --- a/src/test/items/leftovers.test.ts +++ b/src/test/items/leftovers.test.ts @@ -1,6 +1,6 @@ import { DamagePhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -40,11 +40,7 @@ describe("Items - Leftovers", () => { // Make sure leftovers are there expect(game.scene.modifiers[0].type.id).toBe("LEFTOVERS"); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); - - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; // We should have full hp expect(leadPokemon.isFullHp()).toBe(true); diff --git a/src/test/items/light_ball.test.ts b/src/test/items/light_ball.test.ts index eb0c6cc044f..ff7dfa4eba5 100644 --- a/src/test/items/light_ball.test.ts +++ b/src/test/items/light_ball.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; import Phase from "phaser"; @@ -83,7 +83,7 @@ describe("Items - Light Ball", () => { expect(spAtkValue.value / spAtkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["LIGHT_BALL"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPATK, spAtkValue); @@ -122,7 +122,7 @@ describe("Items - Light Ball", () => { expect(spAtkValue.value / spAtkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["LIGHT_BALL"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPATK, spAtkValue); @@ -161,7 +161,7 @@ describe("Items - Light Ball", () => { expect(spAtkValue.value / spAtkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["LIGHT_BALL"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPATK, spAtkValue); @@ -189,7 +189,7 @@ describe("Items - Light Ball", () => { expect(spAtkValue.value / spAtkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["LIGHT_BALL"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPATK, spAtkValue); diff --git a/src/test/items/metal_powder.test.ts b/src/test/items/metal_powder.test.ts index 937fd8feb62..966762e4175 100644 --- a/src/test/items/metal_powder.test.ts +++ b/src/test/items/metal_powder.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; import Phase from "phaser"; @@ -79,7 +79,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["METAL_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(2); @@ -112,7 +112,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["METAL_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(2); @@ -145,7 +145,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["METAL_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(2); @@ -167,7 +167,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["METAL_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(1); diff --git a/src/test/items/quick_powder.test.ts b/src/test/items/quick_powder.test.ts index e662b398cd4..d2435dab431 100644 --- a/src/test/items/quick_powder.test.ts +++ b/src/test/items/quick_powder.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; import Phase from "phaser"; @@ -79,7 +79,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["QUICK_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(2); @@ -112,7 +112,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["QUICK_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(2); @@ -145,7 +145,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["QUICK_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(2); @@ -167,7 +167,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["QUICK_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(1); diff --git a/src/test/items/scope_lens.test.ts b/src/test/items/scope_lens.test.ts index c9fb3ba52d3..7e5c57c1364 100644 --- a/src/test/items/scope_lens.test.ts +++ b/src/test/items/scope_lens.test.ts @@ -1,13 +1,14 @@ import { BattlerIndex } from "#app/battle"; import { CritBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { MoveEffectPhase, TurnStartPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; +import { MoveEffectPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { mockTurnOrder } from "#test/utils/testUtils"; describe("Items - Scope Lens", () => { let phaserGame: Phaser.Game; @@ -42,10 +43,7 @@ describe("Items - Scope Lens", () => { ]); game.doAttack(0); - - await game.phaseInterceptor.to(TurnStartPhase, false); - - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.phaseInterceptor.to(MoveEffectPhase); @@ -57,7 +55,7 @@ describe("Items - Scope Lens", () => { Species.GASTLY ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); diff --git a/src/test/items/thick_club.test.ts b/src/test/items/thick_club.test.ts index 13714c441c0..841cd7c90ac 100644 --- a/src/test/items/thick_club.test.ts +++ b/src/test/items/thick_club.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; import Phase from "phaser"; @@ -79,7 +79,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -101,7 +101,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -123,7 +123,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -160,7 +160,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -197,7 +197,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -219,7 +219,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(1); diff --git a/src/test/items/toxic_orb.test.ts b/src/test/items/toxic_orb.test.ts index 2f2b7e4e16a..69f55cb2bbc 100644 --- a/src/test/items/toxic_orb.test.ts +++ b/src/test/items/toxic_orb.test.ts @@ -1,13 +1,8 @@ import { StatusEffect } from "#app/data/status-effect"; -import { - CommandPhase, - EnemyCommandPhase, - MessagePhase, - TurnEndPhase, -} from "#app/phases"; +import { CommandPhase, EnemyCommandPhase, MessagePhase, TurnEndPhase } from "#app/phases"; import i18next, { initI18n } from "#app/plugins/i18n"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; @@ -77,6 +72,6 @@ describe("Items - Toxic orb", () => { const message2 = game.textInterceptor.getLatestMessage(); expect(message2).toContain("is hurt"); expect(message2).toContain("by poison"); - expect(game.scene.getParty()[0].status.effect).toBe(StatusEffect.TOXIC); + expect(game.scene.getParty()[0].status!.effect).toBe(StatusEffect.TOXIC); }, 20000); }); diff --git a/src/test/localization/battle-stat.test.ts b/src/test/localization/battle-stat.test.ts index ea2714b3852..b99ed2b7064 100644 --- a/src/test/localization/battle-stat.test.ts +++ b/src/test/localization/battle-stat.test.ts @@ -1,27 +1,26 @@ -import {beforeAll, describe, expect, it} from "vitest"; -import {getBattleStatName, getBattleStatLevelChangeDescription} from "#app/data/battle-stat.js"; -import {BattleStat} from "#app/data/battle-stat.js"; -import {pokemonInfo as enPokemonInfo} from "#app/locales/en/pokemon-info.js"; -import {battle as enBattleStat} from "#app/locales/en/battle.js"; -import {pokemonInfo as dePokemonInfo} from "#app/locales/de/pokemon-info.js"; -import {battle as deBattleStat} from "#app/locales/de/battle.js"; -import {pokemonInfo as esPokemonInfo} from "#app/locales/es/pokemon-info.js"; -import {battle as esBattleStat} from "#app/locales/es/battle.js"; -import {pokemonInfo as frPokemonInfo} from "#app/locales/fr/pokemon-info.js"; -import {battle as frBattleStat} from "#app/locales/fr/battle.js"; -import {pokemonInfo as itPokemonInfo} from "#app/locales/it/pokemon-info.js"; -import {battle as itBattleStat} from "#app/locales/it/battle.js"; -import {pokemonInfo as koPokemonInfo} from "#app/locales/ko/pokemon-info.js"; -import {battle as koBattleStat} from "#app/locales/ko/battle.js"; -import {pokemonInfo as ptBrPokemonInfo} from "#app/locales/pt_BR/pokemon-info.js"; -import {battle as ptBrBattleStat} from "#app/locales/pt_BR/battle.js"; -import {pokemonInfo as zhCnPokemonInfo} from "#app/locales/zh_CN/pokemon-info.js"; -import {battle as zhCnBattleStat} from "#app/locales/zh_CN/battle.js"; -import {pokemonInfo as zhTwPokemonInfo} from "#app/locales/zh_TW/pokemon-info.js"; -import {battle as zhTwBattleStat} from "#app/locales/zh_TW/battle.js"; - -import i18next, {initI18n} from "#app/plugins/i18n"; -import {KoreanPostpositionProcessor} from "i18next-korean-postposition-processor"; +import { beforeAll, describe, expect, it } from "vitest"; +import { getBattleStatName, getBattleStatLevelChangeDescription } from "#app/data/battle-stat.js"; +import { BattleStat} from "#app/data/battle-stat.js"; +import { pokemonInfo as enPokemonInfo } from "#app/locales/en/pokemon-info.js"; +import { battle as enBattleStat } from "#app/locales/en/battle.js"; +import { pokemonInfo as dePokemonInfo } from "#app/locales/de/pokemon-info.js"; +import { battle as deBattleStat } from "#app/locales/de/battle.js"; +import { pokemonInfo as esPokemonInfo } from "#app/locales/es/pokemon-info.js"; +import { battle as esBattleStat } from "#app/locales/es/battle.js"; +import { pokemonInfo as frPokemonInfo } from "#app/locales/fr/pokemon-info.js"; +import { battle as frBattleStat } from "#app/locales/fr/battle.js"; +import { pokemonInfo as itPokemonInfo } from "#app/locales/it/pokemon-info.js"; +import { battle as itBattleStat } from "#app/locales/it/battle.js"; +import { pokemonInfo as koPokemonInfo } from "#app/locales/ko/pokemon-info.js"; +import { battle as koBattleStat } from "#app/locales/ko/battle.js"; +import { pokemonInfo as ptBrPokemonInfo } from "#app/locales/pt_BR/pokemon-info.js"; +import { battle as ptBrBattleStat } from "#app/locales/pt_BR/battle.js"; +import { pokemonInfo as zhCnPokemonInfo } from "#app/locales/zh_CN/pokemon-info.js"; +import { battle as zhCnBattleStat } from "#app/locales/zh_CN/battle.js"; +import { pokemonInfo as zhTwPokemonInfo } from "#app/locales/zh_TW/pokemon-info.js"; +import { battle as zhTwBattleStat } from "#app/locales/zh_TW/battle.js"; +import i18next, { initI18n } from "#app/plugins/i18n"; +import { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor"; interface BattleStatTestUnit { stat: BattleStat, diff --git a/src/test/localization/french.test.ts b/src/test/localization/french.test.ts index 45429b1b157..b03a8ee64e8 100644 --- a/src/test/localization/french.test.ts +++ b/src/test/localization/french.test.ts @@ -1,9 +1,9 @@ -import {afterEach, beforeAll, describe, expect, it} from "vitest"; +import { afterEach, beforeAll, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import {Species} from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { Species } from "#enums/species"; import i18next from "i18next"; -import {initI18n} from "#app/plugins/i18n"; +import { initI18n } from "#app/plugins/i18n"; describe("Lokalization - french", () => { let phaserGame: Phaser.Game; diff --git a/src/test/localization/status-effect.test.ts b/src/test/localization/status-effect.test.ts index ad33a59a675..8a9effe1672 100644 --- a/src/test/localization/status-effect.test.ts +++ b/src/test/localization/status-effect.test.ts @@ -1,14 +1,7 @@ import { beforeAll, describe, afterEach, expect, it, vi } from "vitest"; -import { - StatusEffect, - getStatusEffectActivationText, - getStatusEffectDescriptor, - getStatusEffectHealText, - getStatusEffectObtainText, - getStatusEffectOverlapText, -} from "#app/data/status-effect"; +import { StatusEffect, getStatusEffectActivationText, getStatusEffectDescriptor, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect"; import i18next from "i18next"; -import { mockI18next } from "../utils/testUtils"; +import { mockI18next } from "#test/utils/testUtils"; const pokemonName = "PKM"; const sourceText = "SOURCE"; diff --git a/src/test/localization/terrain.test.ts b/src/test/localization/terrain.test.ts index 6450d8574be..c072f9cc9ab 100644 --- a/src/test/localization/terrain.test.ts +++ b/src/test/localization/terrain.test.ts @@ -1,11 +1,11 @@ import { TerrainType, getTerrainName } from "#app/data/terrain"; import { getTerrainBlockMessage, getTerrainClearMessage, getTerrainStartMessage } from "#app/data/weather"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { mockI18next } from "../utils/testUtils"; +import { mockI18next } from "#test/utils/testUtils"; describe("terrain", () => { let phaserGame: Phaser.Game; @@ -37,18 +37,18 @@ describe("terrain", () => { mockI18next(); const text = getTerrainStartMessage(terrainType); - expect(text).toBe(undefined); + expect(text).toBeNull(); }); it("should return the clear text", () => { mockI18next(); const text = getTerrainClearMessage(terrainType); - expect(text).toBe(undefined); + expect(text).toBeNull(); }); it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:defaultBlockMessage"); @@ -80,7 +80,7 @@ describe("terrain", () => { it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:mistyBlockMessage"); @@ -112,7 +112,7 @@ describe("terrain", () => { it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:defaultBlockMessage"); @@ -144,7 +144,7 @@ describe("terrain", () => { it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:defaultBlockMessage"); @@ -176,7 +176,7 @@ describe("terrain", () => { it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:defaultBlockMessage"); diff --git a/src/test/moves/astonish.test.ts b/src/test/moves/astonish.test.ts index c8c013026b3..358e4a9bec3 100644 --- a/src/test/moves/astonish.test.ts +++ b/src/test/moves/astonish.test.ts @@ -6,8 +6,8 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; @@ -43,11 +43,9 @@ describe("Moves - Astonish", () => { async () => { await game.startBattle([Species.MEOWSCARADA]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.ASTONISH)); diff --git a/src/test/moves/aurora_veil.test.ts b/src/test/moves/aurora_veil.test.ts index 80da8087576..a10c9b6b60a 100644 --- a/src/test/moves/aurora_veil.test.ts +++ b/src/test/moves/aurora_veil.test.ts @@ -4,11 +4,9 @@ import { WeatherType } from "#app/data/weather.js"; import { Abilities } from "#app/enums/abilities.js"; import { ArenaTagType } from "#app/enums/arena-tag-type.js"; import Pokemon from "#app/field/pokemon.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { NumberHolder } from "#app/utils.js"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -51,7 +49,7 @@ describe("Moves - Aurora Veil", () => { game.doAttack(getMovePosition(game.scene, 0, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); @@ -66,7 +64,7 @@ describe("Moves - Aurora Veil", () => { game.doAttack(getMovePosition(game.scene, 1, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); @@ -79,7 +77,7 @@ describe("Moves - Aurora Veil", () => { await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); @@ -94,7 +92,7 @@ describe("Moves - Aurora Veil", () => { game.doAttack(getMovePosition(game.scene, 1, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); diff --git a/src/test/moves/baton_pass.test.ts b/src/test/moves/baton_pass.test.ts new file mode 100644 index 00000000000..9f0cb3619b2 --- /dev/null +++ b/src/test/moves/baton_pass.test.ts @@ -0,0 +1,94 @@ +import { BattleStat } from "#app/data/battle-stat.js"; +import { PostSummonPhase, TurnEndPhase } from "#app/phases.js"; +import GameManager from "#app/test/utils/gameManager"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; + + +describe("Moves - Baton Pass", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.DUGTRIO) + .startingLevel(1) + .startingWave(97) + .moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH]) + .enemyMoveset(SPLASH_ONLY) + .disableCrits(); + }); + + it("passes stat stage buffs when player uses it", async() => { + // arrange + await game.startBattle([ + Species.RAICHU, + Species.SHUCKLE + ]); + + // round 1 - buff + game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT)); + await game.toNextTurn(); + expect(game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2); + + // round 2 - baton pass + game.doAttack(getMovePosition(game.scene, 0, Moves.BATON_PASS)); + game.doSelectPartyPokemon(1); + await game.phaseInterceptor.to(TurnEndPhase); + + // assert + const playerPkm = game.scene.getPlayerPokemon()!; + expect(playerPkm.species.speciesId).toEqual(Species.SHUCKLE); + expect(playerPkm.summonData.battleStats[BattleStat.SPATK]).toEqual(2); + }, 20000); + + it("passes stat stage buffs when AI uses it", async() => { + // arrange + game.override + .startingWave(5) + .enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT])); + await game.startBattle([ + Species.RAICHU, + Species.SHUCKLE + ]); + + // round 1 - ai buffs + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.toNextTurn(); + + // round 2 - baton pass + game.scene.getEnemyPokemon()!.hp = 100; + game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS)); + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.phaseInterceptor.to(PostSummonPhase, false); + + // assert + // check buffs are still there + expect(game.scene.getEnemyPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2); + // confirm that a switch actually happened. can't use species because I + // can't find a way to override trainer parties with more than 1 pokemon species + expect(game.scene.getEnemyPokemon()!.hp).not.toEqual(100); + expect(game.phaseInterceptor.log.slice(-4)).toEqual([ + "MoveEffectPhase", + "SwitchSummonPhase", + "SummonPhase", + "PostSummonPhase" + ]); + }, 20000); +}); diff --git a/src/test/moves/beat_up.test.ts b/src/test/moves/beat_up.test.ts index 751b91fefa3..a5e4b3cbd34 100644 --- a/src/test/moves/beat_up.test.ts +++ b/src/test/moves/beat_up.test.ts @@ -1,11 +1,10 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import Overrides from "#app/overrides"; +import GameManager from "#test/utils/gameManager"; import { Species } from "#app/enums/species.js"; import { Moves } from "#app/enums/moves.js"; import { Abilities } from "#app/enums/abilities.js"; -import { getMovePosition } from "../utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { MoveEffectPhase } from "#app/phases.js"; import { StatusEffect } from "#app/enums/status-effect.js"; @@ -27,15 +26,15 @@ describe("Moves - Beat Up", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("single"); + game.override.battleType("single"); - vi.spyOn(Overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX); - vi.spyOn(Overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100); - vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue(Array(4).fill(Moves.SPLASH)); - vi.spyOn(Overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA); + game.override.enemySpecies(Species.SNORLAX); + game.override.enemyLevel(100); + game.override.enemyMoveset(Array(4).fill(Moves.SPLASH)); + game.override.enemyAbility(Abilities.INSOMNIA); - vi.spyOn(Overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100); - vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.BEAT_UP]); + game.override.startingLevel(100); + game.override.moveset([Moves.BEAT_UP]); }); it( @@ -43,8 +42,8 @@ describe("Moves - Beat Up", () => { async () => { await game.startBattle([Species.MAGIKARP, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.PIKACHU, Species.EEVEE]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; let enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); @@ -67,7 +66,7 @@ describe("Moves - Beat Up", () => { async () => { await game.startBattle([Species.MAGIKARP, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.PIKACHU, Species.EEVEE]); - const playerPokemon = game.scene.getPlayerPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; game.scene.getParty()[1].trySetStatus(StatusEffect.BURN); @@ -82,11 +81,11 @@ describe("Moves - Beat Up", () => { it( "should hit twice for each player Pokemon if the user has Multi-Lens", async () => { - vi.spyOn(Overrides, "STARTING_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); await game.startBattle([Species.MAGIKARP, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.PIKACHU, Species.EEVEE]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; let enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); diff --git a/src/test/moves/belly_drum.test.ts b/src/test/moves/belly_drum.test.ts index 5a9ddd41f0f..74afc910faf 100644 --- a/src/test/moves/belly_drum.test.ts +++ b/src/test/moves/belly_drum.test.ts @@ -1,11 +1,8 @@ -import {afterEach, beforeAll, beforeEach, describe, expect, test, vi} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; -import { - TurnEndPhase, -} from "#app/phases"; -import {getMovePosition} from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { TurnEndPhase } from "#app/phases"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { BattleStat } from "#app/data/battle-stat"; @@ -32,10 +29,10 @@ describe("Moves - BELLY DRUM", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "STARTER_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX); - vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100); - vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100); + game.override.starterSpecies(Species.MAGIKARP); + game.override.enemySpecies(Species.SNORLAX); + game.override.startingLevel(100); + game.override.enemyLevel(100); game.override.moveset([Moves.BELLY_DRUM]); game.override.enemyMoveset([Moves.SPLASH]); }); @@ -46,8 +43,7 @@ describe("Moves - BELLY DRUM", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); @@ -62,8 +58,7 @@ describe("Moves - BELLY DRUM", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); // Here - BattleStat.ATK -> -3 and BattleStat.SPATK -> 6 @@ -83,8 +78,7 @@ describe("Moves - BELLY DRUM", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; @@ -100,8 +94,7 @@ describe("Moves - BELLY DRUM", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/src/test/moves/ceaseless_edge.test.ts b/src/test/moves/ceaseless_edge.test.ts index ab3da3d51b8..c5ce8375102 100644 --- a/src/test/moves/ceaseless_edge.test.ts +++ b/src/test/moves/ceaseless_edge.test.ts @@ -2,12 +2,9 @@ import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { - MoveEffectPhase, - TurnEndPhase -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { MoveEffectPhase, TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -48,11 +45,7 @@ describe("Moves - Ceaseless Edge", () => { async () => { await game.startBattle([ Species.ILLUMISE ]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); - - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; @@ -77,11 +70,7 @@ describe("Moves - Ceaseless Edge", () => { game.override.startingHeldItems([{name: "MULTI_LENS"}]); await game.startBattle([ Species.ILLUMISE ]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); - - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; @@ -108,12 +97,6 @@ describe("Moves - Ceaseless Edge", () => { await game.startBattle([ Species.ILLUMISE ]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); - - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE)); await game.phaseInterceptor.to(MoveEffectPhase, false); // Spikes should not have any layers before move effect is applied diff --git a/src/test/moves/clangorous_soul.test.ts b/src/test/moves/clangorous_soul.test.ts index 1b3d16f402f..5493466ba56 100644 --- a/src/test/moves/clangorous_soul.test.ts +++ b/src/test/moves/clangorous_soul.test.ts @@ -1,19 +1,17 @@ -import {afterEach, beforeAll, beforeEach, describe, expect, test, vi} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; -import { - TurnEndPhase, -} from "#app/phases"; -import {getMovePosition} from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { TurnEndPhase } from "#app/phases"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { BattleStat } from "#app/data/battle-stat"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; -// RATIO : HP Cost of Move +/** HP Cost of Move */ const RATIO = 3; -// PREDAMAGE : Amount of extra HP lost +/** Amount of extra HP lost */ const PREDAMAGE = 15; describe("Moves - CLANGOROUS_SOUL", () => { @@ -32,12 +30,12 @@ describe("Moves - CLANGOROUS_SOUL", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "STARTER_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX); - vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100); - vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100); + game.override.starterSpecies(Species.MAGIKARP); + game.override.enemySpecies(Species.SNORLAX); + game.override.startingLevel(100); + game.override.enemyLevel(100); game.override.moveset([Moves.CLANGOROUS_SOUL]); - game.override.enemyMoveset([Moves.SPLASH]); + game.override.enemyMoveset(SPLASH_ONLY); }); //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Clangorous_Soul_(move) @@ -46,8 +44,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); @@ -66,8 +63,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); //Here - BattleStat.SPD -> 0 and BattleStat.SPDEF -> 4 @@ -92,8 +88,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; leadPokemon.summonData.battleStats[BattleStat.DEF] = 6; @@ -117,8 +112,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/src/test/moves/double_team.test.ts b/src/test/moves/double_team.test.ts index 55820ee957b..2153b856517 100644 --- a/src/test/moves/double_team.test.ts +++ b/src/test/moves/double_team.test.ts @@ -1,10 +1,8 @@ import { BattleStat } from "#app/data/battle-stat.js"; import { Abilities } from "#app/enums/abilities.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -38,8 +36,8 @@ describe("Moves - Double Team", () => { it("increases the user's evasion by one stage.", async () => { await game.startBattle([Species.MAGIKARP]); - const ally = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const ally = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getAccuracyMultiplier"); expect(ally.summonData.battleStats[BattleStat.EVA]).toBe(0); diff --git a/src/test/moves/dragon_rage.test.ts b/src/test/moves/dragon_rage.test.ts index b38a6f16513..6ec7521f678 100644 --- a/src/test/moves/dragon_rage.test.ts +++ b/src/test/moves/dragon_rage.test.ts @@ -4,14 +4,14 @@ import { Species } from "#app/enums/species.js"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; import { TurnEndPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Dragon Rage", () => { let phaserGame: Phaser.Game; @@ -35,7 +35,6 @@ describe("Moves - Dragon Rage", () => { game = new GameManager(phaserGame); game.override.battleType("single"); - game.override.disableCrits(); game.override.starterSpecies(Species.SNORLAX); game.override.moveset([Moves.DRAGON_RAGE]); @@ -52,7 +51,7 @@ describe("Moves - Dragon Rage", () => { await game.startBattle(); partyPokemon = game.scene.getParty()[0]; - enemyPokemon = game.scene.getEnemyPokemon(); + enemyPokemon = game.scene.getEnemyPokemon()!; // remove berries game.scene.removePartyMemberModifiers(0); @@ -60,6 +59,7 @@ describe("Moves - Dragon Rage", () => { }); it("ignores weaknesses", async () => { + game.override.disableCrits(); vi.spyOn(enemyPokemon, "getTypes").mockReturnValue([Type.DRAGON]); game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); @@ -70,6 +70,7 @@ describe("Moves - Dragon Rage", () => { }); it("ignores resistances", async () => { + game.override.disableCrits(); vi.spyOn(enemyPokemon, "getTypes").mockReturnValue([Type.STEEL]); game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); @@ -80,6 +81,7 @@ describe("Moves - Dragon Rage", () => { }); it("ignores stat changes", async () => { + game.override.disableCrits(); partyPokemon.summonData.battleStats[BattleStat.SPATK] = 2; game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); @@ -90,6 +92,7 @@ describe("Moves - Dragon Rage", () => { }); it("ignores stab", async () => { + game.override.disableCrits(); vi.spyOn(partyPokemon, "getTypes").mockReturnValue([Type.DRAGON]); game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); @@ -100,7 +103,6 @@ describe("Moves - Dragon Rage", () => { }); it("ignores criticals", async () => { - partyPokemon.removeTag(BattlerTagType.NO_CRIT); partyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 99); game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); @@ -111,6 +113,7 @@ describe("Moves - Dragon Rage", () => { }); it("ignores damage modification from abilities such as ice scales", async () => { + game.override.disableCrits(); game.override.enemyAbility(Abilities.ICE_SCALES); game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); @@ -121,6 +124,7 @@ describe("Moves - Dragon Rage", () => { }); it("ignores multi hit", async () => { + game.override.disableCrits(); game.scene.addModifier(modifierTypes.MULTI_LENS().newModifier(partyPokemon), false); game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); diff --git a/src/test/moves/dragon_tail.test.ts b/src/test/moves/dragon_tail.test.ts new file mode 100644 index 00000000000..7374451e643 --- /dev/null +++ b/src/test/moves/dragon_tail.test.ts @@ -0,0 +1,166 @@ +import { allMoves } from "#app/data/move.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; +import { BattleEndPhase, BerryPhase, TurnEndPhase} from "#app/phases.js"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; +import GameManager from "../utils/gameManager"; +import { getMovePosition } from "../utils/gameManagerUtils"; +import { BattlerIndex } from "#app/battle.js"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Dragon Tail", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override.battleType("single") + .moveset([Moves.DRAGON_TAIL, Moves.SPLASH]) + .enemySpecies(Species.WAILORD) + .enemyMoveset(SPLASH_ONLY) + .startingLevel(5) + .enemyLevel(5); + + vi.spyOn(allMoves[Moves.DRAGON_TAIL], "accuracy", "get").mockReturnValue(100); + }); + + test( + "Single battle should cause opponent to flee, and not crash", + async () => { + await game.startBattle([Species.DRATINI]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + expect(enemyPokemon).toBeDefined(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + + await game.phaseInterceptor.to(BerryPhase); + + const isVisible = enemyPokemon.visible; + const hasFled = enemyPokemon.wildFlee; + expect(!isVisible && hasFled).toBe(true); + + // simply want to test that the game makes it this far without crashing + await game.phaseInterceptor.to(BattleEndPhase); + }, TIMEOUT + ); + + test( + "Single battle should cause opponent to flee, display ability, and not crash", + async () => { + game.override.enemyAbility(Abilities.ROUGH_SKIN); + await game.startBattle([Species.DRATINI]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + expect(leadPokemon).toBeDefined(); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + expect(enemyPokemon).toBeDefined(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + + await game.phaseInterceptor.to(BerryPhase); + + const isVisible = enemyPokemon.visible; + const hasFled = enemyPokemon.wildFlee; + expect(!isVisible && hasFled).toBe(true); + expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); + }, TIMEOUT + ); + + test( + "Double battles should proceed without crashing" , + async () => { + game.override.battleType("double").enemyMoveset(SPLASH_ONLY); + game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]) + .enemyAbility(Abilities.ROUGH_SKIN); + await game.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]); + + const leadPokemon = game.scene.getParty()[0]!; + const secPokemon = game.scene.getParty()[1]!; + expect(leadPokemon).toBeDefined(); + expect(secPokemon).toBeDefined(); + + const enemyLeadPokemon = game.scene.currentBattle.enemyParty[0]!; + const enemySecPokemon = game.scene.currentBattle.enemyParty[1]!; + expect(enemyLeadPokemon).toBeDefined(); + expect(enemySecPokemon).toBeDefined(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + game.doSelectTarget(BattlerIndex.ENEMY); + + game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + + await game.phaseInterceptor.to(TurnEndPhase); + + const isVisibleLead = enemyLeadPokemon.visible; + const hasFledLead = enemyLeadPokemon.wildFlee; + const isVisibleSec = enemySecPokemon.visible; + const hasFledSec = enemySecPokemon.wildFlee; + expect(!isVisibleLead && hasFledLead && isVisibleSec && !hasFledSec).toBe(true); + expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); + + // second turn + + game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.doSelectTarget(BattlerIndex.ENEMY_2); + game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + + await game.phaseInterceptor.to(BerryPhase); + expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp()); + }, TIMEOUT + ); + + test( + "Flee move redirection works" , + async () => { + game.override.battleType("double").enemyMoveset(SPLASH_ONLY); + game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]); + game.override.enemyAbility(Abilities.ROUGH_SKIN); + await game.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]); + + const leadPokemon = game.scene.getParty()[0]!; + const secPokemon = game.scene.getParty()[1]!; + expect(leadPokemon).toBeDefined(); + expect(secPokemon).toBeDefined(); + + const enemyLeadPokemon = game.scene.currentBattle.enemyParty[0]!; + const enemySecPokemon = game.scene.currentBattle.enemyParty[1]!; + expect(enemyLeadPokemon).toBeDefined(); + expect(enemySecPokemon).toBeDefined(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + game.doSelectTarget(BattlerIndex.ENEMY); + + // target the same pokemon, second move should be redirected after first flees + game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + game.doSelectTarget(BattlerIndex.ENEMY); + + await game.phaseInterceptor.to(BerryPhase); + + const isVisibleLead = enemyLeadPokemon.visible; + const hasFledLead = enemyLeadPokemon.wildFlee; + const isVisibleSec = enemySecPokemon.visible; + const hasFledSec = enemySecPokemon.wildFlee; + expect(!isVisibleLead && hasFledLead && !isVisibleSec && hasFledSec).toBe(true); + expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); + expect(secPokemon.hp).toBeLessThan(secPokemon.getMaxHp()); + expect(enemyLeadPokemon.hp).toBeLessThan(enemyLeadPokemon.getMaxHp()); + expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp()); + }, TIMEOUT + ); +}); diff --git a/src/test/moves/dynamax_cannon.test.ts b/src/test/moves/dynamax_cannon.test.ts index 4c010484c78..57846c1aef7 100644 --- a/src/test/moves/dynamax_cannon.test.ts +++ b/src/test/moves/dynamax_cannon.test.ts @@ -1,8 +1,8 @@ import { BattlerIndex } from "#app/battle"; import { allMoves } from "#app/data/move"; import { DamagePhase, MoveEffectPhase, TurnStartPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -81,7 +81,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(120); }, 20000); @@ -98,7 +98,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(140); }, 20000); @@ -115,7 +115,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(160); }, 20000); @@ -132,7 +132,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(180); }, 20000); @@ -149,7 +149,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); diff --git a/src/test/moves/fillet_away.test.ts b/src/test/moves/fillet_away.test.ts index 161bba2c284..6965ced46d9 100644 --- a/src/test/moves/fillet_away.test.ts +++ b/src/test/moves/fillet_away.test.ts @@ -1,19 +1,17 @@ -import {afterEach, beforeAll, beforeEach, describe, expect, test, vi} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; -import { - TurnEndPhase, -} from "#app/phases"; -import {getMovePosition} from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { TurnEndPhase } from "#app/phases"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { BattleStat } from "#app/data/battle-stat"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; -// RATIO : HP Cost of Move +/** HP Cost of Move */ const RATIO = 2; -// PREDAMAGE : Amount of extra HP lost +/** Amount of extra HP lost */ const PREDAMAGE = 15; describe("Moves - FILLET AWAY", () => { @@ -32,12 +30,12 @@ describe("Moves - FILLET AWAY", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "STARTER_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX); - vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100); - vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100); + game.override.starterSpecies(Species.MAGIKARP); + game.override.enemySpecies(Species.SNORLAX); + game.override.startingLevel(100); + game.override.enemyLevel(100); game.override.moveset([Moves.FILLET_AWAY]); - game.override.enemyMoveset([Moves.SPLASH]); + game.override.enemyMoveset(SPLASH_ONLY); }); //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/fillet_away_(move) @@ -46,8 +44,7 @@ describe("Moves - FILLET AWAY", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); @@ -64,8 +61,7 @@ describe("Moves - FILLET AWAY", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); //Here - BattleStat.SPD -> 0 and BattleStat.SPATK -> 3 @@ -86,8 +82,7 @@ describe("Moves - FILLET AWAY", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; @@ -107,8 +102,7 @@ describe("Moves - FILLET AWAY", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/src/test/moves/fissure.test.ts b/src/test/moves/fissure.test.ts index 940c32e975a..979bc40646c 100644 --- a/src/test/moves/fissure.test.ts +++ b/src/test/moves/fissure.test.ts @@ -2,13 +2,13 @@ import { BattleStat } from "#app/data/battle-stat"; import { Species } from "#app/enums/species.js"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import { DamagePhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Fissure", () => { let phaserGame: Phaser.Game; @@ -45,7 +45,7 @@ describe("Moves - Fissure", () => { await game.startBattle(); partyPokemon = game.scene.getParty()[0]; - enemyPokemon = game.scene.getEnemyPokemon(); + enemyPokemon = game.scene.getEnemyPokemon()!; // remove berries game.scene.removePartyMemberModifiers(0); diff --git a/src/test/moves/flame_burst.test.ts b/src/test/moves/flame_burst.test.ts index 69d11c79f93..0f9e311ca86 100644 --- a/src/test/moves/flame_burst.test.ts +++ b/src/test/moves/flame_burst.test.ts @@ -1,14 +1,10 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import Overrides from "#app/overrides"; +import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; -import { - SelectTargetPhase, - TurnEndPhase, -} from "#app/phases"; +import { SelectTargetPhase, TurnEndPhase } from "#app/phases"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#app/enums/abilities.js"; import { allAbilities } from "#app/data/ability.js"; import Pokemon from "#app/field/pokemon.js"; @@ -40,14 +36,14 @@ describe("Moves - Flame Burst", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("double"); - vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.FLAME_BURST, Moves.SPLASH]); - vi.spyOn(Overrides, "NEVER_CRIT_OVERRIDE", "get").mockReturnValue(true); - vi.spyOn(Overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.UNNERVE); - vi.spyOn(Overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(4); - vi.spyOn(Overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SHUCKLE); - vi.spyOn(Overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.BALL_FETCH); - vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue(new Array(4).fill(Moves.SPLASH)); + game.override.battleType("double"); + game.override.moveset([Moves.FLAME_BURST, Moves.SPLASH]); + game.override.disableCrits(); + game.override.ability(Abilities.UNNERVE); + game.override.startingWave(4); + game.override.enemySpecies(Species.SHUCKLE); + game.override.enemyAbility(Abilities.BALL_FETCH); + game.override.enemyMoveset(new Array(4).fill(Moves.SPLASH)); }); it("inflicts damage to the target's ally equal to 1/16 of its max HP", async () => { @@ -65,7 +61,7 @@ describe("Moves - Flame Burst", () => { }); it("does not inflict damage to the target's ally if the target was not affected by Flame Burst", async () => { - vi.spyOn(Overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.FLASH_FIRE); + game.override.enemyAbility(Abilities.FLASH_FIRE); await game.startBattle([Species.PIKACHU, Species.PIKACHU]); const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); diff --git a/src/test/moves/flower_shield.test.ts b/src/test/moves/flower_shield.test.ts index 82476ab5b90..7ca5fb8bc62 100644 --- a/src/test/moves/flower_shield.test.ts +++ b/src/test/moves/flower_shield.test.ts @@ -2,17 +2,15 @@ import { BattleStat } from "#app/data/battle-stat.js"; import { SemiInvulnerableTag } from "#app/data/battler-tags.js"; import { Type } from "#app/data/type.js"; import { Biome } from "#app/enums/biome.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Flower Shield", () => { let phaserGame: Phaser.Game; @@ -41,8 +39,8 @@ describe("Moves - Flower Shield", () => { game.override.enemySpecies(Species.CHERRIM); await game.startBattle([Species.MAGIKARP]); - const cherrim = game.scene.getEnemyPokemon(); - const magikarp = game.scene.getPlayerPokemon(); + const cherrim = game.scene.getEnemyPokemon()!; + const magikarp = game.scene.getPlayerPokemon()!; expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); @@ -83,8 +81,8 @@ describe("Moves - Flower Shield", () => { game.override.enemyLevel(50); await game.startBattle([Species.CHERRIM]); - const paras = game.scene.getEnemyPokemon(); - const cherrim = game.scene.getPlayerPokemon(); + const paras = game.scene.getEnemyPokemon()!; + const cherrim = game.scene.getPlayerPokemon()!; expect(paras.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); @@ -102,8 +100,8 @@ describe("Moves - Flower Shield", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon(); - const ally = game.scene.getPlayerPokemon(); + const enemy = game.scene.getEnemyPokemon()!; + const ally = game.scene.getPlayerPokemon()!; expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(ally.summonData.battleStats[BattleStat.DEF]).toBe(0); diff --git a/src/test/moves/focus_punch.test.ts b/src/test/moves/focus_punch.test.ts new file mode 100644 index 00000000000..f5cf85ffae0 --- /dev/null +++ b/src/test/moves/focus_punch.test.ts @@ -0,0 +1,132 @@ +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import GameManager from "#test/utils/gameManager"; +import { Species } from "#enums/species"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BerryPhase, MessagePhase, MoveHeaderPhase, SwitchSummonPhase, TurnStartPhase } from "#app/phases"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Focus Punch", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .ability(Abilities.UNNERVE) + .moveset([Moves.FOCUS_PUNCH]) + .enemySpecies(Species.GROUDON) + .enemyAbility(Abilities.INSOMNIA) + .enemyMoveset(SPLASH_ONLY) + .startingLevel(100) + .enemyLevel(100); + }); + + it( + "should deal damage at the end of turn if uninterrupted", + async () => { + await game.startBattle([Species.CHARIZARD]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + const enemyStartingHp = enemyPokemon.hp; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + + await game.phaseInterceptor.to(MessagePhase); + + expect(enemyPokemon.hp).toBe(enemyStartingHp); + expect(leadPokemon.getMoveHistory().length).toBe(0); + + await game.phaseInterceptor.to(BerryPhase, false); + + expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp); + expect(leadPokemon.getMoveHistory().length).toBe(1); + expect(leadPokemon.turnData.damageDealt).toBe(enemyStartingHp - enemyPokemon.hp); + }, TIMEOUT + ); + + it( + "should fail if the user is hit", + async () => { + game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + + await game.startBattle([Species.CHARIZARD]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + const enemyStartingHp = enemyPokemon.hp; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + + await game.phaseInterceptor.to(MessagePhase); + + expect(enemyPokemon.hp).toBe(enemyStartingHp); + expect(leadPokemon.getMoveHistory().length).toBe(0); + + await game.phaseInterceptor.to(BerryPhase, false); + + expect(enemyPokemon.hp).toBe(enemyStartingHp); + expect(leadPokemon.getMoveHistory().length).toBe(1); + expect(leadPokemon.turnData.damageDealt).toBe(0); + }, TIMEOUT + ); + + it( + "should be cancelled if the user falls asleep mid-turn", + async () => { + game.override.enemyMoveset(Array(4).fill(Moves.SPORE)); + + await game.startBattle([Species.CHARIZARD]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + + await game.phaseInterceptor.to(MessagePhase); // Header message + + expect(leadPokemon.getMoveHistory().length).toBe(0); + + await game.phaseInterceptor.to(BerryPhase, false); + + expect(leadPokemon.getMoveHistory().length).toBe(1); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); + }, TIMEOUT + ); + + it( + "should not queue its pre-move message before an enemy switches", + async () => { + /** Guarantee a Trainer battle with multiple enemy Pokemon */ + game.override.startingWave(25); + + await game.startBattle([Species.CHARIZARD]); + + game.forceOpponentToSwitch(); + game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + + await game.phaseInterceptor.to(TurnStartPhase); + + expect(game.scene.getCurrentPhase() instanceof SwitchSummonPhase).toBeTruthy(); + expect(game.scene.phaseQueue.find(phase => phase instanceof MoveHeaderPhase)).toBeDefined(); + }, TIMEOUT + ); +}); diff --git a/src/test/moves/follow_me.test.ts b/src/test/moves/follow_me.test.ts index eb41bf55a02..420dd7e0762 100644 --- a/src/test/moves/follow_me.test.ts +++ b/src/test/moves/follow_me.test.ts @@ -1,13 +1,9 @@ import { BattlerIndex } from "#app/battle.js"; import { Stat } from "#app/data/pokemon-stat"; import { Abilities } from "#app/enums/abilities.js"; -import { - CommandPhase, - SelectTargetPhase, - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, SelectTargetPhase, TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; diff --git a/src/test/moves/foresight.test.ts b/src/test/moves/foresight.test.ts new file mode 100644 index 00000000000..5d847ca1bc4 --- /dev/null +++ b/src/test/moves/foresight.test.ts @@ -0,0 +1,72 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#test/utils/gameManager"; +import { Species } from "#app/enums/species.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; +import { Moves } from "#app/enums/moves.js"; +import { getMovePosition } from "../utils/gameManagerUtils"; +import { MoveEffectPhase } from "#app/phases.js"; + +describe("Internals", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .disableCrits() + .enemySpecies(Species.GASTLY) + .enemyMoveset(SPLASH_ONLY) + .enemyLevel(5) + .starterSpecies(Species.MAGIKARP) + .moveset([Moves.FORESIGHT, Moves.QUICK_ATTACK, Moves.MACH_PUNCH]); + }); + + it("should allow Normal and Fighting moves to hit Ghost types", async () => { + await game.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + await game.toNextTurn(); + expect(enemy.hp).toBe(enemy.getMaxHp()); + + game.doAttack(getMovePosition(game.scene, 0, Moves.FORESIGHT)); + await game.toNextTurn(); + game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + await game.toNextTurn(); + + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + enemy.hp = enemy.getMaxHp(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.MACH_PUNCH)); + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + }); + + it("should ignore target's evasiveness boosts", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.MINIMIZE)); + await game.startBattle(); + + const pokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(pokemon, "getAccuracyMultiplier"); + + game.doAttack(getMovePosition(game.scene, 0, Moves.FORESIGHT)); + await game.toNextTurn(); + game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(pokemon.getAccuracyMultiplier).toHaveReturnedWith(1); + }); +}); diff --git a/src/test/moves/fusion_bolt.test.ts b/src/test/moves/fusion_bolt.test.ts index 368c75b0f54..c7a21e2c736 100644 --- a/src/test/moves/fusion_bolt.test.ts +++ b/src/test/moves/fusion_bolt.test.ts @@ -1,8 +1,7 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Species } from "#enums/species"; import { Moves } from "#enums/moves"; import { Abilities } from "#enums/abilities"; @@ -25,16 +24,16 @@ describe("Moves - Fusion Bolt", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([ fusionBolt ]); - vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(1); + game.override.moveset([ fusionBolt ]); + game.override.startingLevel(1); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RESHIRAM); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.ROUGH_SKIN); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemySpecies(Species.RESHIRAM); + game.override.enemyAbility(Abilities.ROUGH_SKIN); + game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); - vi.spyOn(overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("single"); - vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(97); - vi.spyOn(overrides, "NEVER_CRIT_OVERRIDE", "get").mockReturnValue(true); + game.override.battleType("single"); + game.override.startingWave(97); + game.override.disableCrits(); }); it("should not make contact", async() => { @@ -42,7 +41,7 @@ describe("Moves - Fusion Bolt", () => { Species.ZEKROM, ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; const initialHp = partyMember.hp; game.doAttack(getMovePosition(game.scene, 0, fusionBolt)); diff --git a/src/test/moves/fusion_flare.test.ts b/src/test/moves/fusion_flare.test.ts index 5ccbd82d25e..9ae42e7977f 100644 --- a/src/test/moves/fusion_flare.test.ts +++ b/src/test/moves/fusion_flare.test.ts @@ -1,9 +1,8 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; +import GameManager from "#test/utils/gameManager"; import { TurnStartPhase } from "#app/phases"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { StatusEffect } from "#app/data/status-effect"; import { Species } from "#enums/species"; import { Moves } from "#enums/moves"; @@ -26,15 +25,15 @@ describe("Moves - Fusion Flare", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([ fusionFlare ]); - vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(1); + game.override.moveset([ fusionFlare ]); + game.override.startingLevel(1); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RESHIRAM); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ Moves.REST, Moves.REST, Moves.REST, Moves.REST ]); + game.override.enemySpecies(Species.RESHIRAM); + game.override.enemyMoveset([ Moves.REST, Moves.REST, Moves.REST, Moves.REST ]); - vi.spyOn(overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("single"); - vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(97); - vi.spyOn(overrides, "NEVER_CRIT_OVERRIDE", "get").mockReturnValue(true); + game.override.battleType("single"); + game.override.startingWave(97); + game.override.disableCrits(); }); it("should thaw freeze status condition", async() => { @@ -42,7 +41,7 @@ describe("Moves - Fusion Flare", () => { Species.RESHIRAM, ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, fusionFlare)); @@ -50,7 +49,7 @@ describe("Moves - Fusion Flare", () => { // Inflict freeze quietly and check if it was properly inflicted partyMember.trySetStatus(StatusEffect.FREEZE, false); - expect(partyMember.status.effect).toBe(StatusEffect.FREEZE); + expect(partyMember.status!.effect).toBe(StatusEffect.FREEZE); await game.toNextTurn(); diff --git a/src/test/moves/fusion_flare_bolt.test.ts b/src/test/moves/fusion_flare_bolt.test.ts index 83c7c0e5993..95090214962 100644 --- a/src/test/moves/fusion_flare_bolt.test.ts +++ b/src/test/moves/fusion_flare_bolt.test.ts @@ -1,14 +1,14 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import overrides from "#app/overrides"; -import { MoveEffectPhase, MovePhase, MoveEndPhase, TurnStartPhase, DamagePhase } from "#app/phases"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { MoveEffectPhase, MovePhase, MoveEndPhase, DamagePhase } from "#app/phases"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Stat } from "#app/data/pokemon-stat"; import { allMoves } from "#app/data/move"; import { BattlerIndex } from "#app/battle"; import { Species } from "#enums/species"; import { Moves } from "#enums/moves"; +import { mockTurnOrder } from "#test/utils/testUtils"; describe("Moves - Fusion Flare and Fusion Bolt", () => { let phaserGame: Phaser.Game; @@ -29,15 +29,15 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([ fusionFlare.id, fusionBolt.id ]); - vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(1); + game.override.moveset([ fusionFlare.id, fusionBolt.id ]); + game.override.startingLevel(1); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RESHIRAM); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ Moves.REST, Moves.REST, Moves.REST, Moves.REST ]); + game.override.enemySpecies(Species.RESHIRAM); + game.override.enemyMoveset([ Moves.REST, Moves.REST, Moves.REST, Moves.REST ]); - vi.spyOn(overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("double"); - vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(97); - vi.spyOn(overrides, "NEVER_CRIT_OVERRIDE", "get").mockReturnValue(true); + game.override.battleType("double"); + game.override.startingWave(97); + game.override.disableCrits(); vi.spyOn(fusionFlare, "calculateBattlePower"); vi.spyOn(fusionBolt, "calculateBattlePower"); @@ -55,10 +55,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force user party to act before enemy party - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -83,10 +81,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force user party to act before enemy party - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -111,10 +107,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); game.doSelectTarget(0); - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force first enemy to act (and fail) in between party - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -133,7 +127,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { }, 20000); it("FUSION_FLARE should not double power of subsequent FUSION_BOLT if a move succeeded in between", async() => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM @@ -145,10 +139,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force first enemy to act in between party - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -177,10 +169,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); game.doSelectTarget(BattlerIndex.PLAYER); - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force user party to act before enemy party - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -194,7 +184,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { }, 20000); it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves", async() => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id ]); + game.override.enemyMoveset([ fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id ]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM @@ -231,10 +221,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force first enemy to act in between party - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -258,7 +246,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { }, 20000); it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves if moves are aimed at allies", async() => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id ]); + game.override.enemyMoveset([ fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id ]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM @@ -295,10 +283,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); game.doSelectTarget(BattlerIndex.PLAYER); - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force first enemy to act in between party - vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await mockTurnOrder(game, [ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); diff --git a/src/test/moves/gastro_acid.test.ts b/src/test/moves/gastro_acid.test.ts index 0ffa8d9180b..8c5f5f14eac 100644 --- a/src/test/moves/gastro_acid.test.ts +++ b/src/test/moves/gastro_acid.test.ts @@ -1,15 +1,12 @@ import { BattlerIndex } from "#app/battle.js"; -import { Stat } from "#app/data/pokemon-stat.js"; import { Abilities } from "#app/enums/abilities.js"; -import { - Moves -} from "#app/enums/moves.js"; +import { Moves } from "#app/enums/moves.js"; import { Species } from "#app/enums/species.js"; import { MoveResult } from "#app/field/pokemon.js"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { mockTurnOrder, SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -76,11 +73,9 @@ describe("Moves - Gastro Acid", () => { await game.startBattle(); - // Force player to be slower to enable Core Enforcer to proc its suppression effect - game.scene.getPlayerPokemon().stats[Stat.SPD] = 1; - game.scene.getEnemyPokemon().stats[Stat.SPD] = 2; - game.doAttack(getMovePosition(game.scene, 0, Moves.CORE_ENFORCER)); + // Force player to be slower to enable Core Enforcer to proc its suppression effect + await mockTurnOrder(game, [BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("TurnInitPhase"); @@ -88,6 +83,6 @@ describe("Moves - Gastro Acid", () => { await game.phaseInterceptor.to("TurnInitPhase"); - expect(game.scene.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); }, TIMEOUT); }); diff --git a/src/test/moves/glaive_rush.test.ts b/src/test/moves/glaive_rush.test.ts index 9548694183c..ce63da6b565 100644 --- a/src/test/moves/glaive_rush.test.ts +++ b/src/test/moves/glaive_rush.test.ts @@ -1,8 +1,8 @@ import { allMoves } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { DamagePhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -38,7 +38,7 @@ describe("Moves - Glaive Rush", () => { it("takes double damage from attacks", async() => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; vi.spyOn(game.scene, "randBattleSeedInt").mockReturnValue(0); @@ -50,11 +50,11 @@ describe("Moves - Glaive Rush", () => { await game.phaseInterceptor.to(DamagePhase); expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3)); - }, 20000); + }, 5000); // TODO: revert back to 20s it("always gets hit by attacks", async() => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; allMoves[Moves.AVALANCHE].accuracy = 0; @@ -68,8 +68,8 @@ describe("Moves - Glaive Rush", () => { game.override.startingHeldItems([{name: "MULTI_LENS", count: 2}]); game.override.enemyMoveset(Array(4).fill(Moves.AVALANCHE)); await game.startBattle(); - const player = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; player.hp = 1000; @@ -87,8 +87,8 @@ describe("Moves - Glaive Rush", () => { it("secondary effects only last until next move", async() => { game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); await game.startBattle(); - const player = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; player.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; @@ -112,8 +112,8 @@ describe("Moves - Glaive Rush", () => { game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); game.override.starterSpecies(0); await game.startBattle([Species.KLINK, Species.FEEBAS]); - const player = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; diff --git a/src/test/moves/growth.test.ts b/src/test/moves/growth.test.ts index 3d4a37fda73..bfa3cc54896 100644 --- a/src/test/moves/growth.test.ts +++ b/src/test/moves/growth.test.ts @@ -1,12 +1,8 @@ import { BattleStat } from "#app/data/battle-stat"; import { Stat } from "#app/data/pokemon-stat"; -import { - CommandPhase, - EnemyCommandPhase, - TurnInitPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, EnemyCommandPhase, TurnInitPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; diff --git a/src/test/moves/hard_press.test.ts b/src/test/moves/hard_press.test.ts index 89f8e14169f..baf63a1ad23 100644 --- a/src/test/moves/hard_press.test.ts +++ b/src/test/moves/hard_press.test.ts @@ -1,15 +1,13 @@ import { allMoves } from "#app/data/move.js"; -import { - MoveEffectPhase -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { MoveEffectPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Hard Press", () => { let phaserGame: Phaser.Game; @@ -50,7 +48,7 @@ describe("Moves - Hard Press", () => { it("should return 50 power if target HP ratio is at 50%", async () => { await game.startBattle([Species.PIKACHU]); const targetHpRatio = .5; - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); @@ -63,7 +61,7 @@ describe("Moves - Hard Press", () => { it("should return 1 power if target HP ratio is at 1%", async () => { await game.startBattle([Species.PIKACHU]); const targetHpRatio = .01; - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); @@ -76,7 +74,7 @@ describe("Moves - Hard Press", () => { it("should return 1 power if target HP ratio is less than 1%", async () => { await game.startBattle([Species.PIKACHU]); const targetHpRatio = .005; - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); diff --git a/src/test/moves/hyper_beam.test.ts b/src/test/moves/hyper_beam.test.ts index 623b24dbb3c..369d4cac853 100644 --- a/src/test/moves/hyper_beam.test.ts +++ b/src/test/moves/hyper_beam.test.ts @@ -4,10 +4,10 @@ import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { Moves } from "#app/enums/moves.js"; import { Species } from "#app/enums/species.js"; import { BerryPhase, TurnEndPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { getMovePosition } from "../utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; // 20 sec timeout for all tests @@ -43,8 +43,8 @@ describe("Moves - Hyper Beam", () => { async () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); diff --git a/src/test/moves/light_screen.test.ts b/src/test/moves/light_screen.test.ts index 52c1b85fa7a..9de1f8c492b 100644 --- a/src/test/moves/light_screen.test.ts +++ b/src/test/moves/light_screen.test.ts @@ -3,11 +3,9 @@ import Move, { allMoves } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { ArenaTagType } from "#app/enums/arena-tag-type.js"; import Pokemon from "#app/field/pokemon.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { NumberHolder } from "#app/utils.js"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -50,7 +48,7 @@ describe("Moves - Light Screen", () => { await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); @@ -65,7 +63,7 @@ describe("Moves - Light Screen", () => { game.doAttack(getMovePosition(game.scene, 1, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); @@ -77,7 +75,7 @@ describe("Moves - Light Screen", () => { game.doAttack(getMovePosition(game.scene, 0, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power); }); diff --git a/src/test/moves/lucky_chant.test.ts b/src/test/moves/lucky_chant.test.ts new file mode 100644 index 00000000000..1232ce9ffc3 --- /dev/null +++ b/src/test/moves/lucky_chant.test.ts @@ -0,0 +1,113 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import GameManager from "../utils/gameManager"; +import { getMovePosition } from "../utils/gameManagerUtils"; +import { Moves } from "#app/enums/moves.js"; +import { Species } from "#app/enums/species.js"; +import { Abilities } from "#app/enums/abilities.js"; +import { BerryPhase, TurnEndPhase } from "#app/phases.js"; +import { BattlerTagType } from "#app/enums/battler-tag-type.js"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Lucky Chant", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .battleType("single") + .moveset([Moves.LUCKY_CHANT, Moves.SPLASH, Moves.FOLLOW_ME]) + .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.INSOMNIA) + .enemyMoveset(Array(4).fill(Moves.FLOWER_TRICK)) + .startingLevel(100) + .enemyLevel(100); + }); + + it( + "should prevent critical hits from moves", + async () => { + await game.startBattle([Species.CHARIZARD]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + + await game.phaseInterceptor.to(TurnEndPhase); + + const firstTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp; + + game.doAttack(getMovePosition(game.scene, 0, Moves.LUCKY_CHANT)); + + await game.phaseInterceptor.to(BerryPhase, false); + + const secondTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp - firstTurnDamage; + expect(secondTurnDamage).toBeLessThan(firstTurnDamage); + }, TIMEOUT + ); + + it( + "should prevent critical hits against the user's ally", + async () => { + game.override.battleType("double"); + + await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); + + const playerPokemon = game.scene.getPlayerField(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); + game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + + await game.phaseInterceptor.to(TurnEndPhase); + + const firstTurnDamage = playerPokemon[0].getMaxHp() - playerPokemon[0].hp; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); + game.doAttack(getMovePosition(game.scene, 1, Moves.LUCKY_CHANT)); + + await game.phaseInterceptor.to(BerryPhase, false); + + const secondTurnDamage = playerPokemon[0].getMaxHp() - playerPokemon[0].hp - firstTurnDamage; + expect(secondTurnDamage).toBeLessThan(firstTurnDamage); + }, TIMEOUT + ); + + it( + "should prevent critical hits from field effects", + async () => { + game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); + + await game.startBattle([Species.CHARIZARD]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + enemyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 2, Moves.NONE, 0); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + + await game.phaseInterceptor.to(TurnEndPhase); + + const firstTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp; + + game.doAttack(getMovePosition(game.scene, 0, Moves.LUCKY_CHANT)); + + await game.phaseInterceptor.to(BerryPhase, false); + + const secondTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp - firstTurnDamage; + expect(secondTurnDamage).toBeLessThan(firstTurnDamage); + }, TIMEOUT + ); +}); diff --git a/src/test/moves/magnet_rise.test.ts b/src/test/moves/magnet_rise.test.ts index e5dfb94468e..9b3c6c457e2 100644 --- a/src/test/moves/magnet_rise.test.ts +++ b/src/test/moves/magnet_rise.test.ts @@ -1,5 +1,5 @@ import { CommandPhase, TurnEndPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; diff --git a/src/test/moves/make_it_rain.test.ts b/src/test/moves/make_it_rain.test.ts index 83543c4e530..72386930873 100644 --- a/src/test/moves/make_it_rain.test.ts +++ b/src/test/moves/make_it_rain.test.ts @@ -1,17 +1,13 @@ import { BattleStat } from "#app/data/battle-stat.js"; -import { - CommandPhase, - MoveEndPhase, - StatChangePhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { MoveEffectPhase, MoveEndPhase, StatChangePhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -44,16 +40,8 @@ describe("Moves - Make It Rain", () => { await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).toBeDefined()); - - const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).toBeDefined()); game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); - - await game.phaseInterceptor.to(CommandPhase); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); await game.phaseInterceptor.to(MoveEndPhase); @@ -67,11 +55,8 @@ describe("Moves - Make It Rain", () => { await game.startBattle([Species.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon(); - expect(playerPokemon).toBeDefined(); - - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); @@ -87,14 +72,9 @@ describe("Moves - Make It Rain", () => { await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); const playerPokemon = game.scene.getPlayerField(); - playerPokemon.forEach(p => expect(p).toBeDefined()); - const enemyPokemon = game.scene.getEnemyField(); - enemyPokemon.forEach(p => expect(p).toBeDefined()); game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); - - await game.phaseInterceptor.to(CommandPhase); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); await game.phaseInterceptor.to(StatChangePhase); @@ -102,4 +82,23 @@ describe("Moves - Make It Rain", () => { enemyPokemon.forEach(p => expect(p.isFainted()).toBe(true)); expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1); }, TIMEOUT); + + it("should reduce Sp. Atk if it only hits the second target", async () => { + await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); + + const playerPokemon = game.scene.getPlayerField(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); + game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + + await game.phaseInterceptor.to(MoveEffectPhase, false); + + // Make Make It Rain miss the first target + const moveEffectPhase = game.scene.getCurrentPhase() as MoveEffectPhase; + vi.spyOn(moveEffectPhase, "hitCheck").mockReturnValueOnce(false); + + await game.phaseInterceptor.to(MoveEndPhase); + + expect(playerPokemon[0].summonData.battleStats[BattleStat.SPATK]).toBe(-1); + }, TIMEOUT); }); diff --git a/src/test/moves/miracle_eye.test.ts b/src/test/moves/miracle_eye.test.ts new file mode 100644 index 00000000000..53d575ea96d --- /dev/null +++ b/src/test/moves/miracle_eye.test.ts @@ -0,0 +1,51 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#test/utils/gameManager"; +import { Species } from "#app/enums/species.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; +import { Moves } from "#app/enums/moves.js"; +import { getMovePosition } from "../utils/gameManagerUtils"; +import { MoveEffectPhase } from "#app/phases.js"; + +describe("Internals", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .disableCrits() + .enemySpecies(Species.UMBREON) + .enemyMoveset(SPLASH_ONLY) + .enemyLevel(5) + .starterSpecies(Species.MAGIKARP) + .moveset([Moves.MIRACLE_EYE, Moves.CONFUSION]); + }); + + it("should allow Psychic moves to hit Dark types", async () => { + await game.startBattle(); + + const enemy = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.CONFUSION)); + await game.toNextTurn(); + expect(enemy.hp).toBe(enemy.getMaxHp()); + + game.doAttack(getMovePosition(game.scene, 0, Moves.MIRACLE_EYE)); + await game.toNextTurn(); + game.doAttack(getMovePosition(game.scene, 0, Moves.CONFUSION)); + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + }); +}); diff --git a/src/test/moves/multi_target.test.ts b/src/test/moves/multi_target.test.ts index b7c4303afb3..a4ed936c5ee 100644 --- a/src/test/moves/multi_target.test.ts +++ b/src/test/moves/multi_target.test.ts @@ -2,12 +2,12 @@ import { getMoveTargets } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { Species } from "#app/enums/species.js"; import { TurnEndPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Moves } from "#enums/moves"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -73,7 +73,6 @@ async function checkTargetMultiplier(game: GameManager, attackMove: Moves, killA await game.startBattle(); const playerPokemonRepr = game.scene.getPlayerField(); - expect(playerPokemonRepr).not.toBeUndefined(); killAllyAndEnemy(game, killAlly, killSecondEnemy); diff --git a/src/test/moves/octolock.test.ts b/src/test/moves/octolock.test.ts index 8ef161d2131..8988109f431 100644 --- a/src/test/moves/octolock.test.ts +++ b/src/test/moves/octolock.test.ts @@ -1,14 +1,14 @@ import { BattleStat } from "#app/data/battle-stat"; import { TrappedTag } from "#app/data/battler-tags.js"; import { CommandPhase, MoveEndPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { removeEnemyHeldItems, SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Octolock", () => { describe("integration tests", () => { @@ -41,7 +41,6 @@ describe("Moves - Octolock", () => { it("Reduces DEf and SPDEF by 1 each turn", { timeout: 10000 }, async () => { await game.startBattle([Species.GRAPPLOCT]); - removeEnemyHeldItems(game.scene); const enemyPokemon = game.scene.getEnemyField(); @@ -63,7 +62,6 @@ describe("Moves - Octolock", () => { it("Traps the target pokemon", { timeout: 10000 }, async () => { await game.startBattle([Species.GRAPPLOCT]); - removeEnemyHeldItems(game.scene); const enemyPokemon = game.scene.getEnemyField(); diff --git a/src/test/moves/purify.test.ts b/src/test/moves/purify.test.ts index cbc107a3def..0367cc5a9b2 100644 --- a/src/test/moves/purify.test.ts +++ b/src/test/moves/purify.test.ts @@ -1,10 +1,8 @@ import { Status, StatusEffect } from "#app/data/status-effect.js"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon.js"; -import { - MoveEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { MoveEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -44,8 +42,8 @@ describe("Moves - Purify", () => { async () => { await game.startBattle(); - const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.hp = playerPokemon.getMaxHp() - 1; enemyPokemon.status = new Status(StatusEffect.BURN); @@ -53,7 +51,7 @@ describe("Moves - Purify", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.PURIFY)); await game.phaseInterceptor.to(MoveEndPhase); - expect(enemyPokemon.status).toBe(undefined); + expect(enemyPokemon.status).toBeNull(); expect(playerPokemon.isFullHp()).toBe(true); }, TIMEOUT @@ -64,7 +62,7 @@ describe("Moves - Purify", () => { async () => { await game.startBattle(); - const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon(); + const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.hp = playerPokemon.getMaxHp() - 1; const playerInitialHp = playerPokemon.hp; diff --git a/src/test/moves/rage_powder.test.ts b/src/test/moves/rage_powder.test.ts index bc533e1313e..92cdcc9b4f7 100644 --- a/src/test/moves/rage_powder.test.ts +++ b/src/test/moves/rage_powder.test.ts @@ -1,11 +1,7 @@ import { BattlerIndex } from "#app/battle.js"; -import { - CommandPhase, - SelectTargetPhase, - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, SelectTargetPhase, TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/test/moves/reflect.test.ts b/src/test/moves/reflect.test.ts index 6fcd2268d12..f5ea489a75e 100644 --- a/src/test/moves/reflect.test.ts +++ b/src/test/moves/reflect.test.ts @@ -3,11 +3,9 @@ import Move, { allMoves } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { ArenaTagType } from "#app/enums/arena-tag-type.js"; import Pokemon from "#app/field/pokemon.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { NumberHolder } from "#app/utils.js"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -49,7 +47,7 @@ describe("Moves - Reflect", () => { game.doAttack(getMovePosition(game.scene, 0, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); @@ -64,7 +62,7 @@ describe("Moves - Reflect", () => { game.doAttack(getMovePosition(game.scene, 1, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); @@ -77,7 +75,7 @@ describe("Moves - Reflect", () => { await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power); }); diff --git a/src/test/moves/rollout.test.ts b/src/test/moves/rollout.test.ts index 73457c9dcce..ad323c447f5 100644 --- a/src/test/moves/rollout.test.ts +++ b/src/test/moves/rollout.test.ts @@ -1,13 +1,13 @@ import { allMoves } from "#app/data/move.js"; import { CommandPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Rollout", () => { let phaserGame: Phaser.Game; diff --git a/src/test/moves/roost.test.ts b/src/test/moves/roost.test.ts index fe89a0a0a10..a9036dcb478 100644 --- a/src/test/moves/roost.test.ts +++ b/src/test/moves/roost.test.ts @@ -3,10 +3,10 @@ import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { Moves } from "#app/enums/moves.js"; import { Species } from "#app/enums/species.js"; import { MoveEffectPhase, TurnEndPhase } from "#app/phases.js"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { getMovePosition } from "../utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; const TIMEOUT = 20 * 1000; @@ -40,11 +40,7 @@ describe("Moves - Roost", () => { async () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - expect(leadPokemon).toBeDefined(); - - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon).toBeDefined(); + const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; diff --git a/src/test/moves/spikes.test.ts b/src/test/moves/spikes.test.ts index a19c28b2c1e..1f5bb757ee4 100644 --- a/src/test/moves/spikes.test.ts +++ b/src/test/moves/spikes.test.ts @@ -1,7 +1,5 @@ -import { - CommandPhase -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; +import { CommandPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/test/moves/spit_up.test.ts b/src/test/moves/spit_up.test.ts index 0fc01821838..ec0a53028ff 100644 --- a/src/test/moves/spit_up.test.ts +++ b/src/test/moves/spit_up.test.ts @@ -4,13 +4,13 @@ import { allMoves } from "#app/data/move.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { MoveResult, TurnMove } from "#app/field/pokemon.js"; import { MovePhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Spit Up", () => { let phaserGame: Phaser.Game; @@ -45,10 +45,10 @@ describe("Moves - Spit Up", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -69,11 +69,11 @@ describe("Moves - Spit Up", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -94,12 +94,12 @@ describe("Moves - Spit Up", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -118,9 +118,9 @@ describe("Moves - Spit Up", () => { it("fails without stacks", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); @@ -137,10 +137,10 @@ describe("Moves - Spit Up", () => { it("decreases stats based on stored values (both boosts equal)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); @@ -166,10 +166,10 @@ describe("Moves - Spit Up", () => { it("decreases stats based on stored values (different boosts)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); // for the sake of simplicity (and because other tests cover the setup), set boost amounts directly diff --git a/src/test/moves/spotlight.test.ts b/src/test/moves/spotlight.test.ts index 8170c42dfec..0893ba975d7 100644 --- a/src/test/moves/spotlight.test.ts +++ b/src/test/moves/spotlight.test.ts @@ -1,12 +1,8 @@ import { BattlerIndex } from "#app/battle.js"; import { Stat } from "#app/data/pokemon-stat"; -import { - CommandPhase, - SelectTargetPhase, - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, SelectTargetPhase, TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; diff --git a/src/test/moves/stockpile.test.ts b/src/test/moves/stockpile.test.ts index 81bd65ce582..375eeab3c95 100644 --- a/src/test/moves/stockpile.test.ts +++ b/src/test/moves/stockpile.test.ts @@ -2,14 +2,14 @@ import { BattleStat } from "#app/data/battle-stat"; import { StockpilingTag } from "#app/data/battler-tags.js"; import { MoveResult, TurnMove } from "#app/field/pokemon.js"; import { CommandPhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Stockpile", () => { describe("integration tests", () => { @@ -41,7 +41,7 @@ describe("Moves - Stockpile", () => { it("Gains a stockpile stack and increases DEF and SPDEF by 1 on each use, fails at max stacks (3)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const user = game.scene.getPlayerPokemon(); + const user = game.scene.getPlayerPokemon()!; // Unfortunately, Stockpile stacks are not directly queryable (i.e. there is no pokemon.getStockpileStacks()), // we just have to know that they're implemented as a BattlerTag. @@ -59,7 +59,7 @@ describe("Moves - Stockpile", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); await game.phaseInterceptor.to(TurnInitPhase); - const stockpilingTag = user.getTag(StockpilingTag); + const stockpilingTag = user.getTag(StockpilingTag)!; const def = user.summonData.battleStats[BattleStat.DEF]; const spdef = user.summonData.battleStats[BattleStat.SPDEF]; @@ -82,7 +82,7 @@ describe("Moves - Stockpile", () => { it("Gains a stockpile stack even if DEF and SPDEF are at +6", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const user = game.scene.getPlayerPokemon(); + const user = game.scene.getPlayerPokemon()!; user.summonData.battleStats[BattleStat.DEF] = 6; user.summonData.battleStats[BattleStat.SPDEF] = 6; @@ -94,7 +94,7 @@ describe("Moves - Stockpile", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); await game.phaseInterceptor.to(TurnInitPhase); - const stockpilingTag = user.getTag(StockpilingTag); + const stockpilingTag = user.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(1); expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); @@ -106,7 +106,7 @@ describe("Moves - Stockpile", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); await game.phaseInterceptor.to(TurnInitPhase); - const stockpilingTagAgain = user.getTag(StockpilingTag); + const stockpilingTagAgain = user.getTag(StockpilingTag)!; expect(stockpilingTagAgain).toBeDefined(); expect(stockpilingTagAgain.stockpiledCount).toBe(2); expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); diff --git a/src/test/moves/swallow.test.ts b/src/test/moves/swallow.test.ts index 7c3d3ae1613..aed30445fd2 100644 --- a/src/test/moves/swallow.test.ts +++ b/src/test/moves/swallow.test.ts @@ -3,13 +3,13 @@ import { StockpilingTag } from "#app/data/battler-tags.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { MoveResult, TurnMove } from "#app/field/pokemon.js"; import { MovePhase, TurnInitPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Swallow", () => { let phaserGame: Phaser.Game; @@ -44,13 +44,13 @@ describe("Moves - Swallow", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 1; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -71,14 +71,14 @@ describe("Moves - Swallow", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 1; pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -99,7 +99,7 @@ describe("Moves - Swallow", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 0.0001; @@ -107,7 +107,7 @@ describe("Moves - Swallow", () => { pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -126,9 +126,9 @@ describe("Moves - Swallow", () => { it("fails without stacks", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); game.doAttack(0); @@ -141,10 +141,10 @@ describe("Moves - Swallow", () => { it("decreases stats based on stored values (both boosts equal)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); game.doAttack(0); @@ -166,10 +166,10 @@ describe("Moves - Swallow", () => { it("decreases stats based on stored values (different boosts)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); // for the sake of simplicity (and because other tests cover the setup), set boost amounts directly diff --git a/src/test/moves/tackle.test.ts b/src/test/moves/tackle.test.ts index 94eee4f20fa..512b23ae363 100644 --- a/src/test/moves/tackle.test.ts +++ b/src/test/moves/tackle.test.ts @@ -1,10 +1,7 @@ import { Stat } from "#app/data/pokemon-stat"; -import { - CommandPhase, - EnemyCommandPhase, TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, EnemyCommandPhase, TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import { Moves } from "#enums/moves"; diff --git a/src/test/moves/tail_whip.test.ts b/src/test/moves/tail_whip.test.ts index 539829f4003..7630b31f7de 100644 --- a/src/test/moves/tail_whip.test.ts +++ b/src/test/moves/tail_whip.test.ts @@ -1,11 +1,7 @@ import { BattleStat } from "#app/data/battle-stat"; -import { - CommandPhase, - EnemyCommandPhase, - TurnInitPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { CommandPhase, EnemyCommandPhase, TurnInitPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; diff --git a/src/test/moves/tailwind.test.ts b/src/test/moves/tailwind.test.ts index d2a90dd2ed5..b2643dc68f3 100644 --- a/src/test/moves/tailwind.test.ts +++ b/src/test/moves/tailwind.test.ts @@ -1,16 +1,14 @@ import { ArenaTagSide } from "#app/data/arena-tag.js"; import { Stat } from "#app/data/pokemon-stat.js"; import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import { - TurnEndPhase, -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wind Rider", () => { let phaserGame: Phaser.Game; @@ -82,8 +80,8 @@ describe("Abilities - Wind Rider", () => { await game.startBattle([Species.MAGIKARP]); - const ally = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const ally = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; const allySpd = ally.getStat(Stat.SPD); const enemySpd = enemy.getStat(Stat.SPD); diff --git a/src/test/moves/thousand_arrows.test.ts b/src/test/moves/thousand_arrows.test.ts index a7fbee3bebd..84a71ee5256 100644 --- a/src/test/moves/thousand_arrows.test.ts +++ b/src/test/moves/thousand_arrows.test.ts @@ -1,11 +1,8 @@ import { Abilities } from "#app/enums/abilities.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { - BerryPhase, - MoveEffectPhase -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { BerryPhase, MoveEffectPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; @@ -42,7 +39,7 @@ describe("Moves - Thousand Arrows", () => { async () => { await game.startBattle([ Species.ILLUMISE ]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); @@ -65,7 +62,7 @@ describe("Moves - Thousand Arrows", () => { await game.startBattle([ Species.ILLUMISE ]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); @@ -87,9 +84,9 @@ describe("Moves - Thousand Arrows", () => { await game.startBattle([ Species.ILLUMISE ]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; - enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, null, Moves.MAGNET_RISE); + enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, undefined, Moves.MAGNET_RISE); game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); diff --git a/src/test/moves/tidy_up.test.ts b/src/test/moves/tidy_up.test.ts index b1292de0d27..e35a438c562 100644 --- a/src/test/moves/tidy_up.test.ts +++ b/src/test/moves/tidy_up.test.ts @@ -1,14 +1,14 @@ import { BattleStat } from "#app/data/battle-stat.js"; import { ArenaTagType } from "#app/enums/arena-tag-type.js"; import { MoveEndPhase, TurnEndPhase } from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Tidy Up", () => { @@ -106,7 +106,7 @@ describe("Moves - Tidy Up", () => { it("user's stats are raised with no traps set", async() => { await game.startBattle(); - const player = game.scene.getPlayerPokemon().summonData.battleStats; + const player = game.scene.getPlayerPokemon()!.summonData.battleStats; expect(player[BattleStat.ATK]).toBe(0); expect(player[BattleStat.SPD]).toBe(0); diff --git a/src/test/moves/u_turn.test.ts b/src/test/moves/u_turn.test.ts new file mode 100644 index 00000000000..2c12a4da43b --- /dev/null +++ b/src/test/moves/u_turn.test.ts @@ -0,0 +1,100 @@ +import { Abilities } from "#app/enums/abilities.js"; +import { SwitchPhase, TurnEndPhase } from "#app/phases"; +import GameManager from "#app/test/utils/gameManager"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { StatusEffect } from "#app/enums/status-effect.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; + +describe("Moves - U-turn", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemySpecies(Species.GENGAR) + .startingLevel(90) + .startingWave(97) + .moveset([Moves.U_TURN]) + .enemyMoveset(SPLASH_ONLY) + .disableCrits(); + }); + + it("triggers regenerator a single time when a regenerator user switches out with u-turn", async() => { + // arrange + const playerHp = 1; + game.override.ability(Abilities.REGENERATOR); + await game.startBattle([ + Species.RAICHU, + Species.SHUCKLE + ]); + game.scene.getPlayerPokemon()!.hp = playerHp; + + // act + game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.doSelectPartyPokemon(1); + await game.phaseInterceptor.to(TurnEndPhase); + + // assert + expect(game.scene.getParty()[1].hp).toEqual(Math.floor(game.scene.getParty()[1].getMaxHp() * 0.33 + playerHp)); + expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); + expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE); + }, 20000); + + it("triggers rough skin on the u-turn user before a new pokemon is switched in", async() => { + // arrange + game.override.enemyAbility(Abilities.ROUGH_SKIN); + await game.startBattle([ + Species.RAICHU, + Species.SHUCKLE + ]); + + // act + game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.doSelectPartyPokemon(1); + await game.phaseInterceptor.to(SwitchPhase, false); + + // assert + const playerPkm = game.scene.getPlayerPokemon()!; + expect(playerPkm.hp).not.toEqual(playerPkm.getMaxHp()); + expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); // proxy for asserting ability activated + expect(playerPkm.species.speciesId).toEqual(Species.RAICHU); + expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); + }, 20000); + + it("triggers contact abilities on the u-turn user (eg poison point) before a new pokemon is switched in", async() => { + // arrange + game.override.enemyAbility(Abilities.POISON_POINT); + await game.startBattle([ + Species.RAICHU, + Species.SHUCKLE + ]); + vi.spyOn(game.scene.getEnemyPokemon()!, "randSeedInt").mockReturnValue(0); + + // act + game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + await game.phaseInterceptor.to(SwitchPhase, false); + + // assert + const playerPkm = game.scene.getPlayerPokemon()!; + expect(playerPkm.status?.effect).toEqual(StatusEffect.POISON); + expect(playerPkm.species.speciesId).toEqual(Species.RAICHU); + expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); // proxy for asserting ability activated + expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); + }, 20000); +}); diff --git a/src/test/phases/phases.test.ts b/src/test/phases/phases.test.ts index 3d6da362004..c61eb1d41b8 100644 --- a/src/test/phases/phases.test.ts +++ b/src/test/phases/phases.test.ts @@ -1,9 +1,9 @@ import BattleScene from "#app/battle-scene.js"; import { LoginPhase, TitlePhase, UnavailablePhase } from "#app/phases.js"; import { Mode } from "#app/ui/ui.js"; -import {afterEach, beforeAll, beforeEach, describe, expect, it} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; +import GameManager from "#test/utils/gameManager"; describe("Phases", () => { let phaserGame: Phaser.Game; diff --git a/src/test/settingMenu/helpers/inGameManip.ts b/src/test/settingMenu/helpers/inGameManip.ts index 1edab75fd27..e18a82ca571 100644 --- a/src/test/settingMenu/helpers/inGameManip.ts +++ b/src/test/settingMenu/helpers/inGameManip.ts @@ -1,9 +1,6 @@ -import { - getIconForLatestInput, - getSettingNameWithKeycode -} from "#app/configs/inputs/configHandler"; -import {expect} from "vitest"; -import {SettingKeyboard} from "#app/system/settings/settings-keyboard"; +import { getIconForLatestInput, getSettingNameWithKeycode } from "#app/configs/inputs/configHandler"; +import { expect } from "vitest"; +import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; export class InGameManip { private config; diff --git a/src/test/settingMenu/helpers/menuManip.ts b/src/test/settingMenu/helpers/menuManip.ts index 7b4080c3d75..4fd5f526897 100644 --- a/src/test/settingMenu/helpers/menuManip.ts +++ b/src/test/settingMenu/helpers/menuManip.ts @@ -1,14 +1,6 @@ -import {expect} from "vitest"; -import { - deleteBind, - getIconWithKeycode, - getIconWithSettingName, - getKeyWithKeycode, - getKeyWithSettingName, - assign, - getSettingNameWithKeycode, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting -} from "#app/configs/inputs/configHandler"; -import {SettingKeyboard} from "#app/system/settings/settings-keyboard"; +import { expect } from "vitest"; +import { deleteBind, getIconWithKeycode, getIconWithSettingName, getKeyWithKeycode, getKeyWithSettingName, assign, getSettingNameWithKeycode, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting } from "#app/configs/inputs/configHandler"; +import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; export class MenuManip { private config; @@ -78,7 +70,7 @@ export class MenuManip { weWantThisBindInstead(keycode) { this.keycode = Phaser.Input.Keyboard.KeyCodes[keycode]; const icon = getIconWithKeycode(this.config, this.keycode); - const key = getKeyWithKeycode(this.config, this.keycode); + const key = getKeyWithKeycode(this.config, this.keycode)!; // TODO: is this bang correct? const _keys = key.toLowerCase().split("_"); const iconIdentifier = _keys[_keys.length-1]; expect(icon.toLowerCase().includes(iconIdentifier)).toEqual(true); @@ -86,7 +78,7 @@ export class MenuManip { } whenWeDelete(settingName?: string) { - this.settingName = SettingKeyboard[settingName] || this.settingName; + this.settingName = settingName ? SettingKeyboard[settingName] : this.settingName; // const key = getKeyWithSettingName(this.config, this.settingName); deleteBind(this.config, this.settingName); // expect(this.config.custom[key]).toEqual(-1); @@ -94,7 +86,7 @@ export class MenuManip { } whenWeTryToDelete(settingName?: string) { - this.settingName = SettingKeyboard[settingName] || this.settingName; + this.settingName = settingName ? SettingKeyboard[settingName] : this.settingName; deleteBind(this.config, this.settingName); return this; } diff --git a/src/test/settingMenu/rebinding_setting.test.ts b/src/test/settingMenu/rebinding_setting.test.ts index 010ad427d6e..eead23972c2 100644 --- a/src/test/settingMenu/rebinding_setting.test.ts +++ b/src/test/settingMenu/rebinding_setting.test.ts @@ -1,14 +1,11 @@ -import {beforeEach, describe, expect, it} from "vitest"; -import {deepCopy} from "#app/utils"; -import { - getKeyWithKeycode, - getKeyWithSettingName, -} from "#app/configs/inputs/configHandler"; -import {MenuManip} from "#app/test/settingMenu/helpers/menuManip"; -import {InGameManip} from "#app/test/settingMenu/helpers/inGameManip"; -import {InterfaceConfig} from "#app/inputs-controller"; +import { beforeEach, describe, expect, it } from "vitest"; +import { deepCopy } from "#app/utils"; +import { getKeyWithKeycode, getKeyWithSettingName } from "#app/configs/inputs/configHandler"; +import { MenuManip } from "#test/settingMenu/helpers/menuManip"; +import { InGameManip } from "#test/settingMenu/helpers/inGameManip"; +import { InterfaceConfig } from "#app/inputs-controller"; import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; -import {SettingKeyboard} from "#app/system/settings/settings-keyboard"; +import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { Device } from "#enums/devices"; import { Button } from "#enums/buttons"; @@ -40,13 +37,13 @@ describe("Test Rebinding", () => { expect(button).toEqual(Button.LEFT); }); it("Check key for Keyboard KeyCode", () => { - const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.LEFT); + const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.LEFT) ?? ""; const settingName = config.custom[key]; const button = config.settings[settingName]; expect(button).toEqual(Button.LEFT); }); it("Check key for currenly Assigned to action not alt", () => { - const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.A); + const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.A) ?? ""; const settingName = config.custom[key]; const button = config.settings[settingName]; expect(button).toEqual(Button.LEFT); @@ -69,25 +66,25 @@ describe("Test Rebinding", () => { }); it("Check icon for currenly Assigned to key code", () => { const keycode = Phaser.Input.Keyboard.KeyCodes.LEFT; - const key = getKeyWithKeycode(config, keycode); + const key = getKeyWithKeycode(config, keycode) ?? ""; const icon = config.icons[key]; expect(icon).toEqual("KEY_ARROW_LEFT.png"); }); it("Check icon for currenly Assigned to key code", () => { const keycode = Phaser.Input.Keyboard.KeyCodes.A; - const key = getKeyWithKeycode(config, keycode); + const key = getKeyWithKeycode(config, keycode) ?? ""; const icon = config.icons[key]; expect(icon).toEqual("A.png"); }); it("Check icon for currenly Assigned to setting name", () => { const settingName = SettingKeyboard.Button_Left; - const key = getKeyWithSettingName(config, settingName); + const key = getKeyWithSettingName(config, settingName) ?? ""; const icon = config.icons[key]; expect(icon).toEqual("KEY_ARROW_LEFT.png"); }); it("Check icon for currenly Assigned to setting name alt", () => { const settingName = SettingKeyboard.Alt_Button_Left; - const key = getKeyWithSettingName(config, settingName); + const key = getKeyWithSettingName(config, settingName) ?? ""; const icon = config.icons[key]; expect(icon).toEqual("A.png"); }); @@ -372,7 +369,7 @@ describe("Test Rebinding", () => { it("test keyboard listener", () => { const keyDown = Phaser.Input.Keyboard.KeyCodes.S; - const key = getKeyWithKeycode(config, keyDown); + const key = getKeyWithKeycode(config, keyDown) ?? ""; const settingName = config.custom[key]; const buttonDown = config.settings[settingName]; expect(buttonDown).toEqual(Button.DOWN); diff --git a/src/test/sprites/pokemonSprite.test.ts b/src/test/sprites/pokemonSprite.test.ts index 0fd725b2f58..deb5844d677 100644 --- a/src/test/sprites/pokemonSprite.test.ts +++ b/src/test/sprites/pokemonSprite.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from "vitest"; import _masterlist from "../../../public/images/pokemon/variant/_masterlist.json"; import fs from "fs"; import path from "path"; -import { getAppRootDir } from "#app/test/sprites/spritesUtils"; +import { getAppRootDir } from "#test/sprites/spritesUtils"; type PokemonVariantMasterlist = typeof _masterlist; @@ -23,16 +23,19 @@ describe("check if every variant's sprite are correctly set", () => { expVariant = masterlist.exp; femaleVariant = masterlist.female; backVariant = masterlist.back; - delete masterlist.exp; - delete masterlist.female; - delete masterlist.back; + //@ts-ignore + delete masterlist.exp; //TODO: resolve ts-ignore + //@ts-ignore + delete masterlist.female; //TODO: resolve ts-ignore + //@ts-ignore + delete masterlist.back; //TODO: resolve ts-ignore }); it("data should not be undefined", () => { - expect(masterlist).not.toBeUndefined(); - expect(expVariant).not.toBeUndefined(); - expect(femaleVariant).not.toBeUndefined(); - expect(backVariant).not.toBeUndefined(); + expect(masterlist).toBeDefined(); + expect(expVariant).toBeDefined(); + expect(femaleVariant).toBeDefined(); + expect(backVariant).toBeDefined(); }); function getMissingMasterlist(mlist: any, dirpath: string, excludes: string[] = []): string[] { @@ -103,7 +106,7 @@ describe("check if every variant's sprite are correctly set", () => { } function getMissingFiles(keys: Record, dirPath: string): string[] { - const errors = []; + const errors: string[] = []; for (const key of Object.keys(keys)) { const row = keys[key]; for (const [index, elm] of row.entries()) { diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index 95a4605fd68..b61338bfcad 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -1,20 +1,16 @@ -import {afterEach, beforeAll, beforeEach, describe, expect, it} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import { - EncounterPhase, - SelectStarterPhase, - TitlePhase, -} from "#app/phases"; -import {Mode} from "#app/ui/ui"; -import {GameModes} from "#app/game-mode"; +import GameManager from "#test/utils/gameManager"; +import { EncounterPhase, SelectStarterPhase, TitlePhase } from "#app/phases"; +import { Mode } from "#app/ui/ui"; +import { GameModes } from "#app/game-mode"; import StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; -import {OptionSelectItem} from "#app/ui/abstact-option-select-ui-handler"; -import {Gender} from "#app/data/gender"; -import {allSpecies} from "#app/data/pokemon-species"; -import {Nature} from "#app/data/nature"; +import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; +import { Gender } from "#app/data/gender"; +import { allSpecies } from "#app/data/pokemon-species"; +import { Nature} from "#app/data/nature"; import { Button } from "#enums/buttons"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; @@ -59,8 +55,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -73,7 +69,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -120,8 +116,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -134,7 +130,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -184,8 +180,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -198,7 +194,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -246,8 +242,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -260,7 +256,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -307,8 +303,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -321,7 +317,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -368,8 +364,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -382,7 +378,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -430,8 +426,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -444,7 +440,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -490,8 +486,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -504,7 +500,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); let starterSelectUiHandler: StarterSelectUiHandler; await new Promise((resolve) => { @@ -555,8 +551,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -569,9 +565,9 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); - let starterSelectUiHandler: StarterSelectUiHandler; + let starterSelectUiHandler: StarterSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { starterSelectUiHandler = game.scene.ui.getHandler() as StarterSelectUiHandler; @@ -580,11 +576,11 @@ describe("UI - Starter select", () => { }); }); - expect(starterSelectUiHandler.starterSpecies.length).toBe(1); - expect(starterSelectUiHandler.starterSpecies[0].generation).toBe(1); - expect(starterSelectUiHandler.starterSpecies[0].speciesId).toBe(32); - expect(starterSelectUiHandler.cursorObj.x).toBe(53); - expect(starterSelectUiHandler.cursorObj.y).toBe(31); + expect(starterSelectUiHandler?.starterSpecies.length).toBe(1); + expect(starterSelectUiHandler?.starterSpecies[0].generation).toBe(1); + expect(starterSelectUiHandler?.starterSpecies[0].speciesId).toBe(32); + expect(starterSelectUiHandler?.cursorObj.x).toBe(53); + expect(starterSelectUiHandler?.cursorObj.y).toBe(31); game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; diff --git a/src/test/ui/transfer-item.test.ts b/src/test/ui/transfer-item.test.ts index c00e40b8ebd..bbb9a823ad9 100644 --- a/src/test/ui/transfer-item.test.ts +++ b/src/test/ui/transfer-item.test.ts @@ -2,18 +2,15 @@ import { BerryType } from "#app/enums/berry-type"; import { Button } from "#app/enums/buttons"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import { - BattleEndPhase, - SelectModifierPhase -} from "#app/phases"; -import GameManager from "#app/test/utils/gameManager"; +import { BattleEndPhase, SelectModifierPhase } from "#app/phases"; +import GameManager from "#test/utils/gameManager"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; import { Mode } from "#app/ui/ui"; import Phaser from "phaser"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { getMovePosition } from "../utils/gameManagerUtils"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; describe("UI - Transfer Items", () => { diff --git a/src/test/utils/TextInterceptor.ts b/src/test/utils/TextInterceptor.ts index a49f41f6be0..507161eb6d0 100644 --- a/src/test/utils/TextInterceptor.ts +++ b/src/test/utils/TextInterceptor.ts @@ -1,6 +1,6 @@ export default class TextInterceptor { private scene; - public logs = []; + public logs: string[] = []; constructor(scene) { this.scene = scene; scene.messageWrapper = this; @@ -17,6 +17,6 @@ export default class TextInterceptor { } getLatestMessage(): string { - return this.logs.pop(); + return this.logs.pop() ?? ""; } } diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 1938d57dfd1..771ab1e7081 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -1,31 +1,20 @@ -import GameWrapper from "#app/test/utils/gameWrapper"; -import {Mode} from "#app/ui/ui"; -import {generateStarter, waitUntil} from "#app/test/utils/gameManagerUtils"; -import { - CommandPhase, - EncounterPhase, - FaintPhase, - LoginPhase, - NewBattlePhase, - SelectStarterPhase, - SelectTargetPhase, - TitlePhase, TurnEndPhase, TurnInitPhase, - TurnStartPhase, -} from "#app/phases"; +import GameWrapper from "#test/utils/gameWrapper"; +import { Mode } from "#app/ui/ui"; +import { generateStarter, waitUntil } from "#test/utils/gameManagerUtils"; +import { CommandPhase, EncounterPhase, FaintPhase, LoginPhase, MovePhase, NewBattlePhase, SelectStarterPhase, SelectTargetPhase, TitlePhase, TurnEndPhase, TurnInitPhase, TurnStartPhase } from "#app/phases"; import BattleScene from "#app/battle-scene.js"; -import PhaseInterceptor from "#app/test/utils/phaseInterceptor"; -import TextInterceptor from "#app/test/utils/TextInterceptor"; -import {GameModes, getGameMode} from "#app/game-mode"; +import PhaseInterceptor from "#test/utils/phaseInterceptor"; +import TextInterceptor from "#test/utils/TextInterceptor"; +import { GameModes, getGameMode } from "#app/game-mode"; import fs from "fs"; -import {AES, enc} from "crypto-js"; -import {updateUserInfo} from "#app/account"; +import { AES, enc } from "crypto-js"; +import { updateUserInfo } from "#app/account"; import InputsHandler from "#app/test/utils/inputsHandler"; import ErrorInterceptor from "#app/test/utils/errorInterceptor"; -import {EnemyPokemon, PlayerPokemon} from "#app/field/pokemon"; -import {MockClock} from "#app/test/utils/mocks/mockClock"; -import {Command} from "#app/ui/command-ui-handler"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import PartyUiHandler, {PartyUiMode} from "#app/ui/party-ui-handler"; +import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import { MockClock } from "#app/test/utils/mocks/mockClock"; +import PartyUiHandler from "#app/ui/party-ui-handler"; +import CommandUiHandler, { Command } from "#app/ui/command-ui-handler"; import Trainer from "#app/field/trainer"; import { ExpNotification } from "#enums/exp-notification"; import { GameDataType } from "#enums/game-data-type"; @@ -36,6 +25,9 @@ import { BattlerIndex } from "#app/battle.js"; import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js"; import { OverridesHelper } from "./overridesHelper"; import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type.js"; +import overrides from "#app/overrides.js"; +import { removeEnemyHeldItems } from "./testUtils"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler.js"; /** * Class to manage the game state and transitions between phases. @@ -89,7 +81,7 @@ export default class GameManager { * Ends the current phase. */ endPhase() { - this.scene.getCurrentPhase().end(); + this.scene.getCurrentPhase()?.end(); } /** @@ -140,6 +132,9 @@ export default class GameManager { }); await this.phaseInterceptor.run(EncounterPhase); + if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0) { + removeEnemyHeldItems(this); + } } /** @@ -179,11 +174,11 @@ export default class GameManager { // Confirm target selection if move is multi-target this.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { const handler = this.scene.ui.getHandler() as TargetSelectUiHandler; - const move = (this.scene.getCurrentPhase() as SelectTargetPhase).getPokemon().getMoveset()[movePosition].getMove(); + const move = (this.scene.getCurrentPhase() as SelectTargetPhase).getPokemon().getMoveset()[movePosition]!.getMove(); // TODO: is the bang correct? if (move.isMultiTarget()) { handler.processInput(Button.ACTION); } - }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(TurnEndPhase)); + }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(MovePhase) || this.isCurrentPhase(TurnEndPhase)); } /** @@ -260,7 +255,7 @@ export default class GameManager { */ isCurrentPhase(phaseTarget) { const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; - return this.scene.getCurrentPhase().constructor.name === targetName; + return this.scene.getCurrentPhase()?.constructor.name === targetName; } /** @@ -283,7 +278,7 @@ export default class GameManager { await waitUntil(() => this.scene.ui?.getMode() === Mode.TITLE); await this.scene.gameData.tryExportData(GameDataType.SESSION, 0); await waitUntil(() => localStorage.hasOwnProperty("toExport")); - return resolve(localStorage.getItem("toExport")); + return resolve(localStorage.getItem("toExport")!); // TODO: is this bang correct?; }); } @@ -318,26 +313,45 @@ export default class GameManager { } /** - * Switch pokemon and transition to the enemy command phase + * Command an in-battle switch to another Pokemon via the main battle menu. * @param pokemonIndex the index of the pokemon in your party to switch to */ doSwitchPokemon(pokemonIndex: number) { this.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - this.scene.ui.setMode(Mode.PARTY, PartyUiMode.SWITCH, (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getFieldIndex(), null, PartyUiHandler.FilterNonFainted); - }); - this.onNextPrompt("CommandPhase", Mode.PARTY, () => { - (this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.POKEMON, pokemonIndex, false); + (this.scene.ui.getHandler() as CommandUiHandler).setCursor(2); + (this.scene.ui.getHandler() as CommandUiHandler).processInput(Button.ACTION); }); + + this.doSelectPartyPokemon(pokemonIndex, "CommandPhase"); } /** - * Revive pokemon, currently player's only. + * Revive pokemon, currently players only. * @param pokemonIndex the index of the pokemon in your party to revive */ doRevivePokemon(pokemonIndex: number) { const party = this.scene.getParty(); const candidate = new ModifierTypeOption(modifierTypes.MAX_REVIVE(), 0); - const modifier = candidate.type.newModifier(party[pokemonIndex]); + const modifier = candidate.type!.newModifier(party[pokemonIndex]); this.scene.addModifier(modifier, false); } + + /** + * Select a pokemon from the party menu. Only really handles the basic cases + * of the party UI, where you just need to navigate to a party slot and press + * Action twice - navigating any menus that come up after you select a party member + * is not supported. + * @param slot the index of the pokemon in your party to switch to + * @param inPhase Which phase to expect the selection to occur in. Typically + * non-command switch actions happen in SwitchPhase. + */ + doSelectPartyPokemon(slot: number, inPhase = "SwitchPhase") { + this.onNextPrompt(inPhase, Mode.PARTY, () => { + const partyHandler = this.scene.ui.getHandler() as PartyUiHandler; + + partyHandler.setCursor(slot); + partyHandler.processInput(Button.ACTION); // select party slot + partyHandler.processInput(Button.ACTION); // send out (or whatever option is at the top) + }); + } } diff --git a/src/test/utils/gameManagerUtils.ts b/src/test/utils/gameManagerUtils.ts index 74a73bb7875..dfba55fc75c 100644 --- a/src/test/utils/gameManagerUtils.ts +++ b/src/test/utils/gameManagerUtils.ts @@ -85,6 +85,7 @@ export function waitUntil(truth) { export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: Moves) { const playerPokemon = scene.getPlayerField()[pokemonIndex]; const moveSet = playerPokemon.getMoveset(); - const index = moveSet.findIndex((m) => m.moveId === move); + const index = moveSet.findIndex((m) => m?.moveId === move); + console.log(`Move position for ${Moves[move]} (=${move}):`, index); return index; } diff --git a/src/test/utils/gameWrapper.ts b/src/test/utils/gameWrapper.ts index de32215fabb..7edb69aef8a 100644 --- a/src/test/utils/gameWrapper.ts +++ b/src/test/utils/gameWrapper.ts @@ -8,23 +8,23 @@ import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; import EventEmitter = Phaser.Events.EventEmitter; import UpdateList = Phaser.GameObjects.UpdateList; -import MockGraphics from "#app/test/utils/mocks/mocksContainer/mockGraphics"; -import MockTextureManager from "#app/test/utils/mocks/mockTextureManager"; +import MockGraphics from "#test/utils/mocks/mocksContainer/mockGraphics"; +import MockTextureManager from "#test/utils/mocks/mockTextureManager"; import Phaser from "phaser"; -import {blobToString} from "#app/test/utils/gameManagerUtils"; -import {vi} from "vitest"; -import mockLocalStorage from "#app/test/utils/mocks/mockLocalStorage"; -import mockConsoleLog from "#app/test/utils/mocks/mockConsoleLog"; -import MockLoader from "#app/test/utils/mocks/mockLoader"; -import {MockFetch} from "#app/test/utils/mocks/mockFetch"; +import { blobToString } from "#test/utils/gameManagerUtils"; +import { vi } from "vitest"; +import mockLocalStorage from "#test/utils/mocks/mockLocalStorage"; +import mockConsoleLog from "#test/utils/mocks/mockConsoleLog"; +import MockLoader from "#test/utils/mocks/mockLoader"; +import { MockFetch } from "#test/utils/mocks/mockFetch"; import * as Utils from "#app/utils"; import InputText from "phaser3-rex-plugins/plugins/inputtext"; -import {MockClock} from "#app/test/utils/mocks/mockClock"; +import { MockClock } from "#test/utils/mocks/mockClock"; import BattleScene from "#app/battle-scene.js"; -import {MoveAnim} from "#app/data/battle-anims"; +import { MoveAnim } from "#app/data/battle-anims"; import Pokemon from "#app/field/pokemon"; import * as battleScene from "#app/battle-scene"; -import MockImage from "#app/test/utils/mocks/mocksContainer/mockImage.js"; +import MockImage from "#test/utils/mocks/mocksContainer/mockImage.js"; import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; Object.defineProperty(window, "localStorage", { @@ -41,7 +41,7 @@ Phaser.GameObjects.Image = MockImage; window.URL.createObjectURL = (blob: Blob) => { blobToString(blob).then((data: string) => { localStorage.setItem("toExport", data); - }) + }); return null; }; navigator.getGamepads = vi.fn().mockReturnValue([]); @@ -101,7 +101,7 @@ export default class GameWrapper { injectMandatory() { this.game.config = { seed: ["test"], - } + }; this.scene.game = this.game; this.game.renderer = { maxTextures: -1, @@ -139,7 +139,7 @@ export default class GameWrapper { setPostPipeline: () => null, removePostPipeline: () => null, }, - } + }; this.scene.tweens = { add: (data) => { @@ -254,4 +254,4 @@ function createFetchBadResponse(data) { json: () => Promise.resolve(data), text: () => Promise.resolve(JSON.stringify(data)), }; -} \ No newline at end of file +} diff --git a/src/test/utils/inputsHandler.ts b/src/test/utils/inputsHandler.ts index 043dcffbdb9..148329ada32 100644 --- a/src/test/utils/inputsHandler.ts +++ b/src/test/utils/inputsHandler.ts @@ -1,19 +1,23 @@ import BattleScene from "#app/battle-scene"; import Phaser from "phaser"; -import {InputsController} from "#app/inputs-controller"; +import { InputsController } from "#app/inputs-controller"; import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; -import {holdOn} from "#app/test/utils/gameManagerUtils"; +import { holdOn } from "#test/utils/gameManagerUtils"; import TouchControl from "#app/touch-controls"; import { JSDOM } from "jsdom"; import fs from "fs"; +interface LogEntry { + type: string; + button: any; +} export default class InputsHandler { private scene: BattleScene; private events: Phaser.Events.EventEmitter; private inputController: InputsController; - public log = []; - public logUp = []; + public log: LogEntry[] = []; + public logUp: LogEntry[] = []; private fakePad: Fakepad; private fakeMobile: FakeMobile; @@ -22,7 +26,7 @@ export default class InputsHandler { this.inputController = this.scene.inputController; this.fakePad = new Fakepad(pad_xbox360); this.fakeMobile = new FakeMobile(); - this.scene.input.gamepad.gamepads.push(this.fakePad); + this.scene.input.gamepad?.gamepads.push(this.fakePad); this.init(); } @@ -37,18 +41,18 @@ export default class InputsHandler { pressGamepadButton(button: integer, duration: integer): Promise { return new Promise(async (resolve) => { - this.scene.input.gamepad.emit("down", this.fakePad, {index: button}); + this.scene.input.gamepad?.emit("down", this.fakePad, {index: button}); await holdOn(duration); - this.scene.input.gamepad.emit("up", this.fakePad, {index: button}); + this.scene.input.gamepad?.emit("up", this.fakePad, {index: button}); resolve(); }); } pressKeyboardKey(key: integer, duration: integer): Promise { return new Promise(async (resolve) => { - this.scene.input.keyboard.emit("keydown", {keyCode: key}); + this.scene.input.keyboard?.emit("keydown", {keyCode: key}); await holdOn(duration); - this.scene.input.keyboard.emit("keyup", {keyCode: key}); + this.scene.input.keyboard?.emit("keyup", {keyCode: key}); resolve(); }); } @@ -57,7 +61,7 @@ export default class InputsHandler { const touchControl = new TouchControl(this.scene); touchControl.deactivatePressedKey(); //test purpose this.events = this.inputController.events; - this.scene.input.gamepad.emit("connected", this.fakePad); + this.scene.input.gamepad?.emit("connected", this.fakePad); this.listenInputs(); } @@ -77,7 +81,8 @@ class Fakepad extends Phaser.Input.Gamepad.Gamepad { public index: number; constructor(pad) { - super(undefined, {...pad, buttons: pad.deviceMapping, axes: []}); + //@ts-ignore + super(undefined, {...pad, buttons: pad.deviceMapping, axes: []}); //TODO: resolve ts-ignore this.id = "xbox_360_fakepad"; this.index = 0; } diff --git a/src/test/utils/misc.test.ts b/src/test/utils/misc.test.ts index da67fb48192..c1947dbe8a2 100644 --- a/src/test/utils/misc.test.ts +++ b/src/test/utils/misc.test.ts @@ -1,8 +1,8 @@ -import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; -import GameManager from "#app/test/utils/gameManager"; -import {apiFetch} from "#app/utils"; -import {waitUntil} from "#app/test/utils/gameManagerUtils"; +import GameManager from "#test/utils/gameManager"; +import { apiFetch } from "#app/utils"; +import { waitUntil } from "#test/utils/gameManagerUtils"; describe("Test misc", () => { let phaserGame: Phaser.Game; @@ -59,7 +59,7 @@ describe("Test misc", () => { it("test apifetch mock sync", async () => { const data = await game.scene.cachedFetch("./battle-anims/splishy-splash.json"); - expect(data).not.toBeUndefined(); + expect(data).toBeDefined(); }); it("testing wait phase queue", async () => { diff --git a/src/test/utils/mocks/mockClock.ts b/src/test/utils/mocks/mockClock.ts index ba12dc002cc..3664a831305 100644 --- a/src/test/utils/mocks/mockClock.ts +++ b/src/test/utils/mocks/mockClock.ts @@ -2,7 +2,7 @@ import Clock = Phaser.Time.Clock; export class MockClock extends Clock { - public overrideDelay: number; + public overrideDelay: number | undefined; constructor(scene) { super(scene); this.overrideDelay = undefined; diff --git a/src/test/utils/mocks/mockConsoleLog.ts b/src/test/utils/mocks/mockConsoleLog.ts index 61c6c14b989..f44a0c7d6cd 100644 --- a/src/test/utils/mocks/mockConsoleLog.ts +++ b/src/test/utils/mocks/mockConsoleLog.ts @@ -1,12 +1,12 @@ const MockConsoleLog = (_logDisabled= false, _phaseText=false) => { - let logs = []; + let logs: any[] = []; const logDisabled: boolean = _logDisabled; const phaseText: boolean = _phaseText; const originalLog = console.log; const originalError = console.error; const originalDebug = console.debug; const originalWarn = console.warn; - const notified = []; + const notified: any[] = []; const blacklist = ["Phaser", "variant icon does not exist", "Texture \"%s\" not found"]; const whitelist = ["Phase"]; diff --git a/src/test/utils/mocks/mockGameObject.ts b/src/test/utils/mocks/mockGameObject.ts new file mode 100644 index 00000000000..9138e0f687a --- /dev/null +++ b/src/test/utils/mocks/mockGameObject.ts @@ -0,0 +1,3 @@ +export interface MockGameObject { + +} diff --git a/src/test/utils/mocks/mockTextureManager.ts b/src/test/utils/mocks/mockTextureManager.ts index e207f96b722..95e8996836f 100644 --- a/src/test/utils/mocks/mockTextureManager.ts +++ b/src/test/utils/mocks/mockTextureManager.ts @@ -1,10 +1,11 @@ -import MockContainer from "#app/test/utils/mocks/mocksContainer/mockContainer"; -import MockSprite from "#app/test/utils/mocks/mocksContainer/mockSprite"; -import MockRectangle from "#app/test/utils/mocks/mocksContainer/mockRectangle"; -import MockNineslice from "#app/test/utils/mocks/mocksContainer/mockNineslice"; -import MockImage from "#app/test/utils/mocks/mocksContainer/mockImage"; -import MockText from "#app/test/utils/mocks/mocksContainer/mockText"; -import MockPolygon from "#app/test/utils/mocks/mocksContainer/mockPolygon"; +import MockContainer from "#test/utils/mocks/mocksContainer/mockContainer"; +import MockSprite from "#test/utils/mocks/mocksContainer/mockSprite"; +import MockRectangle from "#test/utils/mocks/mocksContainer/mockRectangle"; +import MockNineslice from "#test/utils/mocks/mocksContainer/mockNineslice"; +import MockImage from "#test/utils/mocks/mocksContainer/mockImage"; +import MockText from "#test/utils/mocks/mocksContainer/mockText"; +import MockPolygon from "#test/utils/mocks/mocksContainer/mockPolygon"; +import { MockGameObject } from "./mockGameObject"; export default class MockTextureManager { @@ -12,7 +13,7 @@ export default class MockTextureManager { private scene; public add; public displayList; - public list = []; + public list: MockGameObject[] = []; constructor(scene) { this.scene = scene; diff --git a/src/test/utils/mocks/mocksContainer/mockContainer.ts b/src/test/utils/mocks/mocksContainer/mockContainer.ts index 2e2b9567796..d3672cb5235 100644 --- a/src/test/utils/mocks/mocksContainer/mockContainer.ts +++ b/src/test/utils/mocks/mocksContainer/mockContainer.ts @@ -1,6 +1,7 @@ -import MockTextureManager from "#app/test/utils/mocks/mockTextureManager"; +import MockTextureManager from "#test/utils/mocks/mockTextureManager"; +import { MockGameObject } from "../mockGameObject"; -export default class MockContainer { +export default class MockContainer implements MockGameObject { protected x; protected y; protected scene; @@ -11,7 +12,7 @@ export default class MockContainer { private style; public frame; protected textureManager; - public list = []; + public list: MockGameObject[] = []; constructor(textureManager: MockTextureManager, x, y) { this.x = x; diff --git a/src/test/utils/mocks/mocksContainer/mockGraphics.ts b/src/test/utils/mocks/mocksContainer/mockGraphics.ts index a08f150fe61..e026b212e16 100644 --- a/src/test/utils/mocks/mocksContainer/mockGraphics.ts +++ b/src/test/utils/mocks/mocksContainer/mockGraphics.ts @@ -1,6 +1,8 @@ -export default class MockGraphics { +import { MockGameObject } from "../mockGameObject"; + +export default class MockGraphics implements MockGameObject { private scene; - public list = []; + public list: MockGameObject[] = []; constructor(textureManager, config) { this.scene = textureManager.scene; } diff --git a/src/test/utils/mocks/mocksContainer/mockImage.ts b/src/test/utils/mocks/mocksContainer/mockImage.ts index 601dfd22811..be183a0dd89 100644 --- a/src/test/utils/mocks/mocksContainer/mockImage.ts +++ b/src/test/utils/mocks/mocksContainer/mockImage.ts @@ -1,4 +1,4 @@ -import MockContainer from "#app/test/utils/mocks/mocksContainer/mockContainer"; +import MockContainer from "#test/utils/mocks/mocksContainer/mockContainer"; export default class MockImage extends MockContainer { diff --git a/src/test/utils/mocks/mocksContainer/mockNineslice.ts b/src/test/utils/mocks/mocksContainer/mockNineslice.ts index 3edbbb18c31..a8e10036a72 100644 --- a/src/test/utils/mocks/mocksContainer/mockNineslice.ts +++ b/src/test/utils/mocks/mocksContainer/mockNineslice.ts @@ -1,4 +1,4 @@ -import MockContainer from "#app/test/utils/mocks/mocksContainer/mockContainer"; +import MockContainer from "#test/utils/mocks/mocksContainer/mockContainer"; export default class MockNineslice extends MockContainer { diff --git a/src/test/utils/mocks/mocksContainer/mockPolygon.ts b/src/test/utils/mocks/mocksContainer/mockPolygon.ts index cf448883ce0..12b60904a96 100644 --- a/src/test/utils/mocks/mocksContainer/mockPolygon.ts +++ b/src/test/utils/mocks/mocksContainer/mockPolygon.ts @@ -1,4 +1,4 @@ -import MockContainer from "#app/test/utils/mocks/mocksContainer/mockContainer"; +import MockContainer from "#test/utils/mocks/mocksContainer/mockContainer"; export default class MockPolygon extends MockContainer { diff --git a/src/test/utils/mocks/mocksContainer/mockRectangle.ts b/src/test/utils/mocks/mocksContainer/mockRectangle.ts index db323268037..26c2f74ea42 100644 --- a/src/test/utils/mocks/mocksContainer/mockRectangle.ts +++ b/src/test/utils/mocks/mocksContainer/mockRectangle.ts @@ -1,7 +1,9 @@ -export default class MockRectangle { +import { MockGameObject } from "../mockGameObject"; + +export default class MockRectangle implements MockGameObject { private fillColor; private scene; - public list = []; + public list: MockGameObject[] = []; constructor(textureManager, x, y, width, height, fillColor) { this.fillColor = fillColor; diff --git a/src/test/utils/mocks/mocksContainer/mockSprite.ts b/src/test/utils/mocks/mocksContainer/mockSprite.ts index fb7f84741e8..9c566fc4bcb 100644 --- a/src/test/utils/mocks/mocksContainer/mockSprite.ts +++ b/src/test/utils/mocks/mocksContainer/mockSprite.ts @@ -1,9 +1,10 @@ +import { MockGameObject } from "../mockGameObject"; import Sprite = Phaser.GameObjects.Sprite; import Frame = Phaser.Textures.Frame; import Phaser from "phaser"; -export default class MockSprite { +export default class MockSprite implements MockGameObject { private phaserSprite; public pipelineData; public texture; @@ -12,10 +13,11 @@ export default class MockSprite { public textureManager; public scene; public anims; - public list = []; + public list: MockGameObject[] = []; constructor(textureManager, x, y, texture) { this.textureManager = textureManager; this.scene = textureManager.scene; + // @ts-ignore Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive; // @ts-ignore Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture; diff --git a/src/test/utils/mocks/mocksContainer/mockText.ts b/src/test/utils/mocks/mocksContainer/mockText.ts index f4513e20926..5d405efadfd 100644 --- a/src/test/utils/mocks/mocksContainer/mockText.ts +++ b/src/test/utils/mocks/mocksContainer/mockText.ts @@ -1,12 +1,13 @@ import UI from "#app/ui/ui"; +import { MockGameObject } from "../mockGameObject"; -export default class MockText { +export default class MockText implements MockGameObject { private phaserText; private wordWrapWidth; private splitRegExp; private scene; private textureManager; - public list = []; + public list: MockGameObject[] = []; public style; public text = ""; diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index 2858076e3b6..e77cd49b742 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -124,6 +124,17 @@ export class OverridesHelper { return this; } + /** + * Override the player (pokemon) {@linkcode StatusEffect | status-effect} + * @param statusEffect the {@linkcode StatusEffect | status-effect} to set + * @returns + */ + statusEffect(statusEffect: StatusEffect): this { + vi.spyOn(Overrides, "STATUS_OVERRIDE", "get").mockReturnValue(statusEffect); + this.log(`Player Pokemon status-effect set to ${StatusEffect[statusEffect]} (=${statusEffect})!`); + return this; + } + /** * Override each wave to not have standard trainer battles * @returns this @@ -181,7 +192,7 @@ export class OverridesHelper { * @param battleType battle type to set * @returns this */ - battleType(battleType: "single" | "double"): this { + battleType(battleType: "single" | "double" | null): this { vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue(battleType); this.log(`Battle type set to ${battleType} only!`); return this; diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index e8cec7c9989..34f79f93b6e 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -34,15 +34,15 @@ import { UnavailablePhase, VictoryPhase } from "#app/phases"; -import UI, {Mode} from "#app/ui/ui"; -import {Phase} from "#app/phase"; +import UI, { Mode } from "#app/ui/ui"; +import { Phase } from "#app/phase"; import ErrorInterceptor from "#app/test/utils/errorInterceptor"; -import {QuietFormChangePhase} from "#app/form-change-phase"; +import { QuietFormChangePhase } from "#app/form-change-phase"; export default class PhaseInterceptor { public scene; public phases = {}; - public log; + public log: string[]; private onHold; private interval; private promptInterval; @@ -104,13 +104,20 @@ export default class PhaseInterceptor { */ constructor(scene) { this.scene = scene; - this.log = []; this.onHold = []; this.prompts = []; + this.clearLogs(); this.startPromptHandler(); this.initPhases(); } + /** + * Clears phase logs + */ + clearLogs() { + this.log = []; + } + rejectAll(error) { if (this.inProgress) { clearInterval(this.promptInterval); @@ -282,7 +289,7 @@ export default class PhaseInterceptor { setMode(mode: Mode, ...args: any[]): Promise { const currentPhase = this.scene.getCurrentPhase(); const instance = this.scene.ui; - console.log("setMode", mode, args); + console.log("setMode", `${Mode[mode]} (=${mode})`, args); const ret = this.originalSetMode.apply(instance, [mode, ...args]); if (!this.phases[currentPhase.constructor.name]) { throw new Error(`missing ${currentPhase.constructor.name} in phaseInterceptior PHASES list`); @@ -321,7 +328,7 @@ export default class PhaseInterceptor { * @param callback - The callback function to execute. * @param expireFn - The function to determine if the prompt has expired. */ - addToNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn: () => void, awaitingActionInput: boolean = false) { + addToNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn?: () => void, awaitingActionInput: boolean = false) { this.prompts.push({ phaseTarget, mode, diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts index 4a807c7d892..d5f266cd4ae 100644 --- a/src/test/utils/testUtils.ts +++ b/src/test/utils/testUtils.ts @@ -1,7 +1,9 @@ -import BattleScene from "#app/battle-scene.js"; import { Moves } from "#app/enums/moves.js"; import i18next, { type ParseKeys } from "i18next"; import { vi } from "vitest"; +import GameManager from "./gameManager"; +import { BattlerIndex } from "#app/battle.js"; +import { MoveEffectPhase, TurnStartPhase } from "#app/phases.js"; /** Ready to use array of Moves.SPLASH x4 */ export const SPLASH_ONLY = [Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]; @@ -29,9 +31,42 @@ export function arrayOfRange(start: integer, end: integer) { /** * Removes all held items from enemy pokemon - * @param scene `game.scene` + * @param game The {@link GameManager} instance */ -export function removeEnemyHeldItems(scene: BattleScene) { - scene.clearEnemyHeldItemModifiers(); - scene.clearEnemyModifiers(); +export function removeEnemyHeldItems(game: GameManager): void { + game.scene.clearEnemyHeldItemModifiers(); + game.scene.clearEnemyModifiers(); + console.log("Enemy held items removed"); +} + +/** + * Intercepts `TurnStartPhase` and mocks the getOrder's return value {@linkcode TurnStartPhase.getOrder} + * Used to modify the turn order. + * @param {GameManager} game The GameManager instance + * @param {BattlerIndex[]} order The turn order to set + * @example + * ```ts + * await mockTurnOrder(game, [BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2]); + * ``` + */ +export async function mockTurnOrder(game: GameManager, order: BattlerIndex[]): Promise { + await game.phaseInterceptor.to(TurnStartPhase, false); + + vi.spyOn(game.scene.getCurrentPhase() as TurnStartPhase, "getOrder").mockReturnValue(order); +} + +/** + * Intercepts `MoveEffectPhase` and mocks the hitCheck's return value {@linkcode MoveEffectPhase.hitCheck}. + * Used to force a move to either hit or miss. + * Note that this uses `mockReturnValue()`, meaning it will also apply to a + * succeeding `MoveEffectPhase` immediately following the first one + * (in the case of a multi-target move) + * + * @param {GameManager} game The GameManager instance + * @param shouldHit Whether the move should hit + */ +export async function mockHitCheck(game: GameManager, shouldHit: boolean): Promise { + await game.phaseInterceptor.to(MoveEffectPhase, false); + + vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(shouldHit); } diff --git a/src/test/vitest.setup.ts b/src/test/vitest.setup.ts index 7cddf4bf3b7..b2861b7071c 100644 --- a/src/test/vitest.setup.ts +++ b/src/test/vitest.setup.ts @@ -1,4 +1,4 @@ -import "#app/test/fontFace.setup"; +import "#test/fontFace.setup"; import "vitest-canvas-mock"; import { initLoggedInUser } from "#app/account"; diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index dac67bd7b4e..0502080d3a5 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -52,32 +52,32 @@ export class TimedEventManager { let multiplier = 1; const shinyEvents = timedEvents.filter((te) => te.eventType === EventType.SHINY && this.isActive(te)); shinyEvents.forEach((se) => { - multiplier *= se.shinyMultiplier; + multiplier *= se.shinyMultiplier!; // TODO: is this bang correct? }); return multiplier; } getEventBannerFilename(): string { - return timedEvents.find((te: TimedEvent) => this.isActive(te)).bannerFilename ?? null; + return timedEvents.find((te: TimedEvent) => this.isActive(te))?.bannerFilename!; // TODO: is this bang correct? } } export class TimedEventDisplay extends Phaser.GameObjects.Container { - private event: TimedEvent; + private event: TimedEvent | null; private eventTimerText: Phaser.GameObjects.Text; private banner: Phaser.GameObjects.Image; private bannerShadow: Phaser.GameObjects.Rectangle; - private eventTimer: NodeJS.Timeout; + private eventTimer: NodeJS.Timeout | null; - constructor(scene: BattleScene, x: number, y: number, event: TimedEvent) { + constructor(scene: BattleScene, x: number, y: number, event?: TimedEvent) { super(scene, x, y); - this.event = event; + this.event = event!; // TODO: is this bang correct? this.setVisible(false); } setup() { - this.banner = new Phaser.GameObjects.Image(this.scene, 29, 64, this.event.bannerFilename); + this.banner = new Phaser.GameObjects.Image(this.scene, 29, 64, this.event!.bannerFilename!); // TODO: are the bangs correct here? this.banner.setName("img-event-banner"); this.banner.setOrigin(0, 0); this.banner.setScale(0.07); @@ -97,7 +97,7 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { this.scene, this.banner.x + 8, this.banner.y + 100, - this.timeToGo(this.event.endDate), + this.timeToGo(this.event!.endDate), // TODO: is the bang correct here? TextStyle.WINDOW ); this.eventTimerText.setName("text-event-timer"); @@ -118,7 +118,7 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { clear() { this.setVisible(false); - clearInterval(this.eventTimer); + this.eventTimer && clearInterval(this.eventTimer); this.eventTimer = null; } @@ -145,6 +145,6 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { } updateCountdown() { - this.eventTimerText.setText(this.timeToGo(this.event.endDate)); + this.eventTimerText.setText(this.timeToGo(this.event!.endDate)); // TODO: is the bang correct here? } } diff --git a/src/touch-controls.ts b/src/touch-controls.ts index 9953e99c81a..1726b47a4bd 100644 --- a/src/touch-controls.ts +++ b/src/touch-controls.ts @@ -48,7 +48,7 @@ export default class TouchControl { node.addEventListener("touchend", event => { event.preventDefault(); - this.touchButtonUp(node, key, event.target["id"]); + this.touchButtonUp(node, key, event.target?.["id"]); }); } @@ -120,7 +120,7 @@ export default class TouchControl { * Prevent zoom on specified element * @param {HTMLElement} element */ - preventElementZoom(element: HTMLElement): void { + preventElementZoom(element: HTMLElement | null): void { if (!element) { return; } diff --git a/src/ui/ability-bar.ts b/src/ui/ability-bar.ts index 7a4ae559975..b8259af9f3d 100644 --- a/src/ui/ability-bar.ts +++ b/src/ui/ability-bar.ts @@ -12,8 +12,8 @@ export default class AbilityBar extends Phaser.GameObjects.Container { private bg: Phaser.GameObjects.Image; private abilityBarText: Phaser.GameObjects.Text; - private tween: Phaser.Tweens.Tween; - private autoHideTimer: NodeJS.Timeout; + private tween: Phaser.Tweens.Tween | null; + private autoHideTimer: NodeJS.Timeout | null; public shown: boolean; diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index 2069f034e89..ae7f107efc0 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -1,5 +1,5 @@ import BattleScene from "../battle-scene"; -import { TextStyle, addTextObject } from "./text"; +import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; @@ -36,29 +36,31 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { protected optionSelectText: Phaser.GameObjects.Text; protected optionSelectIcons: Phaser.GameObjects.Sprite[]; - protected config: OptionSelectConfig; + protected config: OptionSelectConfig | null; protected blockInput: boolean; protected scrollCursor: integer = 0; - private cursorObj: Phaser.GameObjects.Image; + protected scale: number = 0.1666666667; - constructor(scene: BattleScene, mode?: Mode) { + private cursorObj: Phaser.GameObjects.Image | null; + + constructor(scene: BattleScene, mode: Mode | null) { super(scene, mode); } abstract getWindowWidth(): integer; getWindowHeight(): integer { - return (Math.min((this.config?.options || []).length, this.config?.maxOptions || 99) + 1) * 16; + return (Math.min((this.config?.options || []).length, this.config?.maxOptions || 99) + 1) * 96 * this.scale; } setup() { const ui = this.getUi(); this.optionSelectContainer = this.scene.add.container((this.scene.game.canvas.width / 6) - 1, -48); - this.optionSelectContainer.setName(`option-select-${Mode[this.mode]}`); + this.optionSelectContainer.setName(`option-select-${this.mode ? Mode[this.mode] : "UNKNOWN"}`); this.optionSelectContainer.setVisible(false); ui.add(this.optionSelectContainer); @@ -69,6 +71,8 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.optionSelectIcons = []; + this.scale = getTextStyleOptions(TextStyle.WINDOW, (this.scene as BattleScene).uiTheme).scale; + this.setCursor(0); } @@ -84,6 +88,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { } this.optionSelectText = addTextObject(this.scene, 0, 0, options.map(o => o.item ? ` ${o.label}` : o.label).join("\n"), TextStyle.WINDOW, { maxLines: options.length }); + this.optionSelectText.setLineSpacing(this.scale * 72); this.optionSelectText.setName("text-option-select"); this.optionSelectText.setLineSpacing(12); this.optionSelectContainer.add(this.optionSelectText); @@ -91,35 +96,37 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.optionSelectBg.width = Math.max(this.optionSelectText.displayWidth + 24, this.getWindowWidth()); - if (this.config?.options.length > this.config?.maxOptions) { + if (this.config?.options && this.config?.options.length > (this.config?.maxOptions!)) { // TODO: is this bang correct? this.optionSelectText.setText(this.getOptionsWithScroll().map(o => o.label).join("\n")); } this.optionSelectBg.height = this.getWindowHeight(); - this.optionSelectText.setPositionRelative(this.optionSelectBg, 16, 9); + this.optionSelectText.setPositionRelative(this.optionSelectBg, 12+24*this.scale, 2+42*this.scale); options.forEach((option: OptionSelectItem, i: integer) => { if (option.item) { const itemIcon = this.scene.add.sprite(0, 0, "items", option.item); - itemIcon.setScale(0.5); + itemIcon.setScale(3 * this.scale); this.optionSelectIcons.push(itemIcon); this.optionSelectContainer.add(itemIcon); - itemIcon.setPositionRelative(this.optionSelectText, 6, 7 + 16 * i); + itemIcon.setPositionRelative(this.optionSelectText, 36 * this.scale, 7 + i * (114 * this.scale - 3)); if (option.item === "candy") { const itemOverlayIcon = this.scene.add.sprite(0, 0, "items", "candy_overlay"); - itemOverlayIcon.setScale(0.5); + itemOverlayIcon.setScale(3 * this.scale); this.optionSelectIcons.push(itemOverlayIcon); this.optionSelectContainer.add(itemOverlayIcon); - itemOverlayIcon.setPositionRelative(this.optionSelectText, 6, 7 + 16 * i); + itemOverlayIcon.setPositionRelative(this.optionSelectText, 36 * this.scale, 7 + i * (114 * this.scale - 3)); - itemIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[0]))); - itemOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[1]))); + if (option.itemArgs) { + itemIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[0]))); + itemOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[1]))); + } } } }); @@ -294,7 +301,8 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.optionSelectContainer.add(this.cursorObj); } - this.cursorObj.setPositionRelative(this.optionSelectBg, 12, 17 + this.cursor * 16); + this.cursorObj.setScale(this.scale * 6); + this.cursorObj.setPositionRelative(this.optionSelectBg, 12, 102*this.scale + this.cursor * (114 * this.scale - 3)); return changed; } diff --git a/src/ui/achv-bar.ts b/src/ui/achv-bar.ts index 05dde03aed7..0f3ab7c2e47 100644 --- a/src/ui/achv-bar.ts +++ b/src/ui/achv-bar.ts @@ -28,7 +28,7 @@ export default class AchvBar extends Phaser.GameObjects.Container { this.defaultWidth = 160; this.defaultHeight = 40; - this.bg = this.scene.add.nineslice(0, 0, "achv_bar", null, this.defaultWidth, this.defaultHeight, 41, 6, 16, 4); + this.bg = this.scene.add.nineslice(0, 0, "achv_bar", undefined, this.defaultWidth, this.defaultHeight, 41, 6, 16, 4); this.bg.setOrigin(0, 0); this.add(this.bg); @@ -119,7 +119,8 @@ export default class AchvBar extends Phaser.GameObjects.Container { this.shown = false; this.setVisible(false); if (this.queue.length) { - this.showAchv(this.queue.shift()); + const shifted = this.queue.shift(); + shifted && this.showAchv(shifted); } } }); diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index b5e17ce2b67..ad707fb52e1 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -10,6 +10,9 @@ import { ParseKeys } from "i18next"; import { PlayerGender } from "#enums/player-gender"; export default class AchvsUiHandler extends MessageUiHandler { + private readonly ACHV_ROWS = 4; + private readonly ACHV_COLS = 17; + private achvsContainer: Phaser.GameObjects.Container; private achvIconsContainer: Phaser.GameObjects.Container; @@ -19,10 +22,16 @@ export default class AchvsUiHandler extends MessageUiHandler { private scoreText: Phaser.GameObjects.Text; private unlockText: Phaser.GameObjects.Text; - private cursorObj: Phaser.GameObjects.NineSlice; + private achvsTotal: number; + private scrollCursor: number; - constructor(scene: BattleScene, mode?: Mode) { + private cursorObj: Phaser.GameObjects.NineSlice | null; + + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); + + this.achvsTotal = Object.keys(achvs).length; + this.scrollCursor = 0; } setup() { @@ -53,9 +62,9 @@ export default class AchvsUiHandler extends MessageUiHandler { this.achvIcons = []; - for (let a = 0; a < Object.keys(achvs).length; a++) { - const x = (a % 17) * 18; - const y = Math.floor(a / 17) * 18; + for (let a = 0; a < this.ACHV_ROWS * this.ACHV_COLS; a++) { + const x = (a % this.ACHV_COLS) * 18; + const y = Math.floor(a / this.ACHV_COLS) * 18; const icon = this.scene.add.sprite(x, y, "items", "unknown"); icon.setOrigin(0, 0); @@ -119,24 +128,11 @@ export default class AchvsUiHandler extends MessageUiHandler { show(args: any[]): boolean { super.show(args); - const achvUnlocks = this.scene.gameData.achvUnlocks; - - Object.values(achvs).forEach((achv: Achv, i: integer) => { - const icon = this.achvIcons[i]; - const unlocked = achvUnlocks.hasOwnProperty(achv.id); - const hidden = !unlocked && achv.secret && (!achv.parentId || !achvUnlocks.hasOwnProperty(achv.parentId)); - const tinted = !hidden && !unlocked; - - icon.setFrame(!hidden ? achv.iconImage : "unknown"); - if (tinted) { - icon.setTintFill(0); - } else { - icon.clearTint(); - } - }); + this.updateAchvIcons(); this.achvsContainer.setVisible(true); this.setCursor(0); + this.setScrollCursor(0); this.getUi().moveTo(this.achvsContainer, this.getUi().length - 1); @@ -173,24 +169,39 @@ export default class AchvsUiHandler extends MessageUiHandler { success = true; this.scene.ui.revertMode(); } else { + const rowIndex = Math.floor(this.cursor / this.ACHV_COLS); + const itemOffset = (this.scrollCursor * this.ACHV_COLS); switch (button) { case Button.UP: - if (this.cursor >= 17) { - success = this.setCursor(this.cursor - 17); + if (this.cursor < this.ACHV_COLS) { + if (this.scrollCursor) { + success = this.setScrollCursor(this.scrollCursor - 1); + } + } else { + success = this.setCursor(this.cursor - this.ACHV_COLS); } break; case Button.DOWN: - if (this.cursor + 17 < Object.keys(achvs).length) { - success = this.setCursor(this.cursor + 17); + const canMoveDown = (this.cursor + itemOffset) + this.ACHV_COLS < this.achvsTotal; + if (rowIndex >= this.ACHV_ROWS - 1) { + if (this.scrollCursor < Math.ceil(this.achvsTotal / this.ACHV_COLS) - this.ACHV_ROWS && canMoveDown) { + success = this.setScrollCursor(this.scrollCursor + 1); + } + } else if (canMoveDown) { + success = this.setCursor(this.cursor + this.ACHV_COLS); } break; case Button.LEFT: - if (this.cursor) { + if (!this.cursor && this.scrollCursor) { + success = this.setScrollCursor(this.scrollCursor - 1) && this.setCursor(this.cursor + (this.ACHV_COLS - 1)); + } else if (this.cursor) { success = this.setCursor(this.cursor - 1); } break; case Button.RIGHT: - if (this.cursor < Object.keys(achvs).length - 1) { + if (this.cursor + 1 === this.ACHV_ROWS * this.ACHV_COLS && this.scrollCursor < Math.ceil(this.achvsTotal / this.ACHV_COLS) - this.ACHV_ROWS) { + success = this.setScrollCursor(this.scrollCursor + 1) && this.setCursor(this.cursor - (this.ACHV_COLS - 1)); + } else if (this.cursor + itemOffset < this.achvsTotal - 1) { success = this.setCursor(this.cursor + 1); } break; @@ -210,7 +221,7 @@ export default class AchvsUiHandler extends MessageUiHandler { let updateAchv = ret; if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight", null, 16, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight", undefined, 16, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.achvIconsContainer.add(this.cursorObj); updateAchv = true; @@ -219,12 +230,65 @@ export default class AchvsUiHandler extends MessageUiHandler { this.cursorObj.setPositionRelative(this.achvIcons[this.cursor], 0, 0); if (updateAchv) { - this.showAchv(achvs[Object.keys(achvs)[cursor]]); + this.showAchv(achvs[Object.keys(achvs)[cursor + this.scrollCursor * this.ACHV_COLS]]); } return ret; } + /** + * setScrollCursor(scrollCursor: integer) : boolean + * scrollCursor refers to the page's position within the entire sum of the data, unlike cursor, which refers to a user's position within displayed data + * @param takes a scrollCursor that has been updated based on user behavior + * @returns returns a boolean that indicates whether the updated scrollCursor led to an update in the data displayed. + */ + setScrollCursor(scrollCursor: integer): boolean { + if (scrollCursor === this.scrollCursor) { + return false; + } + + this.scrollCursor = scrollCursor; + + this.updateAchvIcons(); + + this.showAchv(achvs[Object.keys(achvs)[Math.min(this.cursor + this.scrollCursor * this.ACHV_COLS, Object.values(achvs).length - 1)]]); + + return true; + } + + + /** + * updateAchvIcons(): void + * Determines what data is to be displayed on the UI and updates it accordingly based on the current value of this.scrollCursor + */ + updateAchvIcons(): void { + const achvUnlocks = this.scene.gameData.achvUnlocks; + + const itemOffset = this.scrollCursor * this.ACHV_COLS; + const itemLimit = this.ACHV_ROWS * this.ACHV_COLS; + + const achvRange = Object.values(achvs).slice(itemOffset, itemLimit + itemOffset); + + achvRange.forEach((achv: Achv, i: integer) => { + const icon = this.achvIcons[i]; + const unlocked = achvUnlocks.hasOwnProperty(achv.id); + const hidden = !unlocked && achv.secret && (!achv.parentId || !achvUnlocks.hasOwnProperty(achv.parentId)); + const tinted = !hidden && !unlocked; + + icon.setFrame(!hidden ? achv.iconImage : "unknown"); + icon.setVisible(true); + if (tinted) { + icon.setTintFill(0); + } else { + icon.clearTint(); + } + }); + + if (achvRange.length < this.achvIcons.length) { + this.achvIcons.slice(achvRange.length).map(i => i.setVisible(false)); + } + } + clear() { super.clear(); this.achvsContainer.setVisible(false); diff --git a/src/ui/awaitable-ui-handler.ts b/src/ui/awaitable-ui-handler.ts index 18635b132f9..2052c6e2ade 100644 --- a/src/ui/awaitable-ui-handler.ts +++ b/src/ui/awaitable-ui-handler.ts @@ -5,10 +5,10 @@ import {Button} from "#enums/buttons"; export default abstract class AwaitableUiHandler extends UiHandler { protected awaitingActionInput: boolean; - protected onActionInput: Function; + protected onActionInput: Function | null; public tutorialActive: boolean = false; - constructor(scene: BattleScene, mode: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); } diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index 70349444647..d8b3e5e3ee8 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -1,7 +1,7 @@ import { CommandPhase } from "../phases"; import BattleScene from "../battle-scene"; import { getPokeballName } from "../data/pokeball"; -import { addTextObject, TextStyle } from "./text"; +import { addTextObject, getTextStyleOptions, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; @@ -13,7 +13,9 @@ export default class BallUiHandler extends UiHandler { private pokeballSelectBg: Phaser.GameObjects.NineSlice; private countsText: Phaser.GameObjects.Text; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; + + private scale: number = 0.1666666667; constructor(scene: BattleScene) { super(scene, Mode.BALL); @@ -22,13 +24,7 @@ export default class BallUiHandler extends UiHandler { setup() { const ui = this.getUi(); - this.pokeballSelectContainer = this.scene.add.container((this.scene.game.canvas.width / 6) - 115, -49); - this.pokeballSelectContainer.setVisible(false); - ui.add(this.pokeballSelectContainer); - - this.pokeballSelectBg = addWindow(this.scene, 0, 0, 114, 112); - this.pokeballSelectBg.setOrigin(0, 1); - this.pokeballSelectContainer.add(this.pokeballSelectBg); + this.scale = getTextStyleOptions(TextStyle.WINDOW, this.scene.uiTheme).scale; let optionsTextContent = ""; @@ -37,14 +33,22 @@ export default class BallUiHandler extends UiHandler { } optionsTextContent += "Cancel"; const optionsText = addTextObject(this.scene, 0, 0, optionsTextContent, TextStyle.WINDOW, { align: "right", maxLines: 6 }); + const optionsTextWidth = optionsText.displayWidth; + this.pokeballSelectContainer = this.scene.add.container((this.scene.game.canvas.width / 6) - 51 - Math.max(64, optionsTextWidth), -49); + this.pokeballSelectContainer.setVisible(false); + ui.add(this.pokeballSelectContainer); + + this.pokeballSelectBg = addWindow(this.scene, 0, 0, 50 + Math.max(64, optionsTextWidth), 32 + 480 * this.scale); + this.pokeballSelectBg.setOrigin(0, 1); + this.pokeballSelectContainer.add(this.pokeballSelectBg); + this.pokeballSelectContainer.add(optionsText); optionsText.setOrigin(0, 0); optionsText.setPositionRelative(this.pokeballSelectBg, 42, 9); - optionsText.setLineSpacing(12); - this.pokeballSelectContainer.add(optionsText); + optionsText.setLineSpacing(this.scale * 72); this.countsText = addTextObject(this.scene, 0, 0, "", TextStyle.WINDOW, { maxLines: 5 }); this.countsText.setPositionRelative(this.pokeballSelectBg, 18, 9); - this.countsText.setLineSpacing(12); + this.countsText.setLineSpacing(this.scale * 72); this.pokeballSelectContainer.add(this.countsText); this.setCursor(0); @@ -114,7 +118,8 @@ export default class BallUiHandler extends UiHandler { this.pokeballSelectContainer.add(this.cursorObj); } - this.cursorObj.setPositionRelative(this.pokeballSelectBg, 12, 17 + 16 * this.cursor); + this.cursorObj.setScale(this.scale * 6); + this.cursorObj.setPositionRelative(this.pokeballSelectBg, 12, 15 + (6 + this.cursor * 96) * this.scale); return ret; } diff --git a/src/ui/battle-flyout.ts b/src/ui/battle-flyout.ts index fdce9661793..6204fa2f928 100644 --- a/src/ui/battle-flyout.ts +++ b/src/ui/battle-flyout.ts @@ -147,7 +147,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container { const foundInfo = this.moveInfo.find(x => x?.move.id === moveUsedEvent.move.id); if (foundInfo) { - foundInfo.ppUsed = Math.min(foundInfo.ppUsed + moveUsedEvent.ppUsed, foundInfo.maxPp); + foundInfo.ppUsed = moveUsedEvent.ppUsed; } else { this.moveInfo.push({move: moveUsedEvent.move, maxPp: moveUsedEvent.move.pp, ppUsed: moveUsedEvent.ppUsed}); } diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index efd589aee9c..ab1802d59ea 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -12,8 +12,6 @@ import BattleFlyout from "./battle-flyout"; import { WindowVariant, addWindow } from "./ui-theme"; import i18next from "i18next"; -const battleStatOrder = [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD ]; - export default class BattleInfo extends Phaser.GameObjects.Container { private baseY: number; @@ -22,12 +20,12 @@ export default class BattleInfo extends Phaser.GameObjects.Container { private boss: boolean; private bossSegments: integer; private offset: boolean; - private lastName: string; + private lastName: string | null; private lastTeraType: Type; private lastStatus: StatusEffect; private lastHp: integer; private lastMaxHp: integer; - private lastHpFrame: string; + private lastHpFrame: string | null; private lastExp: integer; private lastLevelExp: integer; private lastLevel: integer; @@ -70,6 +68,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container { public flyoutMenu?: BattleFlyout; + private battleStatOrder: BattleStat[]; + private battleStatOrderPlayer = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD]; + private battleStatOrderEnemy = [BattleStat.HP, BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD]; + constructor(scene: Phaser.Scene, x: number, y: number, player: boolean) { super(scene, x, y); this.baseY = y; @@ -222,20 +224,44 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.statValuesContainer = this.scene.add.container(0, 0); this.statsContainer.add(this.statValuesContainer); - battleStatOrder.map((s, i) => { - const statX = i > 1 ? this.statNumbers[i - 2].x + this.statNumbers[i - 2].width + 4 : -this.statsBox.width + 8; - const statY = -this.statsBox.height / 2 + 4 + (i < battleStatOrder.length - 1 ? (i % 2 ? 10 : 0) : 5); + // this gives us a different starting location from the left of the label and padding between stats for a player vs enemy + // since the player won't have HP to show, it doesn't need to change from the current version + const startingX = this.player ? -this.statsBox.width + 8 : -this.statsBox.width + 5; + const paddingX = this.player ? 4 : 2; + const statOverflow = this.player ? 1 : 0; + this.battleStatOrder = this.player ? this.battleStatOrderPlayer : this.battleStatOrderEnemy; // this tells us whether or not to use the player or enemy battle stat order + + this.battleStatOrder.map((s, i) => { + // we do a check for i > statOverflow to see when the stat labels go onto the next column + // For enemies, we have HP (i=0) by itself then a new column, so we check for i > 0 + // For players, we don't have HP, so we start with i = 0 and i = 1 for our first column, and so need to check for i > 1 + const statX = i > statOverflow ? this.statNumbers[Math.max(i - 2, 0)].x + this.statNumbers[Math.max(i - 2, 0)].width + paddingX : startingX; // we have the Math.max(i - 2, 0) in there so for i===1 to not return a negative number; since this is now based on anything >0 instead of >1, we need to allow for i-2 < 0 + + const baseY = -this.statsBox.height / 2 + 4; // this is the baseline for the y-axis + let statY: number; // this will be the y-axis placement for the labels + if (this.battleStatOrder[i] === BattleStat.SPD || this.battleStatOrder[i] === BattleStat.HP) { + statY = baseY + 5; + } else { + statY = baseY + (!!(i % 2) === this.player ? 10 : 0); // we compare i % 2 against this.player to tell us where to place the label; because this.battleStatOrder for enemies has HP, this.battleStatOrder[1]=ATK, but for players this.battleStatOrder[0]=ATK, so this comparing i % 2 to this.player fixes this issue for us + } + const statLabel = this.scene.add.sprite(statX, statY, "pbinfo_stat", BattleStat[s]); statLabel.setName("icon_stat_label_" + i.toString()); statLabel.setOrigin(0, 0); statLabels.push(statLabel); this.statValuesContainer.add(statLabel); - const statNumber = this.scene.add.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", "3"); + const statNumber = this.scene.add.sprite(statX + statLabel.width, statY, "pbinfo_stat_numbers", this.battleStatOrder[i] !== BattleStat.HP ? "3" : "empty"); statNumber.setName("icon_stat_number_" + i.toString()); statNumber.setOrigin(0, 0); this.statNumbers.push(statNumber); this.statValuesContainer.add(statNumber); + + if (this.battleStatOrder[i] === BattleStat.HP) { + statLabel.setVisible(false); + statNumber.setVisible(false); + } + }); if (!this.player) { @@ -267,13 +293,17 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.add(this.effectivenessContainer); this.effectivenessText = addTextObject(this.scene, 5, 4.5, "", TextStyle.BATTLE_INFO); - this.effectivenessWindow = addWindow((this.scene as BattleScene), 0, 0, 0, 20, false, false, null, null, WindowVariant.XTHIN); + this.effectivenessWindow = addWindow((this.scene as BattleScene), 0, 0, 0, 20, undefined, false, undefined, undefined, WindowVariant.XTHIN); this.effectivenessContainer.add(this.effectivenessWindow); this.effectivenessContainer.add(this.effectivenessText); } } + getStatsValueContainer(): Phaser.GameObjects.Container { + return this.statValuesContainer; + } + initInfo(pokemon: Pokemon) { this.updateNameText(pokemon); const nameTextWidth = this.nameText.displayWidth; @@ -293,7 +323,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.teraIcon.setVisible(this.lastTeraType !== Type.UNKNOWN); this.teraIcon.on("pointerover", () => { if (this.lastTeraType !== Type.UNKNOWN) { - (this.scene as BattleScene).ui.showTooltip(null, `${Utils.toReadableString(Type[this.lastTeraType])} Terastallized`); + (this.scene as BattleScene).ui.showTooltip("", `${Utils.toReadableString(Type[this.lastTeraType])} Terastallized`); } }); this.teraIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); @@ -303,7 +333,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.splicedIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0), 2.5); this.splicedIcon.setVisible(isFusion); if (this.splicedIcon.visible) { - this.splicedIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies.getName(pokemon.fusionFormIndex)}`)); + this.splicedIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies?.getName(pokemon.fusionFormIndex)}`)); this.splicedIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -318,7 +348,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const shinyDescriptor = doubleShiny || baseVariant ? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}` : ""; - this.shinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`)); + this.shinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`)); this.shinyIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -330,7 +360,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { if (!this.player) { if (this.nameText.visible) { - this.nameText.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, i18next.t("battleInfo:generation", { generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`) }))); + this.nameText.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", i18next.t("battleInfo:generation", { generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`) }))); this.nameText.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -403,7 +433,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.statValuesContainer.setPosition(8, 7); } - const battleStats = battleStatOrder.map(() => 0); + const battleStats = this.battleStatOrder.map(() => 0); this.lastBattleStats = battleStats.join(""); this.updateBattleStats(battleStats); @@ -472,7 +502,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { updateBossSegmentDividers(pokemon: EnemyPokemon): void { while (this.hpBarSegmentDividers.length) { - this.hpBarSegmentDividers.pop().destroy(); + this.hpBarSegmentDividers.pop()?.destroy(); } if (this.boss && this.bossSegments > 1) { @@ -629,7 +659,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const battleStats = pokemon.summonData ? pokemon.summonData.battleStats - : battleStatOrder.map(() => 0); + : this.battleStatOrder.map(() => 0); const battleStatsStr = battleStats.join(""); if (this.lastBattleStats !== battleStatsStr) { @@ -758,8 +788,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } updateBattleStats(battleStats: integer[]): void { - battleStatOrder.map((s, i) => { - this.statNumbers[i].setFrame(battleStats[s].toString()); + this.battleStatOrder.map((s, i) => { + if (s !== BattleStat.HP) { + this.statNumbers[i].setFrame(battleStats[s].toString()); + } }); } @@ -786,7 +818,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } this.currentEffectiveness = effectiveness; - if (!(this.scene as BattleScene).typeHints || effectiveness === undefined || this.flyoutMenu.flyoutVisible) { + if (!(this.scene as BattleScene).typeHints || effectiveness === undefined || this.flyoutMenu?.flyoutVisible) { this.effectivenessContainer.setVisible(false); return; } diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index f2da553c6da..1c7dfb27630 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -147,19 +147,21 @@ export default class BattleMessageUiHandler extends MessageUiHandler { } } } + + return false; } clear() { super.clear(); } - showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { this.hideNameText(); super.showText(text, delay, callback, callbackDelay, prompt, promptDelay); } - showDialogue(text: string, name: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { - this.showNameText(name); + showDialogue(text: string, name?: string, delay?: integer | null, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + name && this.showNameText(name); super.showDialogue(text, name, delay, callback, callbackDelay, prompt, promptDelay); } @@ -180,7 +182,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { this.awaitingActionInput = true; this.onActionInput = () => { if (!showTotals) { - return this.promptLevelUpStats(partyMemberIndex, null, true).then(() => resolve()); + return this.promptLevelUpStats(partyMemberIndex, [], true).then(() => resolve()); } else { this.levelUpStatsContainer.setVisible(false); resolve(); @@ -194,24 +196,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { this.scene.executeWithSeedOffset(() => { let levelUpStatsValuesText = ""; const stats = Utils.getEnumValues(Stat); - let shownStats: Stat[] = []; - if (shownIvsCount < 6) { - const statsPool = stats.slice(0); - for (let i = 0; i < shownIvsCount; i++) { - let shownStat: Stat; - let highestIv = -1; - statsPool.map(s => { - if (ivs[s] > highestIv) { - shownStat = s as Stat; - highestIv = ivs[s]; - } - }); - shownStats.push(shownStat); - statsPool.splice(statsPool.indexOf(shownStat), 1); - } - } else { - shownStats = stats; - } + const shownStats = this.getTopIvs(ivs, shownIvsCount); for (const s of stats) { levelUpStatsValuesText += `${shownStats.indexOf(s) > -1 ? this.getIvDescriptor(ivs[s], s, pokemonId) : "???"}\n`; } @@ -227,35 +212,70 @@ export default class BattleMessageUiHandler extends MessageUiHandler { }); } + getTopIvs(ivs: integer[], shownIvsCount: integer): Stat[] { + const stats = Utils.getEnumValues(Stat); + let shownStats: Stat[] = []; + if (shownIvsCount < 6) { + const statsPool = stats.slice(0); + for (let i = 0; i < shownIvsCount; i++) { + let shownStat: Stat | null = null; + let highestIv = -1; + statsPool.map(s => { + if (ivs[s] > highestIv) { + shownStat = s as Stat; + highestIv = ivs[s]; + } + }); + if (shownStat) { + shownStats.push(shownStat); + statsPool.splice(statsPool.indexOf(shownStat), 1); + } + } + } else { + shownStats = stats; + } + return shownStats; + } + getIvDescriptor(value: integer, typeIv: integer, pokemonId: integer): string { - const starterSpecies = this.scene.getPokemonById(pokemonId).species.getRootSpeciesId(true); + const starterSpecies = this.scene.getPokemonById(pokemonId)!.species.getRootSpeciesId(); // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists const starterIvs: number[] = this.scene.gameData.dexData[starterSpecies].ivs; const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible // Function to wrap text in color based on comparison - const coloredText = (text: string, isBetter: boolean) => { - const textStyle: TextStyle = isBetter ? TextStyle.SUMMARY_GREEN : TextStyle.SUMMARY; + const coloredText = (text: string, isBetter: boolean, ivValue) => { + let textStyle: TextStyle; + if (isBetter) { + if (ivValue === 31) { + textStyle = TextStyle.PERFECT_IV; + } else { + textStyle = TextStyle.SUMMARY_GREEN; + } + } else { + textStyle = TextStyle.SUMMARY; + } + //const textStyle: TextStyle = isBetter ? TextStyle.SUMMARY_GREEN : TextStyle.SUMMARY; const color = getTextColor(textStyle, false, uiTheme); return `[color=${color}][shadow=${getTextColor(textStyle, true, uiTheme)}]${text}[/shadow][/color]`; }; if (value > 30) { - return coloredText(i18next.t("battleMessageUiHandler:ivBest"), value > starterIvs[typeIv]); + return coloredText(i18next.t("battleMessageUiHandler:ivBest"), value > starterIvs[typeIv], value); } if (value === 30) { - return coloredText(i18next.t("battleMessageUiHandler:ivFantastic"), value > starterIvs[typeIv]); + return coloredText(i18next.t("battleMessageUiHandler:ivFantastic"), value > starterIvs[typeIv], value); } if (value > 20) { - return coloredText(i18next.t("battleMessageUiHandler:ivVeryGood"), value > starterIvs[typeIv]); + return coloredText(i18next.t("battleMessageUiHandler:ivVeryGood"), value > starterIvs[typeIv], value); } if (value > 10) { - return coloredText(i18next.t("battleMessageUiHandler:ivPrettyGood"), value > starterIvs[typeIv]); + return coloredText(i18next.t("battleMessageUiHandler:ivPrettyGood"), value > starterIvs[typeIv], value); } if (value > 0) { - return coloredText(i18next.t("battleMessageUiHandler:ivDecent"), value > starterIvs[typeIv]); + return coloredText(i18next.t("battleMessageUiHandler:ivDecent"), value > starterIvs[typeIv], value); } - return coloredText(i18next.t("battleMessageUiHandler:ivNoGood"), value > starterIvs[typeIv]); + return coloredText(i18next.t("battleMessageUiHandler:ivNoGood"), value > starterIvs[typeIv], value); } showNameText(name: string): void { diff --git a/src/ui/bgm-bar.ts b/src/ui/bgm-bar.ts index 076eec26a8e..936ab91d8fa 100644 --- a/src/ui/bgm-bar.ts +++ b/src/ui/bgm-bar.ts @@ -24,7 +24,7 @@ export default class BgmBar extends Phaser.GameObjects.Container { this.defaultWidth = 230; this.defaultHeight = 100; - this.bg = this.scene.add.nineslice(-5, -5, "bgm_bar", null, this.defaultWidth, this.defaultHeight, 0, 0, 10, 10); + this.bg = this.scene.add.nineslice(-5, -5, "bgm_bar", undefined, this.defaultWidth, this.defaultHeight, 0, 0, 10, 10); this.bg.setOrigin(0, 0); this.add(this.bg); diff --git a/src/ui/candy-bar.ts b/src/ui/candy-bar.ts index 3a9f180dd59..5496ce9afce 100644 --- a/src/ui/candy-bar.ts +++ b/src/ui/candy-bar.ts @@ -11,8 +11,8 @@ export default class CandyBar extends Phaser.GameObjects.Container { private countText: Phaser.GameObjects.Text; private speciesId: Species; - private tween: Phaser.Tweens.Tween; - private autoHideTimer: NodeJS.Timeout; + private tween: Phaser.Tweens.Tween | null; + private autoHideTimer: NodeJS.Timeout | null; public shown: boolean; @@ -21,7 +21,7 @@ export default class CandyBar extends Phaser.GameObjects.Container { } setup(): void { - this.bg = this.scene.add.nineslice(0, 0, "party_exp_bar", null, 8, 18, 21, 5, 6, 4); + this.bg = this.scene.add.nineslice(0, 0, "party_exp_bar", undefined, 8, 18, 21, 5, 6, 4); this.bg.setOrigin(0, 0); this.add(this.bg); diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index 6dcc359ef31..12211fa71cc 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -30,11 +30,11 @@ export default class GameChallengesUiHandler extends UiHandler { private challengeLabels: Array<{ label: Phaser.GameObjects.Text, value: Phaser.GameObjects.Text }>; private monoTypeValue: Phaser.GameObjects.Sprite; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; private startCursor: Phaser.GameObjects.NineSlice; - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); } @@ -110,7 +110,7 @@ export default class GameChallengesUiHandler extends UiHandler { startText.setOrigin(0, 0); startText.setPositionRelative(startBg, 8, 4); - this.startCursor = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 18) - 10, 16, 1, 1, 1, 1); + this.startCursor = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 18) - 10, 16, 1, 1, 1, 1); this.startCursor.setName("9s-start-cursor"); this.startCursor.setOrigin(0, 0); this.startCursor.setPositionRelative(startBg, 4, 4); @@ -258,7 +258,7 @@ export default class GameChallengesUiHandler extends UiHandler { } else { this.scene.clearPhaseQueue(); this.scene.pushPhase(new TitlePhase(this.scene)); - this.scene.getCurrentPhase().end(); + this.scene.getCurrentPhase()?.end(); } success = true; } else if (button === Button.SUBMIT || button === Button.ACTION) { @@ -267,7 +267,7 @@ export default class GameChallengesUiHandler extends UiHandler { const totalMinDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getMinDifficulty(), 0); if (totalDifficulty >= totalMinDifficulty) { this.scene.unshiftPhase(new SelectStarterPhase(this.scene)); - this.scene.getCurrentPhase().end(); + this.scene.getCurrentPhase()?.end(); success = true; } else { success = false; @@ -354,7 +354,7 @@ export default class GameChallengesUiHandler extends UiHandler { let ret = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 9) - 10, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 9) - 10, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.valuesContainer.add(this.cursorObj); } diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index f083acd2f5b..11814a25240 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -17,7 +17,7 @@ export enum Command { export default class CommandUiHandler extends UiHandler { private commandsContainer: Phaser.GameObjects.Container; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; protected fieldIndex: integer = 0; protected cursor2: integer = 0; diff --git a/src/ui/daily-run-scoreboard.ts b/src/ui/daily-run-scoreboard.ts index 212d7b2a66c..b535a94d35c 100644 --- a/src/ui/daily-run-scoreboard.ts +++ b/src/ui/daily-run-scoreboard.ts @@ -59,14 +59,14 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { } setup() { - const titleWindow = addWindow(this.scene, 0, 0, 114, 18, false, false, null, null, WindowVariant.THIN); + const titleWindow = addWindow(this.scene, 0, 0, 114, 18, false, false, undefined, undefined, WindowVariant.THIN); this.add(titleWindow); this.titleLabel = addTextObject(this.scene, titleWindow.displayWidth / 2, titleWindow.displayHeight / 2, i18next.t("menu:loading"), TextStyle.WINDOW, { fontSize: "64px" }); this.titleLabel.setOrigin(0.5, 0.5); this.add(this.titleLabel); - const window = addWindow(this.scene, 0, 17, 114, 118, false, false, null, null, WindowVariant.THIN); + const window = addWindow(this.scene, 0, 17, 114, 118, false, false, undefined, undefined, WindowVariant.THIN); this.add(window); this.rankingsContainer = this.scene.add.container(6, 21); diff --git a/src/ui/dropdown.ts b/src/ui/dropdown.ts index 4338e11e0c6..f1723559b57 100644 --- a/src/ui/dropdown.ts +++ b/src/ui/dropdown.ts @@ -27,10 +27,10 @@ export class DropDownLabel { public text: string; public sprite?: Phaser.GameObjects.Sprite; - constructor(label: string, sprite?: Phaser.GameObjects.Sprite, state: DropDownState = DropDownState.ON) { + constructor(label: string, sprite?: Phaser.GameObjects.Sprite, state: DropDownState = DropDownState.OFF) { this.text = label || ""; this.sprite = sprite; - this.state = state || DropDownState.ON; + this.state = state; } } @@ -233,9 +233,9 @@ export class DropDownOption extends Phaser.GameObjects.Container { /** * @returns the x position to use for the current label depending on if it has a sprite or not */ - getCurrentLabelX(): number { + getCurrentLabelX(): number | undefined { if (this.labels[this.currentLabelIndex].sprite) { - return this.labels[this.currentLabelIndex].sprite.x; + return this.labels[this.currentLabelIndex].sprite?.x; } return this.text.x; } @@ -262,12 +262,13 @@ export class DropDown extends Phaser.GameObjects.Container { public options: DropDownOption[]; private window: Phaser.GameObjects.NineSlice; private cursorObj: Phaser.GameObjects.Image; - private dropDownType: DropDownType = DropDownType.MULTI; + public dropDownType: DropDownType = DropDownType.MULTI; public cursor: number = 0; + private lastCursor: number = -1; public defaultCursor: number = 0; private onChange: () => void; private lastDir: SortDirection = SortDirection.ASC; - private defaultValues: any[]; + private defaultSettings: any[]; constructor(scene: BattleScene, x: number, y: number, options: DropDownOption[], onChange: () => void, type: DropDownType = DropDownType.MULTI, optionSpacing: number = 2) { const windowPadding = 5; @@ -292,7 +293,7 @@ export class DropDown extends Phaser.GameObjects.Container { this.options.unshift(new DropDownOption(scene, "ALL", new DropDownLabel(i18next.t("filterBar:all"), undefined, this.checkForAllOn() ? DropDownState.ON : DropDownState.OFF))); } - this.defaultValues = this.getVals(); + this.defaultSettings = this.getSettings(); // Place ui elements in the correct spot options.forEach((option, index) => { @@ -312,7 +313,7 @@ export class DropDown extends Phaser.GameObjects.Container { } }); - this.window = addWindow(scene, 0, 0, optionWidth, options[options.length - 1].y + optionHeight + optionPaddingY, false, false, null, null, WindowVariant.XTHIN); + this.window = addWindow(scene, 0, 0, optionWidth, options[options.length - 1].y + optionHeight + optionPaddingY, false, false, undefined, undefined, WindowVariant.XTHIN); this.add(this.window); this.add(options); this.add(this.cursorObj); @@ -339,8 +340,8 @@ export class DropDown extends Phaser.GameObjects.Container { resetCursor(): boolean { // If we are an hybrid dropdown in "hover" mode, don't move the cursor back to 0 - if (this.dropDownType === DropDownType.HYBRID && this.checkForAllOff() && this.cursor > 0) { - return false; + if (this.dropDownType === DropDownType.HYBRID && this.checkForAllOff()) { + return this.setCursor(this.lastCursor); } return this.setCursor(this.defaultCursor); } @@ -361,6 +362,7 @@ export class DropDown extends Phaser.GameObjects.Container { this.cursorObj.setVisible(true); // If hydrid type, we need to update the filters when going up/down in the list if (this.dropDownType === DropDownType.HYBRID) { + this.lastCursor = cursor; this.onChange(); } } @@ -457,23 +459,43 @@ export class DropDown extends Phaser.GameObjects.Container { } } + /** + * Get the current selected settings dictionary for each option + * @returns an array of dictionaries with the current state of each option + * - the settings dictionary is like this { val: any, state: DropDownState, cursor: boolean, dir: SortDirection } + */ + private getSettings(): any[] { + const settings : any[] = []; + for (let i = 0; i < this.options.length; i++) { + settings.push({ val: this.options[i].val, state: this.options[i].state , cursor: (this.cursor === i), dir: this.options[i].dir }); + } + return settings; + } + /** * Check whether the values of all options are the same as the default ones * @returns true if they are the same, false otherwise */ public hasDefaultValues(): boolean { - const currentValues = this.getVals(); + const currentValues = this.getSettings(); + + const compareValues = (keys: string[]): boolean => { + return currentValues.length === this.defaultSettings.length && + currentValues.every((value, index) => + keys.every(key => value[key] === this.defaultSettings[index][key]) + ); + }; switch (this.dropDownType) { case DropDownType.MULTI: - case DropDownType.HYBRID: - return currentValues.length === this.defaultValues.length && currentValues.every((value, index) => value === this.defaultValues[index]); - case DropDownType.RADIAL: - return currentValues.every((value, index) => value["val"] === this.defaultValues[index]["val"] && value["state"] === this.defaultValues[index]["state"]); + return compareValues(["val", "state"]); + + case DropDownType.HYBRID: + return compareValues(["val", "state", "cursor"]); case DropDownType.SINGLE: - return currentValues[0]["dir"] === this.defaultValues[0]["dir"] && currentValues[0]["val"] === this.defaultValues[0]["val"]; + return compareValues(["val", "state", "dir"]); default: return false; @@ -484,46 +506,29 @@ export class DropDown extends Phaser.GameObjects.Container { * Set all values to their default state */ public resetToDefault(): void { - this.setCursor(this.defaultCursor); + if (this.defaultSettings.length > 0) { + this.setCursor(this.defaultCursor); + this.lastDir = SortDirection.ASC; - for (let i = 0; i < this.options.length; i++) { - const option = this.options[i]; - // reset values - switch (this.dropDownType) { - case DropDownType.HYBRID: - case DropDownType.MULTI: - if (this.defaultValues.includes(option.val)) { - option.setOptionState(DropDownState.ON); + for (let i = 0; i < this.options.length; i++) { + // reset values with the defaultValues + if (this.dropDownType === DropDownType.SINGLE) { + if (this.defaultSettings[i].state === DropDownState.OFF) { + this.options[i].setOptionState(DropDownState.OFF); + this.options[i].setDirection(SortDirection.ASC); + this.options[i].toggle.setVisible(false); + } else { + this.options[i].setOptionState(DropDownState.ON); + this.options[i].setDirection(SortDirection.ASC); + this.options[i].toggle.setVisible(true); + } } else { - option.setOptionState(DropDownState.OFF); - } - break; - case DropDownType.RADIAL: - const targetValue = this.defaultValues.find(value => value.val === option.val); - option.setOptionState(targetValue.state); - break; - case DropDownType.SINGLE: - if (option.val === this.defaultValues[0].val) { - if (option.state !== DropDownState.ON) { - this.toggleOptionState(i); - } - if (option.dir !== this.defaultValues[0].dir) { - this.toggleOptionState(i); + if (this.defaultSettings[i]) { + this.options[i].setOptionState(this.defaultSettings[i]["state"]); } } - break; } } - - // Select or unselect "ALL" button if applicable - if (this.dropDownType === DropDownType.MULTI || this.dropDownType === DropDownType.HYBRID) { - if (this.checkForAllOn()) { - this.options[0].setOptionState(DropDownState.ON); - } else { - this.options[0].setOptionState(DropDownState.OFF); - } - } - } /** @@ -565,7 +570,7 @@ export class DropDown extends Phaser.GameObjects.Container { const optionWidth = this.options[i].getWidth(); if (optionWidth > maxWidth) { maxWidth = optionWidth; - x = this.options[i].getCurrentLabelX(); + x = this.options[i].getCurrentLabelX() ?? 0; } } this.window.width = maxWidth + x - this.window.x + 6; diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index ae9d531a245..92f3aaea1a4 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -1,6 +1,6 @@ import BattleScene from "../battle-scene"; import { Mode } from "./ui"; -import { TextStyle, addTextObject, getEggTierTextTint } from "./text"; +import { TextStyle, addTextObject, getEggTierTextTint, getTextStyleOptions } from "./text"; import MessageUiHandler from "./message-ui-handler"; import * as Utils from "../utils"; import { Egg, getLegendaryGachaSpeciesForTimestamp, IEggOptions } from "../data/egg"; @@ -36,6 +36,8 @@ export default class EggGachaUiHandler extends MessageUiHandler { private transitionCancelled: boolean; private defaultText: string; + private scale: number = 0.1666666667; + constructor(scene: BattleScene) { super(scene, Mode.EGG_GACHA); @@ -50,6 +52,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { setup() { this.gachaCursor = 0; + this.scale = getTextStyleOptions(TextStyle.WINDOW, this.scene.uiTheme).scale; const ui = this.getUi(); @@ -57,7 +60,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.eggGachaContainer.setVisible(false); ui.add(this.eggGachaContainer); - const bg = this.scene.add.nineslice(0, 0, "default_bg", null, 320, 180, 0, 0, 16, 0); + const bg = this.scene.add.nineslice(0, 0, "default_bg", undefined, 320, 180, 0, 0, 16, 0); bg.setOrigin(0, 0); this.eggGachaContainer.add(bg); @@ -96,7 +99,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { const gachaInfoContainer = this.scene.add.container(160, 46); - const currentLanguage = i18next.resolvedLanguage; + const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? let gachaTextStyle = TextStyle.WINDOW_ALT; let gachaX = 4; let gachaY = 0; @@ -200,7 +203,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.eggGachaContainer.add(this.eggGachaOptionsContainer); - this.eggGachaOptionSelectBg = addWindow(this.scene, 0, 0, 96, 112); + this.eggGachaOptionSelectBg = addWindow(this.scene, 0, 0, 96, 16 + 576 * this.scale); this.eggGachaOptionSelectBg.setOrigin(1, 1); this.eggGachaOptionsContainer.add(this.eggGachaOptionSelectBg); @@ -214,7 +217,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { { multiplier: multiplierOne, description: `25 ${i18next.t("egg:pulls")}`, icon: getVoucherTypeIcon(VoucherType.GOLDEN) } ]; - const { resolvedLanguage } = i18next; + const resolvedLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? const pullOptionsText = pullOptions.map(option =>{ const desc = option.description.split(" "); if (desc[0].length < 2) { @@ -243,8 +246,8 @@ export default class EggGachaUiHandler extends MessageUiHandler { pullOptions.forEach((option, i) => { const icon = this.scene.add.sprite(0, 0, "items", option.icon); - icon.setScale(0.5); - icon.setPositionRelative(this.eggGachaOptionSelectBg, 20, 17 + i * 16); + icon.setScale(3 * this.scale); + icon.setPositionRelative(this.eggGachaOptionSelectBg, 20, 9 + (48 + i * 96) * this.scale); this.eggGachaOptionsContainer.add(icon); }); @@ -332,7 +335,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { return Utils.fixedInt(delay); } - pull(pullCount?: integer, count?: integer, eggs?: Egg[]): void { + pull(pullCount: integer = 0, count: integer = 0, eggs?: Egg[]): void { if (Overrides.EGG_GACHA_PULL_COUNT_OVERRIDE && !count) { pullCount = Overrides.EGG_GACHA_PULL_COUNT_OVERRIDE; } @@ -342,10 +345,10 @@ export default class EggGachaUiHandler extends MessageUiHandler { const doPull = () => { if (this.transitionCancelled) { - return this.showSummary(eggs); + return this.showSummary(eggs!); } - const egg = this.scene.add.sprite(127, 75, "egg", `egg_${eggs[count].getKey()}`); + const egg = this.scene.add.sprite(127, 75, "egg", `egg_${eggs![count].getKey()}`); egg.setScale(0.5); this.gachaContainers[this.gachaCursor].add(egg); @@ -388,7 +391,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { if (++count < pullCount) { this.pull(pullCount, count, eggs); } else { - this.showSummary(eggs); + this.showSummary(eggs!); } } }); @@ -581,7 +584,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { } showError(text: string): void { - this.showText(text, null, () => this.showText(this.defaultText), Utils.fixedInt(1500)); + this.showText(text, undefined, () => this.showText(this.defaultText), Utils.fixedInt(1500)); } setTransitioning(transitioning: boolean): void { @@ -735,7 +738,8 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.eggGachaOptionsContainer.add(this.cursorObj); } - this.cursorObj.setPositionRelative(this.eggGachaOptionSelectBg, 10, 17 + this.cursor * 16); + this.cursorObj.setScale(this.scale * 6); + this.cursorObj.setPositionRelative(this.eggGachaOptionSelectBg, 10, 9 + (48 + this.cursor * 96) * this.scale); return ret; } diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index 0e247da60ab..f567861e0b7 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -36,7 +36,7 @@ export default class EggHatchSceneHandler extends UiHandler { show(_args: any[]): boolean { super.show(_args); - this.getUi().showText(null, 0); + this.getUi().showText("", 0); this.scene.setModifiersVisible(false); diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/evolution-scene-handler.ts index 64d190c4d3f..ffbd06afde3 100644 --- a/src/ui/evolution-scene-handler.ts +++ b/src/ui/evolution-scene-handler.ts @@ -83,6 +83,8 @@ export default class EvolutionSceneHandler extends MessageUiHandler { } } } + + return false; } setCursor(_cursor: integer): boolean { diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index ed520512443..8279ab72a70 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -21,7 +21,7 @@ export default class FightUiHandler extends UiHandler { private powerText: Phaser.GameObjects.Text; private accuracyLabel: Phaser.GameObjects.Text; private accuracyText: Phaser.GameObjects.Text; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; private moveCategoryIcon: Phaser.GameObjects.Sprite; protected fieldIndex: integer = 0; @@ -176,7 +176,7 @@ export default class FightUiHandler extends UiHandler { const hasMove = cursor < moveset.length; if (hasMove) { - const pokemonMove = moveset[cursor]; + const pokemonMove = moveset[cursor]!; // TODO: is the bang correct? this.typeIcon.setTexture(`types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`, Type[pokemonMove.getMove().type].toLowerCase()).setScale(0.8); this.moveCategoryIcon.setTexture("categories", MoveCategory[pokemonMove.getMove().category].toLowerCase()).setScale(1.0); @@ -246,7 +246,7 @@ export default class FightUiHandler extends UiHandler { moveText.setName("text-empty-move"); if (moveIndex < moveset.length) { - const pokemonMove = moveset[moveIndex]; + const pokemonMove = moveset[moveIndex]!; // TODO is the bang correct? moveText.setText(pokemonMove.getName()); moveText.setName(pokemonMove.getName()); moveText.setColor(this.getMoveColor(pokemon, pokemonMove) ?? moveText.style.color); @@ -273,7 +273,7 @@ export default class FightUiHandler extends UiHandler { const moveColors = opponents.map((opponent) => { return opponent.getMoveEffectiveness(pokemon, pokemonMove); - }).sort((a, b) => b - a).map((effectiveness) => { + }).filter((eff) => !!eff).sort((a, b) => b - a).map((effectiveness) => { return getTypeDamageMultiplierColor(effectiveness, "offense"); }); diff --git a/src/ui/filter-bar.ts b/src/ui/filter-bar.ts index e163284bad3..31d7c562da2 100644 --- a/src/ui/filter-bar.ts +++ b/src/ui/filter-bar.ts @@ -1,5 +1,5 @@ import BattleScene from "#app/battle-scene.js"; -import { DropDown } from "./dropdown"; +import { DropDown, DropDownType } from "./dropdown"; import { StarterContainer } from "./starter-container"; import { addTextObject, getTextColor, TextStyle } from "./text"; import { UiTheme } from "#enums/ui-theme"; @@ -8,7 +8,7 @@ import { addWindow, WindowVariant } from "./ui-theme"; export enum DropDownColumn { GEN, TYPES, - DEX, + CAUGHT, UNLOCKS, MISC, SORT @@ -31,7 +31,7 @@ export class FilterBar extends Phaser.GameObjects.Container { this.width = width; this.height = height; - this.window = addWindow(scene, 0, 0, width, height, false, false, null, null, WindowVariant.THIN); + this.window = addWindow(scene, 0, 0, width, height, false, false, undefined, undefined, WindowVariant.THIN); this.add(this.window); this.cursorObj = this.scene.add.image(1, 1, "cursor"); @@ -120,11 +120,13 @@ export class FilterBar extends Phaser.GameObjects.Container { /** * Move the leftmost dropdown to the left of the FilterBar instead of below it */ - offsetFirstFilter(): void { - if (this.dropDowns[0]) { - this.dropDowns[0].autoSize(); - this.dropDowns[0].x -= this.dropDowns[0].getWidth(); - this.dropDowns[0].y = 0; + offsetHybridFilters(): void { + for (let i=0; i { const stat = displayStats[key] as DisplayStat; - const value = stat.sourceFunc(this.scene.gameData); + const value = stat.sourceFunc!(this.scene.gameData); // TODO: is this bang correct? this.statLabels[s].setText(!stat.hidden || isNaN(parseInt(value)) || parseInt(value) ? i18next.t(`gameStatsUiHandler:${stat.label_key}`) : "???"); this.statValues[s].setText(value); }); diff --git a/src/ui/loading-modal-ui-handler.ts b/src/ui/loading-modal-ui-handler.ts index c335b59de9f..d86f7afd3b6 100644 --- a/src/ui/loading-modal-ui-handler.ts +++ b/src/ui/loading-modal-ui-handler.ts @@ -5,7 +5,7 @@ import { addTextObject, TextStyle } from "./text"; import { Mode } from "./ui"; export default class LoadingModalUiHandler extends ModalUiHandler { - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); } diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 971bef6ea6b..9a787b40b1b 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -13,7 +13,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler { private externalPartyContainer: Phaser.GameObjects.Container; private externalPartyBg: Phaser.GameObjects.NineSlice; private externalPartyTitle: Phaser.GameObjects.Text; - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); } @@ -130,7 +130,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler { .then(response => { if (response.hasOwnProperty("token")) { Utils.setCookie(Utils.sessionIdKey, response.token); - originalLoginAction(); + originalLoginAction && originalLoginAction(); } else { onFail(response); } diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 4710c575ce9..0c99953c62d 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -1,5 +1,5 @@ import BattleScene, { bypassLogin } from "../battle-scene"; -import { TextStyle, addTextObject } from "./text"; +import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; import { Mode } from "./ui"; import * as Utils from "../utils"; import { addWindow } from "./ui-theme"; @@ -38,7 +38,7 @@ export default class MenuUiHandler extends MessageUiHandler { private menuBg: Phaser.GameObjects.NineSlice; protected optionSelectText: Phaser.GameObjects.Text; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; private excludedMenus: () => ConditionalMenu[]; private menuOptions: MenuOptions[]; @@ -46,10 +46,12 @@ export default class MenuUiHandler extends MessageUiHandler { protected manageDataConfig: OptionSelectConfig; protected communityConfig: OptionSelectConfig; + protected scale: number = 0.1666666667; + public bgmBar: BgmBar; - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.excludedMenus = () => [ @@ -67,7 +69,7 @@ export default class MenuUiHandler extends MessageUiHandler { setup(): void { const ui = this.getUi(); // wiki url directs based on languges available on wiki - const lang = i18next.resolvedLanguage.substring(0,2); + const lang = i18next.resolvedLanguage?.substring(0,2)!; // TODO: is this bang correct? if (["de", "fr", "ko", "zh"].includes(lang)) { wikiUrl = `https://wiki.pokerogue.net/${lang}:start`; } @@ -109,10 +111,16 @@ export default class MenuUiHandler extends MessageUiHandler { this.optionSelectText = addTextObject(this.scene, 0, 0, this.menuOptions.map(o => `${i18next.t(`menuUiHandler:${MenuOptions[o]}`)}`).join("\n"), TextStyle.WINDOW, { maxLines: this.menuOptions.length }); this.optionSelectText.setLineSpacing(12); - this.menuBg = addWindow(this.scene, (this.scene.game.canvas.width / 6) - (this.optionSelectText.displayWidth + 25), 0, this.optionSelectText.displayWidth + 23, (this.scene.game.canvas.height / 6) - 2); + this.scale = getTextStyleOptions(TextStyle.WINDOW, (this.scene as BattleScene).uiTheme).scale; + this.menuBg = addWindow(this.scene, + (this.scene.game.canvas.width / 6) - (this.optionSelectText.displayWidth + 25), + 0, + this.optionSelectText.displayWidth + 19+24*this.scale, + (this.scene.game.canvas.height / 6) - 2 + ); this.menuBg.setOrigin(0, 0); - this.optionSelectText.setPositionRelative(this.menuBg, 14, 6); + this.optionSelectText.setPositionRelative(this.menuBg, 10+24*this.scale, 6); this.menuContainer.add(this.menuBg); @@ -139,7 +147,7 @@ export default class MenuUiHandler extends MessageUiHandler { this.menuContainer.add(this.menuMessageBoxContainer); - const manageDataOptions = []; + const manageDataOptions: any[] = []; // TODO: proper type const confirmSlot = (message: string, slotFilter: (i: integer) => boolean, callback: (i: integer) => void) => { ui.revertMode(); @@ -151,7 +159,7 @@ export default class MenuUiHandler extends MessageUiHandler { handler: () => { callback(i); ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); return true; } }; @@ -159,7 +167,7 @@ export default class MenuUiHandler extends MessageUiHandler { label: i18next.t("menuUiHandler:cancel"), handler: () => { ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); return true; } }]), @@ -251,7 +259,7 @@ export default class MenuUiHandler extends MessageUiHandler { { label: "Wiki", handler: () => { - window.open(wikiUrl, "_blank").focus(); + window.open(wikiUrl, "_blank")?.focus(); return true; }, keepOpen: true @@ -259,7 +267,7 @@ export default class MenuUiHandler extends MessageUiHandler { { label: "Discord", handler: () => { - window.open(discordUrl, "_blank").focus(); + window.open(discordUrl, "_blank")?.focus(); return true; }, keepOpen: true @@ -267,7 +275,7 @@ export default class MenuUiHandler extends MessageUiHandler { { label: "GitHub", handler: () => { - window.open(githubUrl, "_blank").focus(); + window.open(githubUrl, "_blank")?.focus(); return true; }, keepOpen: true @@ -275,7 +283,7 @@ export default class MenuUiHandler extends MessageUiHandler { { label: "Reddit", handler: () => { - window.open(redditUrl, "_blank").focus(); + window.open(redditUrl, "_blank")?.focus(); return true; }, keepOpen: true @@ -378,7 +386,7 @@ export default class MenuUiHandler extends MessageUiHandler { if (!bypassLogin && !this.manageDataConfig.options.some(o => o.label === i18next.t("menuUiHandler:linkDiscord") || o.label === i18next.t("menuUiHandler:unlinkDiscord"))) { this.manageDataConfig.options.splice(this.manageDataConfig.options.length-1,0, { - label: loggedInUser.discordId === "" ? i18next.t("menuUiHandler:linkDiscord") : i18next.t("menuUiHandler:unlinkDiscord"), + label: loggedInUser?.discordId === "" ? i18next.t("menuUiHandler:linkDiscord") : i18next.t("menuUiHandler:unlinkDiscord"), handler: () => { if (loggedInUser?.discordId === "") { const token = Utils.getCookie(Utils.sessionIdKey); @@ -434,7 +442,7 @@ export default class MenuUiHandler extends MessageUiHandler { ui.showText(i18next.t("menuUiHandler:losingProgressionWarning"), null, () => { ui.setOverlayMode(Mode.CONFIRM, () => this.scene.gameData.saveAll(this.scene, true, true, true, true).then(() => this.scene.reset(true)), () => { ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); }, false, -98); }); } else { @@ -459,7 +467,7 @@ export default class MenuUiHandler extends MessageUiHandler { ui.showText(i18next.t("menuUiHandler:losingProgressionWarning"), null, () => { ui.setOverlayMode(Mode.CONFIRM, doLogout, () => { ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); }, false, -98); }); } else { @@ -517,7 +525,8 @@ export default class MenuUiHandler extends MessageUiHandler { this.menuContainer.add(this.cursorObj); } - this.cursorObj.setPositionRelative(this.menuBg, 7, 9 + this.cursor * 16); + this.cursorObj.setScale(this.scale * 6); + this.cursorObj.setPositionRelative(this.menuBg, 7, 6 + (18 + this.cursor * 96) * this.scale); return ret; } diff --git a/src/ui/message-ui-handler.ts b/src/ui/message-ui-handler.ts index 05c91ca1643..a78887e1581 100644 --- a/src/ui/message-ui-handler.ts +++ b/src/ui/message-ui-handler.ts @@ -4,28 +4,28 @@ import { Mode } from "./ui"; import * as Utils from "../utils"; export default abstract class MessageUiHandler extends AwaitableUiHandler { - protected textTimer: Phaser.Time.TimerEvent; - protected textCallbackTimer: Phaser.Time.TimerEvent; + protected textTimer: Phaser.Time.TimerEvent | null; + protected textCallbackTimer: Phaser.Time.TimerEvent | null; public pendingPrompt: boolean; public message: Phaser.GameObjects.Text; public prompt: Phaser.GameObjects.Sprite; - constructor(scene: BattleScene, mode: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.pendingPrompt = false; } - showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { this.showTextInternal(text, delay, callback, callbackDelay, prompt, promptDelay); } - showDialogue(text: string, name: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + showDialogue(text: string, name?: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { this.showTextInternal(text, delay, callback, callbackDelay, prompt, promptDelay); } - private showTextInternal(text: string, delay: integer, callback: Function, callbackDelay: integer, prompt: boolean, promptDelay: integer) { + private showTextInternal(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { if (delay === null || delay === undefined) { delay = 20; } @@ -33,7 +33,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { const delayMap = new Map(); const soundMap = new Map(); const actionPattern = /@(c|d|s)\{(.*?)\}/; - let actionMatch: RegExpExecArray; + let actionMatch: RegExpExecArray | null; while ((actionMatch = actionPattern.exec(text))) { switch (actionMatch[1]) { case "c": @@ -99,7 +99,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { this.textTimer = this.scene.time.addEvent({ delay: delay, callback: () => { - const charIndex = text.length - this.textTimer.repeatCount; + const charIndex = text.length - (this.textTimer?.repeatCount!); // TODO: is this bang correct? const charVar = charVarMap.get(charIndex); const charSound = soundMap.get(charIndex); const charDelay = delayMap.get(charIndex); @@ -111,7 +111,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { if (charSound) { this.scene.playSound(charSound); } - if (callback && !this.textTimer.repeatCount) { + if (callback && !this.textTimer?.repeatCount) { if (callbackDelay && !prompt) { this.textCallbackTimer = this.scene.time.delayedCall(callbackDelay, () => { if (this.textCallbackTimer) { @@ -126,11 +126,11 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { } }; if (charDelay) { - this.textTimer.paused = true; + this.textTimer!.paused = true; // TODO: is the bang correct? this.scene.tweens.addCounter({ duration: Utils.getFrameMs(charDelay), onComplete: () => { - this.textTimer.paused = false; + this.textTimer!.paused = false; // TODO: is the bang correct? advance(); } }); @@ -151,7 +151,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { } } - showPrompt(callback: Function, callbackDelay: integer) { + showPrompt(callback?: Function | null, callbackDelay?: integer | null) { const wrappedTextLines = this.message.runWordWrap(this.message.text).split(/\n/g); const textLinesCount = wrappedTextLines.length; const lastTextLine = wrappedTextLines[wrappedTextLines.length - 1]; diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index 85b20e423c3..cecdacc1eb9 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -16,7 +16,7 @@ export abstract class ModalUiHandler extends UiHandler { protected buttonContainers: Phaser.GameObjects.Container[]; protected buttonBgs: Phaser.GameObjects.NineSlice[]; - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.buttonContainers = []; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index b10bcbb18f6..016708027ca 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -35,7 +35,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { public options: ModifierOption[]; public shopOptionsRows: ModifierOption[][]; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; constructor(scene: BattleScene) { super(scene, Mode.CONFIRM); @@ -52,10 +52,13 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); - const [ , styleOptions, , , ] = getTextStyleOptions(TextStyle.PARTY, (this.scene as BattleScene).uiTheme); - context.font = styleOptions.fontSize + "px " + styleOptions.fontFamily; - this.transferButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:transfer")).width; - this.checkButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:checkTeam")).width; + const styleOptions = getTextStyleOptions(TextStyle.PARTY, (this.scene as BattleScene).uiTheme).styleOptions; + + if (context) { + context.font = styleOptions.fontSize + "px " + styleOptions.fontFamily; + this.transferButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:transfer")).width; + this.checkButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:checkTeam")).width; + } this.transferButtonContainer = this.scene.add.container((this.scene.game.canvas.width - this.checkButtonWidth) / 6 - 21, -64); this.transferButtonContainer.setName("transfer-btn"); @@ -394,7 +397,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { } const type = options[this.cursor].modifierTypeOption.type; - ui.showText(type.getDescription(this.scene)); + type && ui.showText(type.getDescription(this.scene)); if (type instanceof TmModifierType) { // prepare the move overlay to be shown with the toggle this.moveInfoOverlay.show(allMoves[type.moveId]); @@ -574,7 +577,7 @@ class ModifierOption extends Phaser.GameObjects.Container { this.add(this.itemContainer); const getItem = () => { - const item = this.scene.add.sprite(0, 0, "items", this.modifierTypeOption.type.iconImage); + const item = this.scene.add.sprite(0, 0, "items", this.modifierTypeOption.type?.iconImage); return item; }; @@ -587,10 +590,10 @@ class ModifierOption extends Phaser.GameObjects.Container { this.itemContainer.add(this.itemTint); } - this.itemText = addTextObject(this.scene, 0, 35, this.modifierTypeOption.type.name, TextStyle.PARTY, { align: "center" }); + this.itemText = addTextObject(this.scene, 0, 35, this.modifierTypeOption.type?.name!, TextStyle.PARTY, { align: "center" }); // TODO: is this bang correct? this.itemText.setOrigin(0.5, 0); this.itemText.setAlpha(0); - this.itemText.setTint(getModifierTierTextTint(this.modifierTypeOption.type.tier)); + this.itemText.setTint(this.modifierTypeOption.type?.tier ? getModifierTierTextTint(this.modifierTypeOption.type?.tier) : undefined); this.add(this.itemText); if (this.modifierTypeOption.cost) { @@ -722,7 +725,7 @@ class ModifierOption extends Phaser.GameObjects.Container { } getPbAtlasKey(tierOffset: integer = 0) { - return getPokeballAtlasKey((this.modifierTypeOption.type.tier + tierOffset) as integer as PokeballType); + return getPokeballAtlasKey((this.modifierTypeOption.type?.tier! + tierOffset) as integer as PokeballType); // TODO: is this bang correct? } updateCostText(): void { diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts index 3b947cb842d..ded19b01a12 100644 --- a/src/ui/move-info-overlay.ts +++ b/src/ui/move-info-overlay.ts @@ -30,7 +30,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem private move: Move; private desc: Phaser.GameObjects.Text; - private descScroll : Phaser.Tweens.Tween = null; + private descScroll : Phaser.Tweens.Tween | null = null; private val: Phaser.GameObjects.Container; private pp: Phaser.GameObjects.Text; @@ -131,7 +131,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem // show this component with infos for the specific move show(move : Move):boolean { if (!(this.scene as BattleScene).enableMoveInfo) { - return; // move infos have been disabled + return false; // move infos have been disabled // TODO:: is `false` correct? i used to be `undeefined` } this.move = move; this.pow.setText(move.power >= 0 ? move.power.toString() : "---"); diff --git a/src/ui/outdated-modal-ui-handler.ts b/src/ui/outdated-modal-ui-handler.ts index 1e737cf638b..fc4b93f9b8a 100644 --- a/src/ui/outdated-modal-ui-handler.ts +++ b/src/ui/outdated-modal-ui-handler.ts @@ -4,7 +4,7 @@ import { addTextObject, TextStyle } from "./text"; import { Mode } from "./ui"; export default class OutdatedModalUiHandler extends ModalUiHandler { - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); } diff --git a/src/ui/party-exp-bar.ts b/src/ui/party-exp-bar.ts index 506b8b5c825..d2521225375 100644 --- a/src/ui/party-exp-bar.ts +++ b/src/ui/party-exp-bar.ts @@ -7,7 +7,7 @@ export default class PartyExpBar extends Phaser.GameObjects.Container { private pokemonIcon: Phaser.GameObjects.Container; private expText: Phaser.GameObjects.Text; - private tween: Phaser.Tweens.Tween; + private tween: Phaser.Tweens.Tween | null; public shown: boolean; @@ -16,7 +16,7 @@ export default class PartyExpBar extends Phaser.GameObjects.Container { } setup(): void { - this.bg = this.scene.add.nineslice(0, 0, "party_exp_bar", null, 8, 18, 21, 5, 6, 4); + this.bg = this.scene.add.nineslice(0, 0, "party_exp_bar", undefined, 8, 18, 21, 5, 6, 4); this.bg.setOrigin(0, 0); this.add(this.bg); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 0b1e2a60d2f..0d02d05c60b 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,13 +1,13 @@ import { CommandPhase, SelectModifierPhase } from "../phases"; import BattleScene from "../battle-scene"; -import { PlayerPokemon, PokemonMove } from "../field/pokemon"; +import { MoveResult, PlayerPokemon, PokemonMove } from "../field/pokemon"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; import MessageUiHandler from "./message-ui-handler"; import { Mode } from "./ui"; import * as Utils from "../utils"; import { PokemonBaseStatModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, SwitchEffectTransferModifier } from "../modifier/modifier"; -import { allMoves } from "../data/move"; +import { allMoves, ForceSwitchOutAttr } from "../data/move"; import { getGenderColor, getGenderSymbol } from "../data/gender"; import { StatusEffect } from "../data/status-effect"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; @@ -25,18 +25,69 @@ import { getPokemonNameWithAffix } from "#app/messages.js"; const defaultMessage = i18next.t("partyUiHandler:choosePokemon"); +/** + * Indicates the reason why the party UI is being opened. + */ export enum PartyUiMode { + /** + * Indicates that the party UI is open because of a user-opted switch. This + * type of switch can be cancelled. + */ SWITCH, + /** + * Indicates that the party UI is open because of a faint or other forced + * switch (eg, move effect). This type of switch cannot be cancelled. + */ FAINT_SWITCH, + /** + * Indicates that the party UI is open because of a start-of-encounter optional + * switch. This type of switch can be cancelled. + */ POST_BATTLE_SWITCH, + /** + * Indicates that the party UI is open because of the move Revival Blessing. + * This selection cannot be cancelled. + */ REVIVAL_BLESSING, + /** + * Indicates that the party UI is open to select a mon to apply a modifier to. + * This type of selection can be cancelled. + */ MODIFIER, + /** + * Indicates that the party UI is open to select a mon to apply a move + * modifier to (such as an Ether or PP Up). This type of selection can be cancelled. + */ MOVE_MODIFIER, + /** + * Indicates that the party UI is open to select a mon to teach a TM. This + * type of selection can be cancelled. + */ TM_MODIFIER, + /** + * Indicates that the party UI is open to select a mon to remember a move. + * This type of selection can be cancelled. + */ REMEMBER_MOVE_MODIFIER, + /** + * Indicates that the party UI is open to transfer items between mons. This + * type of selection can be cancelled. + */ MODIFIER_TRANSFER, + /** + * Indicates that the party UI is open because of a DNA Splicer. This + * type of selection can be cancelled. + */ SPLICE, + /** + * Indicates that the party UI is open to release a party member. This + * type of selection can be cancelled. + */ RELEASE, + /** + * Indicates that the party UI is open to check the team. This + * type of selection can be cancelled. + */ CHECK } @@ -67,9 +118,9 @@ export enum PartyOption { export type PartySelectCallback = (cursor: integer, option: PartyOption) => void; export type PartyModifierTransferSelectCallback = (fromCursor: integer, index: integer, itemQuantity?: integer, toCursor?: integer) => void; export type PartyModifierSpliceSelectCallback = (fromCursor: integer, toCursor?: integer) => void; -export type PokemonSelectFilter = (pokemon: PlayerPokemon) => string; -export type PokemonModifierTransferSelectFilter = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => string; -export type PokemonMoveSelectFilter = (pokemonMove: PokemonMove) => string; +export type PokemonSelectFilter = (pokemon: PlayerPokemon) => string | null; +export type PokemonModifierTransferSelectFilter = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => string | null; +export type PokemonMoveSelectFilter = (pokemonMove: PokemonMove) => string | null; export default class PartyUiHandler extends MessageUiHandler { private partyUiMode: PartyUiMode; @@ -91,7 +142,7 @@ export default class PartyUiHandler extends MessageUiHandler { /** This is only public for test/ui/transfer-item.test.ts */ public optionsContainer: Phaser.GameObjects.Container; private optionsBg: Phaser.GameObjects.NineSlice; - private optionsCursorObj: Phaser.GameObjects.Image; + private optionsCursorObj: Phaser.GameObjects.Image | null; private options: integer[]; private transferMode: boolean; @@ -105,7 +156,7 @@ export default class PartyUiHandler extends MessageUiHandler { private transferAll: boolean; private lastCursor: integer = 0; - private selectCallback: PartySelectCallback | PartyModifierTransferSelectCallback; + private selectCallback: PartySelectCallback | PartyModifierTransferSelectCallback | null; private selectFilter: PokemonSelectFilter | PokemonModifierTransferSelectFilter; private moveSelectFilter: PokemonMoveSelectFilter; private tmMoveId: Moves; @@ -326,17 +377,17 @@ export default class PartyUiHandler extends MessageUiHandler { this.moveInfoOverlay.clear(); const filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); if (filterResult === null) { - this.selectCallback(this.cursor, option); + this.selectCallback?.(this.cursor, option); this.clearOptions(); } else { this.clearOptions(); - this.showText(filterResult as string, null, () => this.showText(null, 0), null, true); + this.showText(filterResult as string, undefined, () => this.showText(null, 0), undefined, true); } ui.playSelect(); return true; } else if ((option !== PartyOption.SUMMARY && option !== PartyOption.UNPAUSE_EVOLUTION && option !== PartyOption.UNSPLICE && option !== PartyOption.RELEASE && option !== PartyOption.CANCEL && option !== PartyOption.RENAME) || (option === PartyOption.RELEASE && this.partyUiMode === PartyUiMode.RELEASE)) { - let filterResult: string; + let filterResult: string | null; const getTransferrableItemsFromPokemon = (pokemon: PlayerPokemon) => this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferrable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[]; if (option !== PartyOption.TRANSFER && option !== PartyOption.SPLICE) { @@ -345,7 +396,7 @@ export default class PartyUiHandler extends MessageUiHandler { filterResult = this.FilterChallengeLegal(pokemon); } if (filterResult === null && this.partyUiMode === PartyUiMode.MOVE_MODIFIER) { - filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]); + filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]!); // TODO: is this bang correct? } } else { filterResult = (this.selectFilter as PokemonModifierTransferSelectFilter)(pokemon, getTransferrableItemsFromPokemon(this.scene.getParty()[this.transferCursor])[this.transferOptionCursor]); @@ -400,7 +451,7 @@ export default class PartyUiHandler extends MessageUiHandler { return true; } else { this.clearOptions(); - this.showText(filterResult as string, null, () => this.showText(null, 0), null, true); + this.showText(filterResult as string, undefined, () => this.showText(null, 0), undefined, true); } } else if (option === PartyOption.SUMMARY) { ui.playSelect(); @@ -410,18 +461,18 @@ export default class PartyUiHandler extends MessageUiHandler { this.clearOptions(); ui.playSelect(); pokemon.pauseEvolutions = false; - this.showText(i18next.t("partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon, false) }), null, () => this.showText(null, 0), null, true); + this.showText(i18next.t("partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon, false) }), undefined, () => this.showText(null, 0), null, true); } else if (option === PartyOption.UNSPLICE) { this.clearOptions(); ui.playSelect(); - this.showText(i18next.t("partyUiHandler:unspliceConfirmation", { fusionName: pokemon.fusionSpecies.name, pokemonName: pokemon.getName() }), null, () => { + this.showText(i18next.t("partyUiHandler:unspliceConfirmation", { fusionName: pokemon.fusionSpecies?.name, pokemonName: pokemon.getName() }), null, () => { ui.setModeWithoutClear(Mode.CONFIRM, () => { const fusionName = pokemon.getName(false); pokemon.unfuse().then(() => { this.clearPartySlots(); this.populatePartySlots(); ui.setMode(Mode.PARTY); - this.showText(i18next.t("partyUiHandler:wasReverted", { fusionName: fusionName, pokemonName: pokemon.getName() }), null, () => { + this.showText(i18next.t("partyUiHandler:wasReverted", { fusionName: fusionName, pokemonName: pokemon.getName() }), undefined, () => { ui.setMode(Mode.PARTY); this.showText(null, 0); }, null, true); @@ -681,7 +732,7 @@ export default class PartyUiHandler extends MessageUiHandler { return changed; } - showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + showText(text: string | null, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean, promptDelay?: integer) { if (text === null) { text = defaultMessage; } @@ -741,7 +792,7 @@ export default class PartyUiHandler extends MessageUiHandler { const learnableLevelMoves = this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER ? pokemon.getLearnableLevelMoves() - : null; + : []; if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER && learnableLevelMoves?.length) { // show the move overlay with info for the first move @@ -751,7 +802,7 @@ export default class PartyUiHandler extends MessageUiHandler { const itemModifiers = this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER ? this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferrable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[] - : null; + : []; if (this.options.length) { this.options.splice(0, this.options.length); @@ -759,7 +810,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.eraseOptionsCursor(); } - let formChangeItemModifiers: PokemonFormChangeItemModifier[]; + let formChangeItemModifiers: PokemonFormChangeItemModifier[] | undefined; if (this.partyUiMode !== PartyUiMode.MOVE_MODIFIER && this.partyUiMode !== PartyUiMode.REMEMBER_MOVE_MODIFIER && (this.transferMode || this.partyUiMode !== PartyUiMode.MODIFIER_TRANSFER)) { switch (this.partyUiMode) { @@ -767,10 +818,21 @@ export default class PartyUiHandler extends MessageUiHandler { case PartyUiMode.FAINT_SWITCH: case PartyUiMode.POST_BATTLE_SWITCH: if (this.cursor >= this.scene.currentBattle.getBattlerCount()) { - this.options.push(PartyOption.SEND_OUT); - if (this.partyUiMode !== PartyUiMode.FAINT_SWITCH - && this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier - && (m as SwitchEffectTransferModifier).pokemonId === this.scene.getPlayerField()[this.fieldIndex].id)) { + const allowBatonModifierSwitch = + this.partyUiMode !== PartyUiMode.FAINT_SWITCH + && this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier + && (m as SwitchEffectTransferModifier).pokemonId === this.scene.getPlayerField()[this.fieldIndex].id); + + const moveHistory = this.scene.getPlayerField()[this.fieldIndex].getMoveHistory(); + const isBatonPassMove = this.partyUiMode === PartyUiMode.FAINT_SWITCH && moveHistory.length && allMoves[moveHistory[moveHistory.length - 1].move].getAttrs(ForceSwitchOutAttr)[0]?.isBatonPass() && moveHistory[moveHistory.length - 1].result === MoveResult.SUCCESS; + + // isBatonPassMove and allowBatonModifierSwitch shouldn't ever be true + // at the same time, because they both explicitly check for a mutually + // exclusive partyUiMode. But better safe than sorry. + this.options.push(isBatonPassMove && !allowBatonModifierSwitch ? PartyOption.PASS_BATON : PartyOption.SEND_OUT); + if (allowBatonModifierSwitch && !isBatonPassMove) { + // the BATON modifier gives an extra switch option for + // pokemon-command switches, allowing buffs to be optionally passed this.options.push(PartyOption.PASS_BATON); } } @@ -889,7 +951,7 @@ export default class PartyUiHandler extends MessageUiHandler { case PartyOption.MOVE_2: case PartyOption.MOVE_3: case PartyOption.MOVE_4: - const move = pokemon.moveset[option - PartyOption.MOVE_1]; + const move = pokemon.moveset[option - PartyOption.MOVE_1]!; // TODO: is the bang correct? if (this.showMovePp) { const maxPP = move.getMovePp(); const currPP = maxPP - move.ppUsed; @@ -998,7 +1060,7 @@ export default class PartyUiHandler extends MessageUiHandler { if (this.partyUiMode === PartyUiMode.RELEASE) { const selectCallback = this.selectCallback; this.selectCallback = null; - selectCallback(this.cursor, PartyOption.RELEASE); + selectCallback && selectCallback(this.cursor, PartyOption.RELEASE); } this.showText(null, 0); }, null, true); diff --git a/src/ui/pokeball-tray.ts b/src/ui/pokeball-tray.ts index 00a8cdadc97..cf3b24c4d11 100644 --- a/src/ui/pokeball-tray.ts +++ b/src/ui/pokeball-tray.ts @@ -15,7 +15,7 @@ export default class PokeballTray extends Phaser.GameObjects.Container { } setup(): void { - this.bg = this.scene.add.nineslice(0, 0, `pb_tray_overlay_${this.player ? "player" : "enemy"}`, null, 104, 4, 48, 8, 0, 0); + this.bg = this.scene.add.nineslice(0, 0, `pb_tray_overlay_${this.player ? "player" : "enemy"}`, undefined, 104, 4, 48, 8, 0, 0); this.bg.setOrigin(this.player ? 1 : 0, 0); this.add(this.bg); diff --git a/src/ui/pokemon-icon-anim-handler.ts b/src/ui/pokemon-icon-anim-handler.ts index 99fe39c4184..d6796d5cb5d 100644 --- a/src/ui/pokemon-icon-anim-handler.ts +++ b/src/ui/pokemon-icon-anim-handler.ts @@ -21,7 +21,9 @@ export default class PokemonIconAnimHandler { const value = tween.getValue(); this.toggled = !!value; for (const i of this.icons.keys()) { - i.y += this.getModeYDelta(this.icons.get(i)) * (this.toggled ? 1 : -1); + const icon = this.icons.get(i); + const delta = icon ? this.getModeYDelta(icon) : 0; + i.y += delta * (this.toggled ? 1 : -1); } }; scene.tweens.addCounter({ @@ -56,7 +58,7 @@ export default class PokemonIconAnimHandler { } if (this.toggled) { const lastYDelta = this.icons.has(i) - ? this.icons.get(i) + ? this.icons.get(i)! : 0; const yDelta = this.getModeYDelta(mode); i.y += yDelta + lastYDelta; @@ -71,7 +73,9 @@ export default class PokemonIconAnimHandler { } for (const i of icons) { if (this.toggled) { - i.y -= this.getModeYDelta(this.icons.get(i)); + const icon = this.icons.get(i); + const delta = icon ? this.getModeYDelta(icon) : 0; + i.y -= delta; } this.icons.delete(i); } @@ -80,7 +84,9 @@ export default class PokemonIconAnimHandler { removeAll(): void { for (const i of this.icons.keys()) { if (this.toggled) { - i.y -= this.getModeYDelta(this.icons.get(i)); + const icon = this.icons.get(i); + const delta = icon ? this.getModeYDelta(icon) : 0; + i.y -= delta; } this.icons.delete(i); } diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index b2ee5a9164a..edb85ecff7a 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -80,8 +80,8 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { setup(): void { this.setName("pkmn-info"); - const currentLanguage = i18next.resolvedLanguage; - const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)); + const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? + const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct? const textSettings = languageSettings[langSettingKey]; const infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132); infoBg.setOrigin(0.5, 0.5); @@ -243,7 +243,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonFormText.setText(formName.length > this.numCharsBeforeCutoff ? formName.substring(0, this.numCharsBeforeCutoff - 3) + "..." : formName); if (formName.length > this.numCharsBeforeCutoff) { this.pokemonFormText.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.pokemonFormText.width, this.pokemonFormText.height), Phaser.Geom.Rectangle.Contains); - this.pokemonFormText.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, pokemon.species.forms?.[pokemon.formIndex]?.formName, true)); + this.pokemonFormText.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", pokemon.species.forms?.[pokemon.formIndex]?.formName, true)); this.pokemonFormText.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } else { this.pokemonFormText.disableInteractive(); @@ -302,7 +302,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { const shinyDescriptor = doubleShiny || baseVariant ? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}` : ""; - this.pokemonShinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); + this.pokemonShinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); this.pokemonShinyIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); const newShiny = BigInt(1 << (pokemon.shiny ? 1 : 0)); @@ -324,11 +324,11 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { } const starterSpeciesId = pokemon.species.getRootSpeciesId(); - const originalIvs: integer[] = this.scene.gameData.dexData[starterSpeciesId].caughtAttr + const originalIvs: integer[] | null = this.scene.gameData.dexData[starterSpeciesId].caughtAttr ? this.scene.gameData.dexData[starterSpeciesId].ivs : null; - this.statsContainer.updateIvs(pokemon.ivs, originalIvs); + this.statsContainer.updateIvs(pokemon.ivs, originalIvs!); // TODO: is this bang correct? this.scene.tweens.add({ targets: this, @@ -352,7 +352,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { } for (let m = 0; m < 4; m++) { - const move = m < pokemon.moveset.length ? pokemon.moveset[m].getMove() : null; + const move = m < pokemon.moveset.length && pokemon.moveset[m] ? pokemon.moveset[m]!.getMove() : null; this.pokemonMoveBgs[m].setFrame(Type[move ? move.type : Type.UNKNOWN].toString().toLowerCase()); this.pokemonMoveLabels[m].setText(move ? move.name : "-"); this.pokemonMovesContainers[m].setVisible(!!move); diff --git a/src/ui/registration-form-ui-handler.ts b/src/ui/registration-form-ui-handler.ts index d5e7f239378..733aab79b05 100644 --- a/src/ui/registration-form-ui-handler.ts +++ b/src/ui/registration-form-ui-handler.ts @@ -92,7 +92,7 @@ export default class RegistrationFormUiHandler extends FormModalUiHandler { .then(response => { if (response.hasOwnProperty("token")) { Utils.setCookie(Utils.sessionIdKey, response.token); - originalRegistrationAction(); + originalRegistrationAction && originalRegistrationAction(); } else { onFail(response); } diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 8a81ac4858d..e6ab0d3b3c3 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -29,11 +29,11 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { private sessionSlots: SessionSlot[]; private uiMode: SaveSlotUiMode; - private saveSlotSelectCallback: SaveSlotSelectCallback; + private saveSlotSelectCallback: SaveSlotSelectCallback | null; private scrollCursor: integer = 0; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; private sessionSlotsContainerInitialY: number; @@ -106,16 +106,16 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { switch (this.uiMode) { case SaveSlotUiMode.LOAD: this.saveSlotSelectCallback = null; - originalCallback(cursor); + originalCallback && originalCallback(cursor); break; case SaveSlotUiMode.SAVE: const saveAndCallback = () => { const originalCallback = this.saveSlotSelectCallback; this.saveSlotSelectCallback = null; ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); ui.setMode(Mode.MESSAGE); - originalCallback(cursor); + originalCallback && originalCallback(cursor); }; if (this.sessionSlots[cursor].hasData) { ui.showText(i18next.t("saveSlotSelectUiHandler:overwriteData"), null, () => { @@ -129,7 +129,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { }); }, () => { ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); }, false, 0, 19, 2000); }); } else if (this.sessionSlots[cursor].hasData === false) { @@ -143,7 +143,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { } } else { this.saveSlotSelectCallback = null; - originalCallback(-1); + originalCallback && originalCallback(-1); success = true; } } else { @@ -202,7 +202,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { const changed = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", null, 296, 44, 6, 6, 6, 6); + this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", undefined, 296, 44, 6, 6, 6, 6); this.cursorObj.setOrigin(0, 0); this.sessionSlotsContainer.add(this.cursorObj); } @@ -292,7 +292,7 @@ class SessionSlot extends Phaser.GameObjects.Container { const icon = this.scene.addPokemonIcon(pokemon, 0, 0, 0, 0); const text = addTextObject(this.scene, 32, 20, `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(pokemon.level, 1000)}`, TextStyle.PARTY, { fontSize: "54px", color: "#f8f8f8" }); - text.setShadow(0, 0, null); + text.setShadow(0, 0, undefined); text.setStroke("#424242", 14); text.setOrigin(1, 0); @@ -316,9 +316,11 @@ class SessionSlot extends Phaser.GameObjects.Container { if (modifier instanceof PokemonHeldItemModifier) { continue; } - const icon = modifier.getIcon(this.scene, false); - icon.setPosition(24 * visibleModifierIndex, 0); - modifierIconsContainer.add(icon); + const icon = modifier?.getIcon(this.scene, false); + if (icon) { + icon.setPosition(24 * visibleModifierIndex, 0); + modifierIconsContainer.add(icon); + } if (++visibleModifierIndex === 12) { break; } diff --git a/src/ui/session-reload-modal-ui-handler.ts b/src/ui/session-reload-modal-ui-handler.ts index 5313de58580..147634b19d2 100644 --- a/src/ui/session-reload-modal-ui-handler.ts +++ b/src/ui/session-reload-modal-ui-handler.ts @@ -4,7 +4,7 @@ import { addTextObject, TextStyle } from "./text"; import { Mode } from "./ui"; export default class SessionReloadModalUiHandler extends ModalUiHandler { - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); } diff --git a/src/ui/settings/abstract-binding-ui-handler.ts b/src/ui/settings/abstract-binding-ui-handler.ts index 809c4ffa2f8..5e6dd80c6d6 100644 --- a/src/ui/settings/abstract-binding-ui-handler.ts +++ b/src/ui/settings/abstract-binding-ui-handler.ts @@ -7,6 +7,8 @@ import {Button} from "#enums/buttons"; import {NavigationManager} from "#app/ui/settings/navigationMenu"; import i18next from "i18next"; +type CancelFn = (succes?: boolean) => boolean; + /** * Abstract class for handling UI elements related to button bindings. */ @@ -35,7 +37,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { protected targetButtonIcon: Phaser.GameObjects.Sprite; // Function to call on cancel or completion of binding. - protected cancelFn: (boolean?) => boolean; + protected cancelFn: CancelFn | null; abstract swapAction(): boolean; protected timeLeftAutoClose: number = 5; @@ -50,7 +52,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { * @param scene - The BattleScene instance. * @param mode - The UI mode. */ - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); } @@ -107,7 +109,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { if (this.timeLeftAutoClose >= 0) { this.manageAutoCloseTimer(); } else { - this.cancelFn(); + this.cancelFn && this.cancelFn(); } }, 1000); } @@ -163,7 +165,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { */ processInput(button: Button): boolean { if (this.buttonPressed === null) { - return; + return false; // TODO: is false correct as default? (previously was `undefined`) } const ui = this.getUi(); let success = false; @@ -177,11 +179,11 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { case Button.ACTION: // Process actions based on current cursor position. if (this.cursor === 0) { - this.cancelFn(); + this.cancelFn && this.cancelFn(); } else { success = this.swapAction(); NavigationManager.getInstance().updateIcons(); - this.cancelFn(success); + this.cancelFn && this.cancelFn(success); } break; } @@ -242,7 +244,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { * @param assignedButtonIcon - The icon of the button that is assigned. * @param type - The type of button press. */ - onInputDown(buttonIcon: string, assignedButtonIcon: string, type: string): void { + onInputDown(buttonIcon: string, assignedButtonIcon: string | null, type: string): void { clearTimeout(this.countdownTimer); this.timerText.setText(""); this.newButtonIcon.setTexture(type); diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts index 9bf0cb40975..f8dab1bf7cc 100644 --- a/src/ui/settings/abstract-control-settings-ui-handler.ts +++ b/src/ui/settings/abstract-control-settings-ui-handler.ts @@ -33,7 +33,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler protected scrollCursor: integer; protected optionCursors: integer[]; - protected cursorObj: Phaser.GameObjects.NineSlice; + protected cursorObj: Phaser.GameObjects.NineSlice | null; protected optionsBg: Phaser.GameObjects.NineSlice; protected actionsBg: Phaser.GameObjects.NineSlice; @@ -73,14 +73,14 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler * @param scene - The BattleScene instance. * @param mode - The UI mode. */ - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.rowsToDisplay = 8; } getLocalStorageSetting(): object { // Retrieve the settings from local storage or use an empty object if none exist. - const settings: object = localStorage.hasOwnProperty(this.localStoragePropertyName) ? JSON.parse(localStorage.getItem(this.localStoragePropertyName)) : {}; + const settings: object = localStorage.hasOwnProperty(this.localStoragePropertyName) ? JSON.parse(localStorage.getItem(this.localStoragePropertyName)!) : {}; // TODO: is this bang correct? return settings; } @@ -442,7 +442,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler switch (button) { case Button.ACTION: if (!this.optionCursors || !this.optionValueLabels) { - return; + return false; // TODO: is false correct as default? (previously was `undefined`) } if (this.settingBlacklisted.includes(setting) || !setting.includes("BUTTON_")) { success = false; @@ -490,7 +490,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler break; case Button.LEFT: // Move selection left within the current option set. if (!this.optionCursors || !this.optionValueLabels) { - return; + return false; // TODO: is false correct as default? (previously was `undefined`) } if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) { success = false; @@ -500,7 +500,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler break; case Button.RIGHT: // Move selection right within the current option set. if (!this.optionCursors || !this.optionValueLabels) { - return; + return false; // TODO: is false correct as default? (previously was `undefined`) } if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) { success = false; @@ -526,7 +526,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler resetScroll() { this.cursorObj?.destroy(); this.cursorObj = null; - this.cursor = null; + this.cursor = 0; this.setCursor(0); this.setScrollCursor(0); this.updateSettingsScroll(); @@ -547,7 +547,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler // Check if the cursor object exists, if not, create it. if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); // Set the origin to the top-left corner. this.optionsContainer.add(this.cursorObj); // Add the cursor to the options container. } diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 3ffd32cf842..6a074c83e90 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -30,7 +30,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { protected navigationIcons: InputsIcons; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; private reloadSettings: Array; private reloadRequired: boolean; @@ -40,7 +40,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { protected settings: Array; protected localStorageKey: string; - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.reloadRequired = false; @@ -105,7 +105,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { this.optionsContainer.add(this.settingLabels[s]); this.optionValueLabels.push(setting.options.map((option, o) => { - const valueLabel = addTextObject(this.scene, 0, 0, option.label, setting.default === o ? TextStyle.SETTINGS_SELECTED : TextStyle.WINDOW); + const valueLabel = addTextObject(this.scene, 0, 0, option.label, setting.default === o ? TextStyle.SETTINGS_SELECTED : TextStyle.SETTINGS_VALUE); valueLabel.setOrigin(0, 0); this.optionsContainer.add(valueLabel); @@ -180,7 +180,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { super.show(args); this.updateBindings(); - const settings: object = localStorage.hasOwnProperty(this.localStorageKey) ? JSON.parse(localStorage.getItem(this.localStorageKey)) : {}; + const settings: object = localStorage.hasOwnProperty(this.localStorageKey) ? JSON.parse(localStorage.getItem(this.localStorageKey)!) : {}; // TODO: is this bang correct? this.settings.forEach((setting, s) => this.setOptionCursor(s, settings.hasOwnProperty(setting.key) ? settings[setting.key] : this.settings[s].default)); @@ -285,7 +285,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { const ret = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.optionsContainer.add(this.cursorObj); } @@ -314,8 +314,8 @@ export default class AbstractSettingsUiHandler extends UiHandler { const lastCursor = this.optionCursors[settingIndex]; const lastValueLabel = this.optionValueLabels[settingIndex][lastCursor]; - lastValueLabel.setColor(this.getTextColor(TextStyle.WINDOW)); - lastValueLabel.setShadowColor(this.getTextColor(TextStyle.WINDOW, true)); + lastValueLabel.setColor(this.getTextColor(TextStyle.SETTINGS_VALUE)); + lastValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_VALUE, true)); this.optionCursors[settingIndex] = cursor; diff --git a/src/ui/settings/gamepad-binding-ui-handler.ts b/src/ui/settings/gamepad-binding-ui-handler.ts index 70291da15a2..4e5b4576b85 100644 --- a/src/ui/settings/gamepad-binding-ui-handler.ts +++ b/src/ui/settings/gamepad-binding-ui-handler.ts @@ -8,9 +8,9 @@ import {addTextObject, TextStyle} from "#app/ui/text"; export default class GamepadBindingUiHandler extends AbstractBindingUiHandler { - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); - this.scene.input.gamepad.on("down", this.gamepadButtonDown, this); + this.scene.input.gamepad?.on("down", this.gamepadButtonDown, this); } setup() { super.setup(); diff --git a/src/ui/settings/keyboard-binding-ui-handler.ts b/src/ui/settings/keyboard-binding-ui-handler.ts index 2a92bb0fa6c..043c5fba6a5 100644 --- a/src/ui/settings/keyboard-binding-ui-handler.ts +++ b/src/ui/settings/keyboard-binding-ui-handler.ts @@ -8,11 +8,12 @@ import {addTextObject, TextStyle} from "#app/ui/text"; export default class KeyboardBindingUiHandler extends AbstractBindingUiHandler { - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); // Listen to gamepad button down events to initiate binding. - scene.input.keyboard.on("keydown", this.onKeyDown, this); + scene.input.keyboard?.on("keydown", this.onKeyDown, this); } + setup() { super.setup(); diff --git a/src/ui/settings/settings-audio-ui-handler.ts b/src/ui/settings/settings-audio-ui-handler.ts index 10313ed388c..e86c0d26546 100644 --- a/src/ui/settings/settings-audio-ui-handler.ts +++ b/src/ui/settings/settings-audio-ui-handler.ts @@ -11,7 +11,7 @@ export default class SettingsAudioUiHandler extends AbstractSettingsUiHandler { * @param scene - The BattleScene instance. * @param mode - The UI mode, optional. */ - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.title = "Audio"; this.settings = Setting.filter(s => s.type === SettingType.AUDIO); diff --git a/src/ui/settings/settings-display-ui-handler.ts b/src/ui/settings/settings-display-ui-handler.ts index f0e84f29e00..3f0c9edd26f 100644 --- a/src/ui/settings/settings-display-ui-handler.ts +++ b/src/ui/settings/settings-display-ui-handler.ts @@ -11,7 +11,7 @@ export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler * @param scene - The BattleScene instance. * @param mode - The UI mode, optional. */ - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.title = "Display"; this.settings = Setting.filter(s => s.type === SettingType.DISPLAY); @@ -79,6 +79,18 @@ export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler label: "한국어", }; break; + case "ja": + this.settings[languageIndex].options[0] = { + value: "日本語", + label: "日本語", + }; + break; + case "ca-ES": + this.settings[languageIndex].options[0] = { + value: "Català", + label: "Català", + }; + break; default: this.settings[languageIndex].options[0] = { value: "English", diff --git a/src/ui/settings/settings-gamepad-ui-handler.ts b/src/ui/settings/settings-gamepad-ui-handler.ts index f86e8088ec6..902d7eff34e 100644 --- a/src/ui/settings/settings-gamepad-ui-handler.ts +++ b/src/ui/settings/settings-gamepad-ui-handler.ts @@ -31,7 +31,7 @@ export default class SettingsGamepadUiHandler extends AbstractControlSettingsUiH * @param scene - The BattleScene instance. * @param mode - The UI mode, optional. */ - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.titleSelected = "Gamepad"; this.setting = SettingGamepad; diff --git a/src/ui/settings/settings-keyboard-ui-handler.ts b/src/ui/settings/settings-keyboard-ui-handler.ts index 5c4444991ad..dc6de8c90dc 100644 --- a/src/ui/settings/settings-keyboard-ui-handler.ts +++ b/src/ui/settings/settings-keyboard-ui-handler.ts @@ -29,7 +29,7 @@ export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUi * @param scene - The BattleScene instance. * @param mode - The UI mode, optional. */ - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.titleSelected = "Keyboard"; this.setting = SettingKeyboard; @@ -42,10 +42,10 @@ export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUi this.settingBlacklisted = settingKeyboardBlackList; this.device = Device.KEYBOARD; - const deleteEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DELETE); - const restoreDefaultEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.HOME); - deleteEvent.on("up", this.onDeleteDown, this); - restoreDefaultEvent.on("up", this.onHomeDown, this); + const deleteEvent = scene.input.keyboard?.addKey(Phaser.Input.Keyboard.KeyCodes.DELETE); + const restoreDefaultEvent = scene.input.keyboard?.addKey(Phaser.Input.Keyboard.KeyCodes.HOME); + deleteEvent && deleteEvent.on("up", this.onDeleteDown, this); + restoreDefaultEvent && restoreDefaultEvent.on("up", this.onHomeDown, this); } setSetting = setSettingKeyboard; diff --git a/src/ui/settings/settings-ui-handler.ts b/src/ui/settings/settings-ui-handler.ts index a6332be1343..b4a40eae1a5 100644 --- a/src/ui/settings/settings-ui-handler.ts +++ b/src/ui/settings/settings-ui-handler.ts @@ -10,7 +10,7 @@ export default class SettingsUiHandler extends AbstractSettingsUiHandler { * @param scene - The BattleScene instance. * @param mode - The UI mode, optional. */ - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.title = "General"; this.settings = Setting.filter(s => s.type === SettingType.GENERAL); diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 22ce5cb7531..447f5c95e46 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -103,7 +103,15 @@ const languageSettings: { [key: string]: LanguageSetting } = { "ko":{ starterInfoTextSize: "52px", instructionTextSize: "38px", - } + }, + "ja":{ + starterInfoTextSize: "51px", + instructionTextSize: "38px", + }, + "ca-ES":{ + starterInfoTextSize: "56px", + instructionTextSize: "38px", + }, }; const starterCandyCosts: { passive: integer, costReduction: [integer, integer], egg: integer }[] = [ @@ -278,7 +286,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private abilityCursor: number = -1; private natureCursor: number = -1; private filterBarCursor: integer = 0; - private starterMoveset: StarterMoveset; + private starterMoveset: StarterMoveset | null; private scrollCursor: number; private allSpecies: PokemonSpecies[] = []; @@ -290,7 +298,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private starterAbilityIndexes: integer[] = []; private starterNatures: Nature[] = []; private starterMovesets: StarterMoveset[] = []; - private speciesStarterDexEntry: DexEntry; + private speciesStarterDexEntry: DexEntry | null; private speciesStarterMoves: Moves[]; private canCycleShiny: boolean; private canCycleForm: boolean; @@ -301,7 +309,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private value: integer = 0; private canAddParty: boolean; - private assetLoadCancelled: Utils.BooleanHolder; + private assetLoadCancelled: Utils.BooleanHolder | null; public cursorObj: Phaser.GameObjects.Image; private starterCursorObjs: Phaser.GameObjects.Image[]; private pokerusCursorObjs: Phaser.GameObjects.Image[]; @@ -323,7 +331,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private instructionRowY = 0; private instructionRowTextOffset = 12; - private starterSelectCallback: StarterSelectCallback; + private starterSelectCallback: StarterSelectCallback | null; private starterPreferences: StarterPreferences; @@ -335,8 +343,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { setup() { const ui = this.getUi(); - const currentLanguage = i18next.resolvedLanguage; - const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)); + const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? + const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang))!; // TODO: is this bang correct? const textSettings = languageSettings[langSettingKey]; this.starterSelectContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6); @@ -398,7 +406,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }); this.filterBar.addFilter(DropDownColumn.TYPES, i18next.t("filterBar:typeFilter"), new DropDown(this.scene, 0, 0, typeOptions, this.updateStarters, DropDownType.HYBRID, 0.5)); - // shiny filter + // caught filter const shiny1Sprite = this.scene.add.sprite(0, 0, "shiny_icons"); shiny1Sprite.setOrigin(0.15, 0.2); shiny1Sprite.setScale(0.6); @@ -415,7 +423,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { shiny3Sprite.setFrame(getVariantIcon(2)); shiny3Sprite.setTint(getVariantTint(2)); - const shinyOptions = [ + const caughtOptions = [ new DropDownOption(this.scene, "SHINY3", new DropDownLabel("", shiny3Sprite)), new DropDownOption(this.scene, "SHINY2", new DropDownLabel("", shiny2Sprite)), new DropDownOption(this.scene, "SHINY", new DropDownLabel("", shiny1Sprite)), @@ -423,7 +431,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { new DropDownOption(this.scene, "UNCAUGHT", new DropDownLabel(i18next.t("filterBar:uncaught"))) ]; - this.filterBar.addFilter(DropDownColumn.DEX, i18next.t("filterBar:dexFilter"), new DropDown(this.scene, 0, 0, shinyOptions, this.updateStarters, DropDownType.HYBRID)); + this.filterBar.addFilter(DropDownColumn.CAUGHT, i18next.t("filterBar:caughtFilter"), new DropDown(this.scene, 0, 0, caughtOptions, this.updateStarters, DropDownType.HYBRID)); // unlocks filter const passiveLabels = [ @@ -431,8 +439,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler { new DropDownLabel(i18next.t("filterBar:passiveUnlocked"), undefined, DropDownState.ON), new DropDownLabel(i18next.t("filterBar:passiveLocked"), undefined, DropDownState.EXCLUDE), ]; + + const costReductionLabels = [ + new DropDownLabel(i18next.t("filterBar:costReduction"), undefined, DropDownState.OFF), + new DropDownLabel(i18next.t("filterBar:costReductionUnlocked"), undefined, DropDownState.ON), + new DropDownLabel(i18next.t("filterBar:costReductionLocked"), undefined, DropDownState.EXCLUDE), + ]; + const unlocksOptions = [ new DropDownOption(this.scene, "PASSIVE", passiveLabels), + new DropDownOption(this.scene, "COST_REDUCTION", costReductionLabels), ]; this.filterBar.addFilter(DropDownColumn.UNLOCKS, i18next.t("filterBar:unlocksFilter"), new DropDown(this.scene, 0, 0, unlocksOptions, this.updateStarters, DropDownType.RADIAL)); @@ -443,18 +459,29 @@ export default class StarterSelectUiHandler extends MessageUiHandler { new DropDownLabel(i18next.t("filterBar:hasWon"), undefined, DropDownState.ON), new DropDownLabel(i18next.t("filterBar:hasNotWon"), undefined, DropDownState.EXCLUDE), ]; + const hiddenAbilityLabels = [ + new DropDownLabel(i18next.t("filterBar:hiddenAbility"), undefined, DropDownState.OFF), + new DropDownLabel(i18next.t("filterBar:hasHiddenAbility"), undefined, DropDownState.ON), + new DropDownLabel(i18next.t("filterBar:noHiddenAbility"), undefined, DropDownState.EXCLUDE), + ]; + const pokerusLabels = [ + new DropDownLabel(i18next.t("filterBar:pokerus"), undefined, DropDownState.OFF), + new DropDownLabel(i18next.t("filterBar:hasPokerus"), undefined, DropDownState.ON), + ]; const miscOptions = [ new DropDownOption(this.scene, "WIN", winLabels), + new DropDownOption(this.scene, "HIDDEN_ABILITY", hiddenAbilityLabels), + new DropDownOption(this.scene, "POKERUS", pokerusLabels), ]; this.filterBar.addFilter(DropDownColumn.MISC, i18next.t("filterBar:miscFilter"), new DropDown(this.scene, 0, 0, miscOptions, this.updateStarters, DropDownType.RADIAL)); // sort filter const sortOptions = [ - new DropDownOption(this.scene, 0, new DropDownLabel(i18next.t("filterBar:sortByNumber"))), - new DropDownOption(this.scene, 1, new DropDownLabel(i18next.t("filterBar:sortByCost"), undefined, DropDownState.OFF)), - new DropDownOption(this.scene, 2, new DropDownLabel(i18next.t("filterBar:sortByCandies"), undefined, DropDownState.OFF)), - new DropDownOption(this.scene, 3, new DropDownLabel(i18next.t("filterBar:sortByIVs"), undefined, DropDownState.OFF)), - new DropDownOption(this.scene, 4, new DropDownLabel(i18next.t("filterBar:sortByName"), undefined, DropDownState.OFF)) + new DropDownOption(this.scene, 0, new DropDownLabel(i18next.t("filterBar:sortByNumber"), undefined, DropDownState.ON)), + new DropDownOption(this.scene, 1, new DropDownLabel(i18next.t("filterBar:sortByCost"))), + new DropDownOption(this.scene, 2, new DropDownLabel(i18next.t("filterBar:sortByCandies"))), + new DropDownOption(this.scene, 3, new DropDownLabel(i18next.t("filterBar:sortByIVs"))), + new DropDownOption(this.scene, 4, new DropDownLabel(i18next.t("filterBar:sortByName"))) ]; this.filterBar.addFilter(DropDownColumn.SORT, i18next.t("filterBar:sortFilter"), new DropDown(this.scene, 0, 0, sortOptions, this.updateStarters, DropDownType.SINGLE)); this.filterBarContainer.add(this.filterBar); @@ -462,7 +489,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterSelectContainer.add(this.filterBarContainer); // Offset the generation filter dropdown to avoid covering the filtered pokemon - this.filterBar.offsetFirstFilter(); + this.filterBar.offsetHybridFilters(); if (!this.scene.uiTheme) { starterContainerWindow.setVisible(false); @@ -547,7 +574,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { startLabel.setOrigin(0.5, 0); this.starterSelectContainer.add(startLabel); - this.startCursorObj = this.scene.add.nineslice(teamWindowX+4, 160, "select_cursor", null, 26, 15, 6, 6, 6, 6); + this.startCursorObj = this.scene.add.nineslice(teamWindowX+4, 160, "select_cursor", undefined, 26, 15, 6, 6, 6, 6); this.startCursorObj.setVisible(false); this.startCursorObj.setOrigin(0, 0); this.starterSelectContainer.add(this.startCursorObj); @@ -818,7 +845,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.executeWithSeedOffset(() => { for (let c = 0; c < 3; c++) { let randomSpeciesId: Species; - let species: PokemonSpecies; + let species: PokemonSpecies | undefined; const generateSpecies = () => { randomSpeciesId = Utils.randSeedItem(starterSpecies); @@ -840,7 +867,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } while (dupe); - this.pokerusSpecies.push(species); + this.pokerusSpecies.push(species!); // TODO: is the bang correct? } }, 0, date.getTime().toString()); @@ -914,19 +941,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { */ resetFilters() : void { const genDropDown: DropDown = this.filterBar.getFilter(DropDownColumn.GEN); - if (this.scene.gameMode.isChallenge) { - // In challenge mode all gens are selected by default - genDropDown.defaultCursor = 0; - } else { - // in other modes, gen 1 is selected by default, and all options disabled - genDropDown.defaultCursor = 1; - } this.filterBar.setValsToDefault(); - // for all modes except challenge, disable all gen options to enable hovering behavior if (!this.scene.gameMode.isChallenge) { - genDropDown.unselectAllOptions(); + // if not in a challenge, in Gen hybrid filter hovering mode, set the cursor to the Gen1 + genDropDown.setCursor(1); } } @@ -1276,7 +1296,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { species = this.starterSpecies[this.starterIconsCursorIndex]; } const ui = this.getUi(); - let options = []; + let options: any[] = []; // TODO: add proper type const [isDupe, removeIndex]: [boolean, number] = this.isInParty(species); // checks to see if the pokemon is a duplicate; if it is, returns the index that will be removed @@ -1302,7 +1322,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const cursorObj = this.starterCursorObjs[this.starterSpecies.length]; cursorObj.setVisible(true); cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y); - this.addToParty(species, this.dexAttrCursor, this.abilityCursor, this.natureCursor as unknown as Nature, this.starterMoveset.slice(0) as StarterMoveset); + this.addToParty(species, this.dexAttrCursor, this.abilityCursor, this.natureCursor as unknown as Nature, this.starterMoveset?.slice(0) as StarterMoveset); ui.playSelect(); } else { ui.playError(); // this should be redundant as there is now a trigger for when a pokemon can't be added to party @@ -1358,7 +1378,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: allMoves[sm].name, handler: () => { this.switchMoveHandler(i, sm, m); - showSwapOptions(this.starterMoveset); + showSwapOptions(this.starterMoveset!); // TODO: is this bang correct? return true; }, onHover: () => { @@ -1369,7 +1389,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }).concat({ label: i18next.t("menu:cancel"), handler: () => { - showSwapOptions(this.starterMoveset); + showSwapOptions(this.starterMoveset!); // TODO: is this bang correct? return true; }, onHover: () => { @@ -1413,7 +1433,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { options.push({ label: i18next.t("starterSelectUiHandler:manageMoves"), handler: () => { - showSwapOptions(this.starterMoveset); + showSwapOptions(this.starterMoveset!); // TODO: is this bang correct? return true; } }); @@ -1429,7 +1449,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.setMode(Mode.STARTER_SELECT).then(() => { ui.showText(i18next.t("starterSelectUiHandler:selectNature"), null, () => { - const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry.natureAttr); + const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr); ui.setModeWithoutClear(Mode.OPTION_SELECT, { options: natures.map((n: Nature, i: number) => { const option: OptionSelectItem = { @@ -1498,7 +1518,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } const showUseCandies = () => { // this lets you use your candies - const options = []; + const options: any[] = []; // TODO: add proper type if (!(passiveAttr & PassiveAttr.UNLOCKED)) { const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]); options.push({ @@ -1671,7 +1691,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { let newFormIndex = props.formIndex; do { newFormIndex = (newFormIndex + 1) % formCount; - if (this.lastSpecies.forms[newFormIndex].isStarterSelectable && this.speciesStarterDexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex)) { + if (this.lastSpecies.forms[newFormIndex].isStarterSelectable && this.speciesStarterDexEntry!.caughtAttr! & this.scene.gameData.getFormAttr(newFormIndex)) { // TODO: are those bangs correct? break; } } while (newFormIndex !== props.formIndex); @@ -1716,7 +1736,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { break; case Button.CYCLE_NATURE: if (this.canCycleNature) { - const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry.natureAttr); + const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr); const natureIndex = natures.indexOf(this.natureCursor); const newNature = natures[natureIndex < natures.length - 1 ? natureIndex + 1 : 0]; // store cycled nature as default @@ -1731,24 +1751,24 @@ export default class StarterSelectUiHandler extends MessageUiHandler { do { newVariant = (newVariant + 1) % 3; if (!newVariant) { - if (this.speciesStarterDexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT) { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.DEFAULT_VARIANT) { // TODO: is this bang correct? break; } } else if (newVariant === 1) { - if (this.speciesStarterDexEntry.caughtAttr & DexAttr.VARIANT_2) { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_2) { // TODO: is this bang correct? break; } } else { - if (this.speciesStarterDexEntry.caughtAttr & DexAttr.VARIANT_3) { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3) { // TODO: is this bang correct? break; } } } while (newVariant !== props.variant); starterAttributes.variant = newVariant; // store the selected variant - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, newVariant, undefined, undefined); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, newVariant as Variant, undefined, undefined); // Cycle tint based on current sprite tint - const tint = getVariantTint(newVariant); - this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant)); + const tint = getVariantTint(newVariant as Variant); + this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant as Variant)); this.pokemonShinyIcon.setTint(tint); success = true; } @@ -1930,34 +1950,34 @@ export default class StarterSelectUiHandler extends MessageUiHandler { switchMoveHandler(i: number, newMove: Moves, move: Moves) { const speciesId = this.lastSpecies.speciesId; - const existingMoveIndex = this.starterMoveset.indexOf(newMove); - this.starterMoveset[i] = newMove; + const existingMoveIndex = this.starterMoveset?.indexOf(newMove)!; // TODO: is this bang correct? + this.starterMoveset![i] = newMove; // TODO: is this bang correct? if (existingMoveIndex > -1) { - this.starterMoveset[existingMoveIndex] = move; + this.starterMoveset![existingMoveIndex] = move; // TODO: is this bang correct? } const props: DexAttrProps = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.dexAttrCursor); // species has different forms if (pokemonFormLevelMoves.hasOwnProperty(speciesId)) { // starterMoveData doesn't have base form moves or is using the single form format if (!this.scene.gameData.starterData[speciesId].moveset || Array.isArray(this.scene.gameData.starterData[speciesId].moveset)) { - this.scene.gameData.starterData[speciesId].moveset = { [props.formIndex]: this.starterMoveset.slice(0) as StarterMoveset }; + this.scene.gameData.starterData[speciesId].moveset = { [props.formIndex]: this.starterMoveset?.slice(0) as StarterMoveset }; } const starterMoveData = this.scene.gameData.starterData[speciesId].moveset; // starterMoveData doesn't have active form moves if (!starterMoveData.hasOwnProperty(props.formIndex)) { - this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset?.slice(0) as StarterMoveset; } // does the species' starter move data have its form's starter moves and has it been updated if (starterMoveData.hasOwnProperty(props.formIndex)) { // active form move hasn't been updated if (starterMoveData[props.formIndex][existingMoveIndex] !== newMove) { - this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset?.slice(0) as StarterMoveset; } } } else { - this.scene.gameData.starterData[speciesId].moveset = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset = this.starterMoveset?.slice(0) as StarterMoveset; } this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined, undefined, false); @@ -1966,7 +1986,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { Array.from({ length: this.starterSpecies.length }, (_, i) => { const starterSpecies = this.starterSpecies[i]; if (starterSpecies.speciesId === speciesId) { - this.starterMovesets[i] = this.starterMoveset; + this.starterMovesets[i] = this.starterMoveset!; // TODO: is this bang correct? } }); } @@ -2107,24 +2127,26 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const isCaught = !!(caughtVariants & DexAttr.NON_SHINY); const isUncaught = !isCaught && !isVariantCaught && !isVariant2Caught && !isVariant3Caught; const isPassiveUnlocked = this.scene.gameData.starterData[container.species.speciesId].passiveAttr > 0; + const isCostReduced = this.scene.gameData.starterData[container.species.speciesId].valueReduction > 0; const isWin = this.scene.gameData.starterData[container.species.speciesId].classicWinCount > 0; const isNotWin = this.scene.gameData.starterData[container.species.speciesId].classicWinCount === 0; const isUndefined = this.scene.gameData.starterData[container.species.speciesId].classicWinCount === undefined; + const isHA = this.scene.gameData.starterData[container.species.speciesId].abilityAttr & AbilityAttr.ABILITY_HIDDEN; const fitsGen = this.filterBar.getVals(DropDownColumn.GEN).includes(container.species.generation); const fitsType = this.filterBar.getVals(DropDownColumn.TYPES).some(type => container.species.isOfType((type as number) - 1)); - const fitsShiny = this.filterBar.getVals(DropDownColumn.DEX).some(variant => { - if (variant === "SHINY3") { + const fitsCaught = this.filterBar.getVals(DropDownColumn.CAUGHT).some(caught => { + if (caught === "SHINY3") { return isVariant3Caught; - } else if (variant === "SHINY2") { + } else if (caught === "SHINY2") { return isVariant2Caught && !isVariant3Caught; - } else if (variant === "SHINY") { + } else if (caught === "SHINY") { return isVariantCaught && !isVariant2Caught && !isVariant3Caught; - } else if (variant === "NORMAL") { + } else if (caught === "NORMAL") { return isCaught && !isVariantCaught && !isVariant2Caught && !isVariant3Caught; - } else if (variant === "UNCAUGHT") { + } else if (caught === "UNCAUGHT") { return isUncaught; } }); @@ -2139,6 +2161,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } }); + const fitsCostReduction = this.filterBar.getVals(DropDownColumn.UNLOCKS).some(unlocks => { + if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.ON) { + return isCostReduced; + } else if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.EXCLUDE) { + return !isCostReduced; + } else if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.OFF) { + return true; + } + }); + const fitsWin = this.filterBar.getVals(DropDownColumn.MISC).some(misc => { if (container.species.speciesId < 10) { } @@ -2151,7 +2183,27 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } }); - if (fitsGen && fitsType && fitsShiny && fitsPassive && fitsWin) { + const fitsHA = this.filterBar.getVals(DropDownColumn.MISC).some(misc => { + if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.ON) { + return isHA; + } else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.EXCLUDE) { + return !isHA; + } else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.OFF) { + return true; + } + }); + + const fitsPokerus = this.filterBar.getVals(DropDownColumn.MISC).some(misc => { + if (misc.val === "POKERUS" && misc.state === DropDownState.ON) { + return this.pokerusSpecies.includes(container.species); + } else if (misc.val === "POKERUS" && misc.state === DropDownState.EXCLUDE) { + return !this.pokerusSpecies.includes(container.species); + } else if (misc.val === "POKERUS" && misc.state === DropDownState.OFF) { + return true; + } + }); + + if (fitsGen && fitsType && fitsCaught && fitsPassive && fitsCostReduction && fitsWin && fitsHA && fitsPokerus) { this.filteredStarterContainers.push(container); } }); @@ -2330,22 +2382,22 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } - setSpecies(species: PokemonSpecies) { + setSpecies(species: PokemonSpecies | null) { this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null; this.dexAttrCursor = species ? this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true) : 0n; this.abilityCursor = species ? this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0; this.natureCursor = species ? this.scene.gameData.getSpeciesDefaultNature(species) : 0; - const starterAttributes : StarterAttributes = species ? {...this.starterPreferences[species.speciesId]} : null; + const starterAttributes : StarterAttributes | null = species ? {...this.starterPreferences[species.speciesId]} : null; // validate starterAttributes if (starterAttributes) { // this may cause changes so we created a copy of the attributes before - if (!isNaN(starterAttributes.variant)) { + if (starterAttributes.variant && !isNaN(starterAttributes.variant)) { if (![ - this.speciesStarterDexEntry.caughtAttr & DexAttr.NON_SHINY, - this.speciesStarterDexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT, - this.speciesStarterDexEntry.caughtAttr & DexAttr.VARIANT_2, - this.speciesStarterDexEntry.caughtAttr & DexAttr.VARIANT_3 + this.speciesStarterDexEntry!.caughtAttr & DexAttr.NON_SHINY, // TODO: is that bang correct? + this.speciesStarterDexEntry!.caughtAttr & DexAttr.DEFAULT_VARIANT, // TODO: is that bang correct? + this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_2, // TODO: is that bang correct? + this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3 // TODO: is that bang correct? ][starterAttributes.variant+1]) { // add 1 as -1 = non-shiny // requested variant wasn't unlocked, purging setting delete starterAttributes.variant; @@ -2353,29 +2405,29 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (typeof starterAttributes.female !== "boolean" || !(starterAttributes.female ? - this.speciesStarterDexEntry.caughtAttr & DexAttr.FEMALE : - this.speciesStarterDexEntry.caughtAttr & DexAttr.MALE + this.speciesStarterDexEntry!.caughtAttr & DexAttr.FEMALE : // TODO: is this bang correct? + this.speciesStarterDexEntry!.caughtAttr & DexAttr.MALE // TODO: is this bang correct? )) { // requested gender wasn't unlocked, purging setting delete starterAttributes.female; } - const abilityAttr = this.scene.gameData.starterData[species.speciesId].abilityAttr; + const abilityAttr = this.scene.gameData.starterData[species!.speciesId].abilityAttr; // TODO: is this bang correct? if (![ abilityAttr & AbilityAttr.ABILITY_1, - species.ability2 ? (abilityAttr & AbilityAttr.ABILITY_2) : abilityAttr & AbilityAttr.ABILITY_HIDDEN, - species.ability2 && abilityAttr & AbilityAttr.ABILITY_HIDDEN - ][starterAttributes.ability]) { + species!.ability2 ? (abilityAttr & AbilityAttr.ABILITY_2) : abilityAttr & AbilityAttr.ABILITY_HIDDEN, // TODO: is this bang correct? + species!.ability2 && abilityAttr & AbilityAttr.ABILITY_HIDDEN // TODO: is this bang correct? + ][starterAttributes.ability!]) { // TODO: is this bang correct? // requested ability wasn't unlocked, purging setting delete starterAttributes.ability; } - if (!(species.forms[starterAttributes.form]?.isStarterSelectable && this.speciesStarterDexEntry.caughtAttr & this.scene.gameData.getFormAttr(starterAttributes.form))) { + if (!(species?.forms[starterAttributes.form!]?.isStarterSelectable && this.speciesStarterDexEntry!.caughtAttr & this.scene.gameData.getFormAttr(starterAttributes.form!))) { // TODO: are those bangs correct? // requested form wasn't unlocked/isn't a starter form, purging setting delete starterAttributes.form; } - if (this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry.natureAttr).indexOf(starterAttributes.nature as unknown as Nature) < 0) { + if (this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr).indexOf(starterAttributes.nature as unknown as Nature) < 0) { // requested nature wasn't unlocked, purging setting delete starterAttributes.nature; } @@ -2385,7 +2437,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // load default nature from stater save data, if set this.natureCursor = starterAttributes.nature; } - if (!isNaN(starterAttributes?.ability)) { + if (starterAttributes?.ability && !isNaN(starterAttributes.ability)) { // load default nature from stater save data, if set this.abilityCursor = starterAttributes.ability; } @@ -2396,7 +2448,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.showStats(); } else { this.statsContainer.setVisible(false); - this.statsContainer.updateIvs(null); + //@ts-ignore + this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh? } } @@ -2413,7 +2466,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.tweens.getTweensOf(icon).forEach(tween => tween.resume()); } - this.lastSpecies = species; + this.lastSpecies = species!; // TODO: is this bang correct? if (species && (this.speciesStarterDexEntry?.seenAttr || this.speciesStarterDexEntry?.caughtAttr)) { this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 4)); @@ -2495,7 +2548,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const candyCropY = 16 - (16 * (currentFriendship / friendshipCap)); if (this.pokemonCandyDarknessOverlay.visible) { - this.pokemonCandyDarknessOverlay.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${currentFriendship}/${friendshipCap}`, true)); + this.pokemonCandyDarknessOverlay.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true)); this.pokemonCandyDarknessOverlay.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -2530,7 +2583,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // load default nature from stater save data, if set const defaultNature = starterAttributes?.nature || this.scene.gameData.getSpeciesDefaultNature(species); props = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); - if (!isNaN(starterAttributes?.variant)) { + if (starterAttributes?.variant && !isNaN(starterAttributes.variant)) { if (props.shiny = (starterAttributes.variant >= 0)) { props.variant = starterAttributes.variant as Variant; } @@ -2542,7 +2595,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } const speciesForm = getPokemonSpeciesForm(species.speciesId, props.formIndex); - this.setTypeIcons(speciesForm.type1, speciesForm.type2); + this.setTypeIcons(speciesForm.type1, speciesForm!.type2!); // TODO: are those bangs correct? this.pokemonSprite.clearTint(); if (this.pokerusSpecies.includes(species)) { @@ -2596,14 +2649,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonCandyCountText.setVisible(false); this.pokemonFormText.setVisible(false); - this.setSpeciesDetails(species, false, 0, false, 0, 0, 0); + this.setSpeciesDetails(species!, false, 0, false, 0, 0, 0); // TODO: is this bang correct? this.pokemonSprite.clearTint(); } } - setSpeciesDetails(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, variant: Variant, abilityIndex: integer, natureIndex: integer, forSeen: boolean = false): void { + setSpeciesDetails(species: PokemonSpecies, shiny?: boolean, formIndex?: integer, female?: boolean, variant?: Variant, abilityIndex?: integer, natureIndex?: integer, forSeen: boolean = false): void { const oldProps = species ? this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null; const oldAbilityIndex = this.abilityCursor > -1 ? this.abilityCursor : this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species); const oldNatureIndex = this.natureCursor > -1 ? this.natureCursor : this.scene.gameData.getSpeciesDefaultNature(species); @@ -2620,10 +2673,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (species) { - this.dexAttrCursor |= (shiny !== undefined ? !shiny : !(shiny = oldProps.shiny)) ? DexAttr.NON_SHINY : DexAttr.SHINY; - this.dexAttrCursor |= (female !== undefined ? !female : !(female = oldProps.female)) ? DexAttr.MALE : DexAttr.FEMALE; - this.dexAttrCursor |= (variant !== undefined ? !variant : !(variant = oldProps.variant)) ? DexAttr.DEFAULT_VARIANT : variant === 1 ? DexAttr.VARIANT_2 : DexAttr.VARIANT_3; - this.dexAttrCursor |= this.scene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps.formIndex)); + this.dexAttrCursor |= (shiny !== undefined ? !shiny : !(shiny = oldProps?.shiny)) ? DexAttr.NON_SHINY : DexAttr.SHINY; + this.dexAttrCursor |= (female !== undefined ? !female : !(female = oldProps?.female)) ? DexAttr.MALE : DexAttr.FEMALE; + this.dexAttrCursor |= (variant !== undefined ? !variant : !(variant = oldProps?.variant)) ? DexAttr.DEFAULT_VARIANT : variant === 1 ? DexAttr.VARIANT_2 : DexAttr.VARIANT_3; + this.dexAttrCursor |= this.scene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps!.formIndex)); // TODO: is this bang correct? this.abilityCursor = abilityIndex !== undefined ? abilityIndex : (abilityIndex = oldAbilityIndex); this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex); } @@ -2665,7 +2718,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } - this.shinyOverlay.setVisible(shiny); + this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default? this.pokemonNumberText.setColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, false)); this.pokemonNumberText.setShadowColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, true)); @@ -2681,16 +2734,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const assetLoadCancelled = new Utils.BooleanHolder(false); this.assetLoadCancelled = assetLoadCancelled; - species.loadAssets(this.scene, female, formIndex, shiny, variant, true).then(() => { + species.loadAssets(this.scene, female!, formIndex, shiny, variant, true).then(() => { // TODO: is this bang correct? if (assetLoadCancelled.value) { return; } this.assetLoadCancelled = null; this.speciesLoaded.set(species.speciesId, true); - this.pokemonSprite.play(species.getSpriteKey(female, formIndex, shiny, variant)); + this.pokemonSprite.play(species.getSpriteKey(female!, formIndex, shiny, variant)); // TODO: is this bang correct? this.pokemonSprite.setPipelineData("shiny", shiny); this.pokemonSprite.setPipelineData("variant", variant); - this.pokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female, formIndex, shiny, variant)); + this.pokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female!, formIndex, shiny, variant)); // TODO: is this bang correct? this.pokemonSprite.setVisible(!this.statsMode); }); @@ -2698,7 +2751,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const isValidForChallenge = new Utils.BooleanHolder(true); Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), !!this.starterSpecies.length); const starterSprite = this.filteredStarterContainers[this.cursor].icon as Phaser.GameObjects.Sprite; - starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female, formIndex, shiny, variant)); + starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female!, formIndex, shiny, variant)); // TODO: is this bang correct? this.filteredStarterContainers[this.cursor].checkIconId(female, formIndex, shiny, variant); this.canCycleShiny = !!(dexEntry.caughtAttr & DexAttr.NON_SHINY && dexEntry.caughtAttr & DexAttr.SHINY); this.canCycleGender = !!(dexEntry.caughtAttr & DexAttr.MALE && dexEntry.caughtAttr & DexAttr.FEMALE); @@ -2706,7 +2759,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey)) .map((_, f) => dexEntry.caughtAttr & this.scene.gameData.getFormAttr(f)).filter(f => f).length > 1; this.canCycleNature = this.scene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1; - this.canCycleVariant = shiny && [ dexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT, dexEntry.caughtAttr & DexAttr.VARIANT_2, dexEntry.caughtAttr & DexAttr.VARIANT_3].filter(v => v).length > 1; + this.canCycleVariant = !!shiny && [ dexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT, dexEntry.caughtAttr & DexAttr.VARIANT_2, dexEntry.caughtAttr & DexAttr.VARIANT_3].filter(v => v).length > 1; } if (dexEntry.caughtAttr && species.malePercent !== null) { @@ -2719,7 +2772,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (dexEntry.caughtAttr) { - const ability = this.lastSpecies.getAbility(abilityIndex); + const ability = this.lastSpecies.getAbility(abilityIndex!); // TODO: is this bang correct? this.pokemonAbilityText.setText(allAbilities[ability].name); const isHidden = abilityIndex === (this.lastSpecies.ability2 ? 2 : 1); @@ -2734,7 +2787,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonNatureText.setText(getNatureName(natureIndex as unknown as Nature, true, true, false, this.scene.uiTheme)); let levelMoves: LevelMoves; - if (pokemonFormLevelMoves.hasOwnProperty(species.speciesId) && pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)) { + if (pokemonFormLevelMoves.hasOwnProperty(species.speciesId) && formIndex && pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)) { levelMoves = pokemonFormLevelMoves[species.speciesId][formIndex]; } else { levelMoves = pokemonSpeciesLevelMoves[species.speciesId]; @@ -2749,41 +2802,42 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } const speciesMoveData = this.scene.gameData.starterData[species.speciesId].moveset; - const moveData: StarterMoveset = speciesMoveData + const moveData: StarterMoveset | null = speciesMoveData ? Array.isArray(speciesMoveData) ? speciesMoveData as StarterMoveset - : (speciesMoveData as StarterFormMoveData)[formIndex] + : (speciesMoveData as StarterFormMoveData)[formIndex!] // TODO: is this bang correct? : null; const availableStarterMoves = this.speciesStarterMoves.concat(speciesEggMoves.hasOwnProperty(species.speciesId) ? speciesEggMoves[species.speciesId].filter((_, em: integer) => this.scene.gameData.starterData[species.speciesId].eggMoves & (1 << em)) : []); this.starterMoveset = (moveData || (this.speciesStarterMoves.slice(0, 4) as StarterMoveset)).filter(m => availableStarterMoves.find(sm => sm === m)) as StarterMoveset; // Consolidate move data if it contains an incompatible move if (this.starterMoveset.length < 4 && this.starterMoveset.length < availableStarterMoves.length) { - this.starterMoveset.push(...availableStarterMoves.filter(sm => this.starterMoveset.indexOf(sm) === -1).slice(0, 4 - this.starterMoveset.length)); + this.starterMoveset.push(...availableStarterMoves.filter(sm => this.starterMoveset?.indexOf(sm) === -1).slice(0, 4 - this.starterMoveset.length)); } // Remove duplicate moves this.starterMoveset = this.starterMoveset.filter( (move, i) => { - return this.starterMoveset.indexOf(move) === i; + return this.starterMoveset?.indexOf(move) === i; }) as StarterMoveset; - const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex); - const formText = Utils.capitalizeString(species?.forms[formIndex]?.formKey, "-", false, false); + const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex!); // TODO: is the bang correct? + const formText = Utils.capitalizeString(species?.forms[formIndex!]?.formKey, "-", false, false); // TODO: is the bang correct? const speciesName = Utils.capitalizeString(Species[species.speciesId], "_", true, false); if (species.speciesId === Species.ARCEUS) { - this.pokemonFormText.setText(i18next.t(`pokemonInfo:Type.${formText.toUpperCase()}`)); + this.pokemonFormText.setText(i18next.t(`pokemonInfo:Type.${formText?.toUpperCase()}`)); } else { this.pokemonFormText.setText(formText ? i18next.t(`pokemonForm:${speciesName}${formText}`) : ""); } - this.setTypeIcons(speciesForm.type1, speciesForm.type2); + this.setTypeIcons(speciesForm.type1, speciesForm.type2!); // TODO: is this bang correct? } else { this.pokemonAbilityText.setText(""); this.pokemonPassiveText.setText(""); this.pokemonNatureText.setText(""); - this.setTypeIcons(null, null); + // @ts-ignore + this.setTypeIcons(null, null); // TODO: resolve ts-ignore.. huh!? } } else { this.shinyOverlay.setVisible(false); @@ -2793,7 +2847,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonAbilityText.setText(""); this.pokemonPassiveText.setText(""); this.pokemonNatureText.setText(""); - this.setTypeIcons(null, null); + // @ts-ignore + this.setTypeIcons(null, null); // TODO: resolve ts-ignore.. huh!? } if (!this.starterMoveset) { @@ -2816,7 +2871,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : "???"); } - this.pokemonEggMovesContainer.setVisible(this.speciesStarterDexEntry?.caughtAttr && hasEggMoves); + this.pokemonEggMovesContainer.setVisible(!!this.speciesStarterDexEntry?.caughtAttr && hasEggMoves); this.pokemonAdditionalMoveCountLabel.setText(`(+${Math.max(this.speciesStarterMoves.length - 4, 0)})`); this.pokemonAdditionalMoveCountLabel.setVisible(this.speciesStarterMoves.length > 4); @@ -3012,7 +3067,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.pushPhase(new TitlePhase(this.scene)); } this.clearText(); - this.scene.getCurrentPhase().end(); + this.scene.getCurrentPhase()?.end(); }, cancel, null, null, 19); }); @@ -3045,7 +3100,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const thisObj = this; const originalStarterSelectCallback = this.starterSelectCallback; this.starterSelectCallback = null; - originalStarterSelectCallback(new Array(this.starterSpecies.length).fill(0).map(function (_, i) { + originalStarterSelectCallback && originalStarterSelectCallback(new Array(this.starterSpecies.length).fill(0).map(function (_, i) { const starterSpecies = thisObj.starterSpecies[i]; return { species: starterSpecies, @@ -3064,7 +3119,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } else { const handler = this.scene.ui.getHandler() as AwaitableUiHandler; handler.tutorialActive = true; - this.scene.ui.showText(i18next.t("starterSelectUiHandler:invalidParty"), null, () => this.scene.ui.showText(null, 0, () => handler.tutorialActive = false), null, true); + this.scene.ui.showText(i18next.t("starterSelectUiHandler:invalidParty"), null, () => this.scene.ui.showText("", 0, () => handler.tutorialActive = false), null, true); } return true; } @@ -3095,7 +3150,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.statsMode = false; this.statsContainer.setVisible(false); this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr); - this.statsContainer.updateIvs(null); + //@ts-ignore + this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? } } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 8c45d767ae0..042437d8c85 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -2,7 +2,7 @@ import BattleScene, { starterColors } from "../battle-scene"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import * as Utils from "../utils"; -import { PlayerPokemon } from "../field/pokemon"; +import { PlayerPokemon, PokemonMove } from "../field/pokemon"; import { getStarterValueFriendshipCap, speciesStarters } from "../data/pokemon-species"; import { argbFromRgba } from "@material/material-color-utilities"; import { Type, getTypeRgb } from "../data/type"; @@ -24,7 +24,6 @@ import i18next from "i18next"; import {modifierSortFunc} from "../modifier/modifier"; import { PlayerGender } from "#enums/player-gender"; - enum Page { PROFILE, STATS, @@ -41,11 +40,11 @@ interface abilityContainer { /** An image displaying the summary label */ labelImage: Phaser.GameObjects.Image, /** The ability object */ - ability: Ability, + ability: Ability | null, /** The text object displaying the name of the ability */ - nameText: Phaser.GameObjects.Text, + nameText: Phaser.GameObjects.Text | null, /** The text object displaying the description of the ability */ - descriptionText: Phaser.GameObjects.Text, + descriptionText: Phaser.GameObjects.Text | null, } export default class SummaryUiHandler extends UiHandler { @@ -79,8 +78,8 @@ export default class SummaryUiHandler extends UiHandler { private summaryPageContainer: Phaser.GameObjects.Container; private movesContainer: Phaser.GameObjects.Container; private moveDescriptionText: Phaser.GameObjects.Text; - private moveCursorObj: Phaser.GameObjects.Sprite; - private selectedMoveCursorObj: Phaser.GameObjects.Sprite; + private moveCursorObj: Phaser.GameObjects.Sprite | null; + private selectedMoveCursorObj: Phaser.GameObjects.Sprite | null; private moveRowsContainer: Phaser.GameObjects.Container; private extraMoveRowContainer: Phaser.GameObjects.Container; private moveEffectContainer: Phaser.GameObjects.Container; @@ -89,14 +88,14 @@ export default class SummaryUiHandler extends UiHandler { private moveCategoryIcon: Phaser.GameObjects.Sprite; private summaryPageTransitionContainer: Phaser.GameObjects.Container; - private descriptionScrollTween: Phaser.Tweens.Tween; - private moveCursorBlinkTimer: Phaser.Time.TimerEvent; + private descriptionScrollTween: Phaser.Tweens.Tween | null; + private moveCursorBlinkTimer: Phaser.Time.TimerEvent | null; - private pokemon: PlayerPokemon; + private pokemon: PlayerPokemon | null; private playerParty: boolean; /**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items**/ - private newMove: Move; - private moveSelectFunction: Function; + private newMove: Move | null; + private moveSelectFunction: Function | null; private transitioning: boolean; private statusVisible: boolean; private moveEffectsVisible: boolean; @@ -104,7 +103,7 @@ export default class SummaryUiHandler extends UiHandler { private moveSelect: boolean; private moveCursor: integer; private selectedMoveIndex: integer; - private selectCallback: Function; + private selectCallback: Function | null; constructor(scene: BattleScene) { super(scene, Mode.SUMMARY); @@ -138,7 +137,7 @@ export default class SummaryUiHandler extends UiHandler { this.numberText.setOrigin(0, 1); this.summaryContainer.add(this.numberText); - this.pokemonSprite = this.scene.initPokemonSprite(this.scene.add.sprite(56, -106, "pkmn__sub"), null, false, true); + this.pokemonSprite = this.scene.initPokemonSprite(this.scene.add.sprite(56, -106, "pkmn__sub"), undefined, false, true); this.summaryContainer.add(this.pokemonSprite); this.nameText = addTextObject(this.scene, 6, -54, "", TextStyle.SUMMARY); @@ -305,10 +304,10 @@ export default class SummaryUiHandler extends UiHandler { this.pokemonSprite.setPipelineData("variant", this.pokemon.illusion.variant ?? this.pokemon.variant); [ "spriteColors", "fusionSpriteColors" ].map(k => { delete this.pokemonSprite.pipelineData[`${k}Base`]; - if (this.pokemon.summonData?.speciesForm) { + if (this.pokemon?.summonData?.speciesForm) { k += "Base"; } - this.pokemonSprite.pipelineData[k] = this.pokemon.getSprite().pipelineData[k]; + this.pokemonSprite.pipelineData[k] = this.pokemon?.getSprite().pipelineData[k]; }); this.pokemon.cry(); @@ -319,7 +318,7 @@ export default class SummaryUiHandler extends UiHandler { this.splicedIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + 2, 3); this.splicedIcon.setVisible(isFusion); if (this.splicedIcon.visible) { - this.splicedIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${this.pokemon.species.getName(this.pokemon.formIndex)}/${this.pokemon.fusionSpecies.getName(this.pokemon.fusionFormIndex)}`, true)); + this.splicedIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${this.pokemon?.species.getName(this.pokemon.formIndex)}/${this.pokemon?.fusionSpecies?.getName(this.pokemon?.fusionFormIndex)}`, true)); this.splicedIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -338,7 +337,7 @@ export default class SummaryUiHandler extends UiHandler { const candyCropY = 16 - (16 * (currentFriendship / friendshipCap)); if (this.candyShadow.visible) { - this.candyShadow.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${currentFriendship}/${friendshipCap}`, true)); + this.candyShadow.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true)); this.candyShadow.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -357,7 +356,7 @@ export default class SummaryUiHandler extends UiHandler { const shinyDescriptor = doubleShiny || baseVariant ? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${this.pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : this.pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}` : ""; - this.shinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); + this.shinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); this.shinyIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -416,16 +415,16 @@ export default class SummaryUiHandler extends UiHandler { if (this.moveSelect) { if (button === Button.ACTION) { - if (this.moveCursor < this.pokemon.moveset.length) { + if (this.pokemon && this.moveCursor < this.pokemon.moveset.length) { if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { - this.moveSelectFunction(this.moveCursor); + this.moveSelectFunction && this.moveSelectFunction(this.moveCursor); } else { if (this.selectedMoveIndex === -1) { this.selectedMoveIndex = this.moveCursor; this.setCursor(this.moveCursor); } else { if (this.selectedMoveIndex !== this.moveCursor) { - const tempMove = this.pokemon.moveset[this.selectedMoveIndex]; + const tempMove = this.pokemon?.moveset[this.selectedMoveIndex]; this.pokemon.moveset[this.selectedMoveIndex] = this.pokemon.moveset[this.moveCursor]; this.pokemon.moveset[this.moveCursor] = tempMove; @@ -483,15 +482,15 @@ export default class SummaryUiHandler extends UiHandler { if (this.cursor === Page.MOVES) { this.showMoveSelect(); success = true; - } else if (this.cursor === Page.PROFILE && this.pokemon.hasPassive()) { + } else if (this.cursor === Page.PROFILE && this.pokemon?.hasPassive()) { // if we're on the PROFILE page and this pokemon has a passive unlocked.. // Since abilities are displayed by default, all we need to do is toggle visibility on all elements to show passives - this.abilityContainer.nameText.setVisible(!this.abilityContainer.descriptionText.visible); - this.abilityContainer.descriptionText.setVisible(!this.abilityContainer.descriptionText.visible); + this.abilityContainer.nameText?.setVisible(!this.abilityContainer.descriptionText?.visible); + this.abilityContainer.descriptionText?.setVisible(!this.abilityContainer.descriptionText.visible); this.abilityContainer.labelImage.setVisible(!this.abilityContainer.labelImage.visible); - this.passiveContainer.nameText.setVisible(!this.passiveContainer.descriptionText.visible); - this.passiveContainer.descriptionText.setVisible(!this.passiveContainer.descriptionText.visible); + this.passiveContainer.nameText?.setVisible(!this.passiveContainer.descriptionText?.visible); + this.passiveContainer.descriptionText?.setVisible(!this.passiveContainer.descriptionText.visible); this.passiveContainer.labelImage.setVisible(!this.passiveContainer.labelImage.visible); } } else if (button === Button.CANCEL) { @@ -523,7 +522,7 @@ export default class SummaryUiHandler extends UiHandler { } const isDown = button === Button.DOWN; const party = this.scene.getParty(); - const partyMemberIndex = party.indexOf(this.pokemon); + const partyMemberIndex = this.pokemon ? party.indexOf(this.pokemon) : -1; if ((isDown && partyMemberIndex < party.length - 1) || (!isDown && partyMemberIndex)) { const page = this.cursor; this.clear(); @@ -609,7 +608,7 @@ export default class SummaryUiHandler extends UiHandler { loop: true, delay: Utils.fixedInt(600), callback: () => { - this.moveCursorObj.setVisible(false); + this.moveCursorObj?.setVisible(false); this.scene.time.delayedCall(Utils.fixedInt(100), () => { if (!this.moveCursorObj) { return; @@ -727,16 +726,16 @@ export default class SummaryUiHandler extends UiHandler { return typeIcon; }; - const types = this.pokemon.getTypes(false, false, true, false); + const types = this.pokemon?.getTypes(false, false, true, false)!; profileContainer.add(getTypeIcon(0, types[0])); if (types.length > 1) { profileContainer.add(getTypeIcon(1, types[1])); } - if (this.pokemon.isTerastallized()) { + if (this.pokemon?.isTerastallized()) { profileContainer.add(getTypeIcon(types.length, this.pokemon.getTeraType(), true)); } - if (this.pokemon.getLuck()) { + if (this.pokemon?.getLuck()) { const luckLabelText = addTextObject(this.scene, 141, 28, i18next.t("common:luckIndicator"), TextStyle.SUMMARY_ALT); luckLabelText.setOrigin(0, 0); profileContainer.add(luckLabelText); @@ -749,13 +748,13 @@ export default class SummaryUiHandler extends UiHandler { this.abilityContainer = { labelImage: this.scene.add.image(0, 0, "summary_profile_ability"), - ability: this.pokemon.getAbility(true), + ability: this.pokemon?.getAbility(true)!, // TODO: is this bang correct? nameText: null, descriptionText: null}; const allAbilityInfo = [this.abilityContainer]; // Creates an array to iterate through // Only add to the array and set up displaying a passive if it's unlocked - if (this.pokemon.hasPassive()) { + if (this.pokemon?.hasPassive()) { this.passiveContainer = { labelImage: this.scene.add.image(0, 0, "summary_profile_passive"), ability: this.pokemon.getPassiveAbility(), @@ -777,11 +776,11 @@ export default class SummaryUiHandler extends UiHandler { abilityInfo.labelImage.setOrigin(0, 0); profileContainer.add(abilityInfo.labelImage); - abilityInfo.nameText = addTextObject(this.scene, 7, 66, abilityInfo.ability.name, TextStyle.SUMMARY_ALT); + abilityInfo.nameText = addTextObject(this.scene, 7, 66, abilityInfo.ability?.name!, TextStyle.SUMMARY_ALT); // TODO: is this bang correct? abilityInfo.nameText.setOrigin(0, 1); profileContainer.add(abilityInfo.nameText); - abilityInfo.descriptionText = addTextObject(this.scene, 7, 69, abilityInfo.ability.description, TextStyle.WINDOW_ALT, { wordWrap: { width: 1224 } }); + abilityInfo.descriptionText = addTextObject(this.scene, 7, 69, abilityInfo.ability?.description!, TextStyle.WINDOW_ALT, { wordWrap: { width: 1224 } }); // TODO: is this bang correct? abilityInfo.descriptionText.setOrigin(0, 0); profileContainer.add(abilityInfo.descriptionText); @@ -813,17 +812,17 @@ export default class SummaryUiHandler extends UiHandler { }); // Turn off visibility of passive info by default this.passiveContainer?.labelImage.setVisible(false); - this.passiveContainer?.nameText.setVisible(false); - this.passiveContainer?.descriptionText.setVisible(false); + this.passiveContainer?.nameText?.setVisible(false); + this.passiveContainer?.descriptionText?.setVisible(false); const closeFragment = getBBCodeFrag("", TextStyle.WINDOW_ALT); - const rawNature = Utils.toReadableString(Nature[this.pokemon.getNature()]); - const nature = `${getBBCodeFrag(Utils.toReadableString(getNatureName(this.pokemon.getNature())), TextStyle.SUMMARY_RED)}${closeFragment}`; + const rawNature = Utils.toReadableString(Nature[this.pokemon?.getNature()!]); // TODO: is this bang correct? + const nature = `${getBBCodeFrag(Utils.toReadableString(getNatureName(this.pokemon?.getNature()!)), TextStyle.SUMMARY_RED)}${closeFragment}`; // TODO: is this bang correct? const memoString = i18next.t("pokemonSummary:memoString", { - metFragment: i18next.t(`pokemonSummary:metFragment.${this.pokemon.metBiome === -1? "apparently": "normal"}`, { - biome: `${getBBCodeFrag(getBiomeName(this.pokemon.metBiome), TextStyle.SUMMARY_RED)}${closeFragment}`, - level: `${getBBCodeFrag(this.pokemon.metLevel.toString(), TextStyle.SUMMARY_RED)}${closeFragment}`, + metFragment: i18next.t(`pokemonSummary:metFragment.${this.pokemon?.metBiome === -1? "apparently": "normal"}`, { + biome: `${getBBCodeFrag(getBiomeName(this.pokemon?.metBiome!), TextStyle.SUMMARY_RED)}${closeFragment}`, // TODO: is this bang correct? + level: `${getBBCodeFrag(this.pokemon?.metLevel.toString()!, TextStyle.SUMMARY_RED)}${closeFragment}`, // TODO: is this bang correct? }), natureFragment: i18next.exists(`pokemonSummary:natureFragment.${rawNature}`) ? @@ -831,7 +830,7 @@ export default class SummaryUiHandler extends UiHandler { nature, }); - const memoText = addBBCodeTextObject(this.scene, 7, 113, memoString, TextStyle.WINDOW_ALT); + const memoText = addBBCodeTextObject(this.scene, 7, 113, String(memoString), TextStyle.WINDOW_ALT); memoText.setOrigin(0, 0); profileContainer.add(memoText); break; @@ -846,15 +845,15 @@ export default class SummaryUiHandler extends UiHandler { const rowIndex = s % 3; const colIndex = Math.floor(s / 3); - const natureStatMultiplier = getNatureStatMultiplier(this.pokemon.getNature(), s); + const natureStatMultiplier = getNatureStatMultiplier(this.pokemon?.getNature()!, s); // TODO: is this bang correct? const statLabel = addTextObject(this.scene, 27 + 115 * colIndex + (colIndex === 1 ? 5 : 0), 56 + 16 * rowIndex, statName, natureStatMultiplier === 1 ? TextStyle.SUMMARY : natureStatMultiplier > 1 ? TextStyle.SUMMARY_PINK : TextStyle.SUMMARY_BLUE); statLabel.setOrigin(0.5, 0); statsContainer.add(statLabel); const statValueText = stat !== Stat.HP - ? Utils.formatStat(this.pokemon.stats[s]) - : `${Utils.formatStat(this.pokemon.hp, true)}/${Utils.formatStat(this.pokemon.getMaxHp(), true)}`; + ? Utils.formatStat(this.pokemon?.stats[s]!) // TODO: is this bang correct? + : `${Utils.formatStat(this.pokemon?.hp!, true)}/${Utils.formatStat(this.pokemon?.getMaxHp()!, true)}`; // TODO: are those bangs correct? const statValue = addTextObject(this.scene, 120 + 88 * colIndex, 56 + 16 * rowIndex, statValueText, TextStyle.WINDOW_ALT); statValue.setOrigin(1, 0); @@ -862,7 +861,7 @@ export default class SummaryUiHandler extends UiHandler { }); const itemModifiers = (this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.pokemonId === this.pokemon.id, this.playerParty) as PokemonHeldItemModifier[]) + && m.pokemonId === this.pokemon?.id, this.playerParty) as PokemonHeldItemModifier[]) .sort(modifierSortFunc); itemModifiers.forEach((item, i) => { @@ -876,8 +875,12 @@ export default class SummaryUiHandler extends UiHandler { icon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); }); - const relLvExp = getLevelRelExp(this.pokemon.level + 1, this.pokemon.species.growthRate); - const expRatio = this.pokemon.level < this.scene.getMaxExpLevel() ? this.pokemon.levelExp / relLvExp : 0; + const pkmLvl = this.pokemon?.level!; // TODO: is this bang correct? + const pkmLvlExp = this.pokemon?.levelExp!; // TODO: is this bang correct? + const pkmExp = this.pokemon?.exp!; // TODO: is this bang correct? + const pkmSpeciesGrowthRate = this.pokemon?.species.growthRate!; // TODO: is this bang correct? + const relLvExp = getLevelRelExp(pkmLvl + 1, pkmSpeciesGrowthRate); + const expRatio = pkmLvl < this.scene.getMaxExpLevel() ? pkmLvlExp / relLvExp : 0; const expLabel = addTextObject(this.scene, 6, 112, i18next.t("pokemonSummary:expPoints"), TextStyle.SUMMARY); expLabel.setOrigin(0, 0); @@ -887,12 +890,12 @@ export default class SummaryUiHandler extends UiHandler { nextLvExpLabel.setOrigin(0, 0); statsContainer.add(nextLvExpLabel); - const expText = addTextObject(this.scene, 208, 112, this.pokemon.exp.toString(), TextStyle.WINDOW_ALT); + const expText = addTextObject(this.scene, 208, 112, pkmExp.toString(), TextStyle.WINDOW_ALT); expText.setOrigin(1, 0); statsContainer.add(expText); - const nextLvExp = this.pokemon.level < this.scene.getMaxExpLevel() - ? getLevelTotalExp(this.pokemon.level + 1, this.pokemon.species.growthRate) - this.pokemon.exp + const nextLvExp = pkmLvl < this.scene.getMaxExpLevel() + ? getLevelTotalExp(pkmLvl + 1, pkmSpeciesGrowthRate) - pkmExp : 0; const nextLvExpText = addTextObject(this.scene, 208, 128, nextLvExp.toString(), TextStyle.WINDOW_ALT); nextLvExpText.setOrigin(1, 0); @@ -924,14 +927,14 @@ export default class SummaryUiHandler extends UiHandler { extraRowOverlay.setOrigin(0, 1); this.extraMoveRowContainer.add(extraRowOverlay); - const extraRowText = addTextObject(this.scene, 35, 0, this.summaryUiMode === SummaryUiMode.LEARN_MOVE ? this.newMove.name : i18next.t("pokemonSummary:cancel"), + const extraRowText = addTextObject(this.scene, 35, 0, this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.newMove ? this.newMove.name : i18next.t("pokemonSummary:cancel"), this.summaryUiMode === SummaryUiMode.LEARN_MOVE ? TextStyle.SUMMARY_PINK : TextStyle.SUMMARY); extraRowText.setOrigin(0, 1); this.extraMoveRowContainer.add(extraRowText); if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { this.extraMoveRowContainer.setVisible(true); - const newMoveTypeIcon = this.scene.add.sprite(0, 0, `types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`, Type[this.newMove.type].toLowerCase()); + const newMoveTypeIcon = this.scene.add.sprite(0, 0, `types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`, Type[this.newMove?.type!].toLowerCase()); // TODO: is this bang correct? newMoveTypeIcon.setOrigin(0, 1); this.extraMoveRowContainer.add(newMoveTypeIcon); @@ -939,7 +942,7 @@ export default class SummaryUiHandler extends UiHandler { ppOverlay.setOrigin(0, 1); this.extraMoveRowContainer.add(ppOverlay); - const pp = Utils.padInt(this.newMove.pp, 2, " "); + const pp = Utils.padInt(this.newMove?.pp!, 2, " "); // TODO: is this bang correct? const ppText = addTextObject(this.scene, 173, 1, `${pp}/${pp}`, TextStyle.WINDOW); ppText.setOrigin(0, 1); this.extraMoveRowContainer.add(ppText); @@ -949,7 +952,7 @@ export default class SummaryUiHandler extends UiHandler { this.movesContainer.add(this.moveRowsContainer); for (let m = 0; m < 4; m++) { - const move = m < this.pokemon.moveset.length ? this.pokemon.moveset[m] : null; + const move: PokemonMove | null = this.pokemon && this.pokemon.moveset.length > m ? this.pokemon?.moveset[m] : null; const moveRowContainer = this.scene.add.container(0, 16 * m); this.moveRowsContainer.add(moveRowContainer); @@ -1020,13 +1023,13 @@ export default class SummaryUiHandler extends UiHandler { }); } - getSelectedMove(): Move { + getSelectedMove(): Move | null { if (this.cursor !== Page.MOVES) { return null; } - if (this.moveCursor < 4 && this.moveCursor < this.pokemon.moveset.length) { - return this.pokemon.moveset[this.moveCursor].getMove(); + if (this.moveCursor < 4 && this.pokemon && this.moveCursor < this.pokemon.moveset.length) { + return this.pokemon.moveset[this.moveCursor]!.getMove(); // TODO: is this bang correct? } else if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.moveCursor === 4) { return this.newMove; } @@ -1043,7 +1046,7 @@ export default class SummaryUiHandler extends UiHandler { hideMoveSelect() { if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { - this.moveSelectFunction(4); + this.moveSelectFunction && this.moveSelectFunction(4); return; } diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index 48799473343..9a0922715e8 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -18,7 +18,7 @@ export default class TargetSelectUiHandler extends UiHandler { private isMultipleTargets: boolean = false; private targets: BattlerIndex[]; private targetsHighlighted: Pokemon[]; - private targetFlashTween: Phaser.Tweens.Tween; + private targetFlashTween: Phaser.Tweens.Tween | null; private targetBattleInfoMoveTween: Phaser.Tweens.Tween[] = []; constructor(scene: BattleScene) { @@ -68,12 +68,12 @@ export default class TargetSelectUiHandler extends UiHandler { switch (button) { case Button.UP: if (this.cursor < BattlerIndex.ENEMY && this.targets.findIndex(t => t >= BattlerIndex.ENEMY) > -1) { - success = this.setCursor(this.targets.find(t => t >= BattlerIndex.ENEMY)); + success = this.setCursor(this.targets.find(t => t >= BattlerIndex.ENEMY)!); // TODO: is the bang correct here? } break; case Button.DOWN: if (this.cursor >= BattlerIndex.ENEMY && this.targets.findIndex(t => t < BattlerIndex.ENEMY) > -1) { - success = this.setCursor(this.targets.find(t => t < BattlerIndex.ENEMY)); + success = this.setCursor(this.targets.find(t => t < BattlerIndex.ENEMY)!); // TODO: is the bang correct here? } break; case Button.LEFT: diff --git a/src/ui/text.ts b/src/ui/text.ts index 2a12e883deb..c1d7fe091c0 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -5,6 +5,7 @@ import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodete import InputText from "phaser3-rex-plugins/plugins/inputtext"; import BattleScene from "../battle-scene"; import { ModifierTier } from "../modifier/modifier-tier"; +import i18next from "#app/plugins/i18n.js"; export enum TextStyle { MESSAGE, @@ -24,6 +25,7 @@ export enum TextStyle { MONEY, STATS_LABEL, STATS_VALUE, + SETTINGS_VALUE, SETTINGS_LABEL, SETTINGS_SELECTED, SETTINGS_LOCKED, @@ -35,47 +37,68 @@ export enum TextStyle { MOVE_PP_NEAR_EMPTY, MOVE_PP_EMPTY, SMALLER_WINDOW_ALT, - BGM_BAR + BGM_BAR, + PERFECT_IV +} + +export interface TextStyleOptions { + scale: number, + styleOptions: Phaser.Types.GameObjects.Text.TextStyle | InputText.IConfig, + shadowColor: string, + shadowXpos: number, + shadowYpos: number } export function addTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): Phaser.GameObjects.Text { - const [ scale, styleOptions, shadowColor, shadowXpos, shadowYpos ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); + const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); const ret = scene.add.text(x, y, content, styleOptions); ret.setScale(scale); ret.setShadow(shadowXpos, shadowYpos, shadowColor); if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) { - ret.setLineSpacing(5); + ret.setLineSpacing(scale * 30); + } + + if (ret.lineSpacing < 12 && i18next.resolvedLanguage === "ja") { + ret.setLineSpacing(ret.lineSpacing + 35); } return ret; } export function setTextStyle(obj: Phaser.GameObjects.Text, scene: Phaser.Scene, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle) { - const [ scale, styleOptions, shadowColor, shadowXpos, shadowYpos ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); + const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); obj.setScale(scale); obj.setShadow(shadowXpos, shadowYpos, shadowColor); if (!(styleOptions as Phaser.Types.GameObjects.Text.TextStyle).lineSpacing) { - obj.setLineSpacing(5); + obj.setLineSpacing(scale * 30); + } + + if (obj.lineSpacing < 12 && i18next.resolvedLanguage === "ja") { + obj.setLineSpacing(obj.lineSpacing + 35); } } export function addBBCodeTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): BBCodeText { - const [ scale, styleOptions, shadowColor, shadowXpos, shadowYpos ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); + const { scale, styleOptions, shadowColor, shadowXpos, shadowYpos } = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); const ret = new BBCodeText(scene, x, y, content, styleOptions as BBCodeText.TextStyle); scene.add.existing(ret); ret.setScale(scale); ret.setShadow(shadowXpos, shadowYpos, shadowColor); if (!(styleOptions as BBCodeText.TextStyle).lineSpacing) { - ret.setLineSpacing(10); + ret.setLineSpacing(scale * 60); + } + + if (ret.lineSpacing < 12 && i18next.resolvedLanguage === "ja") { + ret.setLineSpacing(ret.lineSpacing + 35); } return ret; } export function addTextInputObject(scene: Phaser.Scene, x: number, y: number, width: number, height: number, style: TextStyle, extraStyleOptions?: InputText.IConfig): InputText { - const [ scale, styleOptions ] = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); + const { scale, styleOptions } = getTextStyleOptions(style, (scene as BattleScene).uiTheme, extraStyleOptions); const ret = new InputText(scene, x, y, width, height, styleOptions as InputText.IConfig); scene.add.existing(ret); @@ -84,10 +107,11 @@ export function addTextInputObject(scene: Phaser.Scene, x: number, y: number, wi return ret; } -export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): [ number, Phaser.Types.GameObjects.Text.TextStyle | InputText.IConfig, string, number, number ] { +export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle): TextStyleOptions { + const lang = i18next.resolvedLanguage; let shadowXpos = 4; let shadowYpos = 5; - const scale = 0.1666666667; + let scale = 0.1666666667; const defaultFontSize = 96; let styleOptions: Phaser.Types.GameObjects.Text.TextStyle = { @@ -99,6 +123,11 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty } }; + if (i18next.resolvedLanguage === "ja") { + scale = 0.1388888889; + styleOptions.padding = { top:2, bottom:4 }; + } + switch (style) { case TextStyle.SUMMARY: case TextStyle.SUMMARY_ALT: @@ -110,11 +139,37 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty case TextStyle.SUMMARY_GREEN: case TextStyle.WINDOW: case TextStyle.WINDOW_ALT: - case TextStyle.STATS_VALUE: shadowXpos = 3; shadowYpos = 3; break; case TextStyle.STATS_LABEL: + let fontSizeLabel = "96px"; + switch (lang) { + case "de": + shadowXpos = 3; + shadowYpos = 3; + fontSizeLabel = "80px"; + break; + default: + fontSizeLabel = "96px"; + break; + } + styleOptions.fontSize = fontSizeLabel; + break; + case TextStyle.STATS_VALUE: + shadowXpos = 3; + shadowYpos = 3; + let fontSizeValue = "96px"; + switch (lang) { + case "de": + fontSizeValue = "80px"; + break; + default: + fontSizeValue = "96px"; + break; + } + styleOptions.fontSize = fontSizeValue; + break; case TextStyle.MESSAGE: case TextStyle.SETTINGS_LABEL: case TextStyle.SETTINGS_LOCKED: @@ -158,13 +213,13 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty if (extraStyleOptions) { if (extraStyleOptions.fontSize) { - const sizeRatio = parseInt(extraStyleOptions.fontSize.toString().slice(0, -2)) / parseInt(styleOptions.fontSize.toString().slice(0, -2)); + const sizeRatio = parseInt(extraStyleOptions.fontSize.toString().slice(0, -2)) / parseInt(styleOptions.fontSize?.toString().slice(0, -2) ?? "1"); shadowXpos *= sizeRatio; } styleOptions = Object.assign(styleOptions, extraStyleOptions); } - return [ scale, styleOptions, shadowColor, shadowXpos, shadowYpos ]; + return { scale, styleOptions, shadowColor, shadowXpos, shadowYpos }; } export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string { @@ -179,6 +234,7 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui case TextStyle.MOVE_INFO_CONTENT: case TextStyle.MOVE_PP_FULL: case TextStyle.TOOLTIP_CONTENT: + case TextStyle.SETTINGS_VALUE: if (uiTheme) { return !shadow ? "#484848" : "#d0d0c8"; } @@ -236,6 +292,7 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui case TextStyle.SUMMARY_GREEN: return !shadow ? "#78c850" : "#306850"; case TextStyle.SETTINGS_LABEL: + case TextStyle.PERFECT_IV: return !shadow ? "#f8b050" : "#c07800"; case TextStyle.SETTINGS_SELECTED: return !shadow ? "#f88880" : "#f83018"; diff --git a/src/ui/time-of-day-widget.ts b/src/ui/time-of-day-widget.ts index e71839a84c5..5d2f184e679 100644 --- a/src/ui/time-of-day-widget.ts +++ b/src/ui/time-of-day-widget.ts @@ -147,7 +147,10 @@ export default class TimeOfDayWidget extends Phaser.GameObjects.Container { // Swaps all elements of the icon arrays by shifting the first element onto the end of the array // This ensures index[0] is always the new time of day icon and index[1] is always the current one this.timeOfDayIconPairs.forEach( - icons => icons.push(icons.shift())); + icons => { + const shifted = icons.shift(); + shifted && icons.push(shifted); + }); } /** diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index 4036e0b9922..3c25ed34d61 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -2,7 +2,7 @@ import BattleScene from "../battle-scene"; import OptionSelectUiHandler from "./settings/option-select-ui-handler"; import { Mode } from "./ui"; import * as Utils from "../utils"; -import { TextStyle, addTextObject } from "./text"; +import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; import { getBattleCountSplashMessage, getSplashMessages } from "../data/splash-messages"; import i18next from "i18next"; import { TimedEventDisplay } from "#app/timed-event-manager.js"; @@ -14,7 +14,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { private splashMessageText: Phaser.GameObjects.Text; private eventDisplay: TimedEventDisplay; - private titleStatsTimer: NodeJS.Timeout; + private titleStatsTimer: NodeJS.Timeout | null; constructor(scene: BattleScene, mode: Mode = Mode.TITLE) { super(scene, mode); @@ -40,7 +40,14 @@ export default class TitleUiHandler extends OptionSelectUiHandler { this.titleContainer.add(this.eventDisplay); } - this.playerCountLabel = addTextObject(this.scene, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - 109, `? ${i18next.t("menu:playersOnline")}`, TextStyle.MESSAGE, { fontSize: "54px" }); + this.playerCountLabel = addTextObject( + this.scene, + (this.scene.game.canvas.width / 6) - 2, + (this.scene.game.canvas.height / 6) - 13 - 576 * getTextStyleOptions(TextStyle.WINDOW, this.scene.uiTheme).scale, + `? ${i18next.t("menu:playersOnline")}`, + TextStyle.MESSAGE, + { fontSize: "54px" } + ); this.playerCountLabel.setOrigin(1, 0); this.titleContainer.add(this.playerCountLabel); @@ -111,7 +118,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { this.eventDisplay?.clear(); - clearInterval(this.titleStatsTimer); + this.titleStatsTimer && clearInterval(this.titleStatsTimer); this.titleStatsTimer = null; this.scene.tweens.add({ diff --git a/src/ui/ui-handler.ts b/src/ui/ui-handler.ts index 8336109847e..94625efaa75 100644 --- a/src/ui/ui-handler.ts +++ b/src/ui/ui-handler.ts @@ -8,7 +8,7 @@ import {Button} from "#enums/buttons"; */ export default abstract class UiHandler { protected scene: BattleScene; - protected mode: integer; + protected mode: integer | null; protected cursor: integer = 0; public active: boolean = false; @@ -16,7 +16,7 @@ export default abstract class UiHandler { * @param {BattleScene} scene The same scene as everything else. * @param {Mode} mode The mode of the UI element. These should be unique. */ - constructor(scene: BattleScene, mode: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { this.scene = scene; this.mode = mode; } diff --git a/src/ui/ui-theme.ts b/src/ui/ui-theme.ts index 58490e1c618..75725910b82 100644 --- a/src/ui/ui-theme.ts +++ b/src/ui/ui-theme.ts @@ -43,7 +43,7 @@ export function addWindow(scene: BattleScene, x: number, y: number, width: numbe const borderSize = scene.uiTheme ? 6 : 8; - const window = scene.add.nineslice(x, y, `window_${scene.windowType}${getWindowVariantSuffix(windowVariant)}`, null, width, height, borderSize, borderSize, borderSize, borderSize); + const window = scene.add.nineslice(x, y, `window_${scene.windowType}${getWindowVariantSuffix(windowVariant)}`, undefined, width, height, borderSize, borderSize, borderSize, borderSize); window.setOrigin(0, 0); if (mergeMaskLeft || mergeMaskTop || maskOffsetX || maskOffsetY) { diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 6061baf3b4a..8ea4270deb3 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -271,10 +271,10 @@ export default class UI extends Phaser.GameObjects.Container { return handler.processInput(button); } - showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer): void { + showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null): void { if (prompt && text.indexOf("$") > -1) { const messagePages = text.split(/\$/g).map(m => m.trim()); - let showMessageAndCallback = () => callback(); + let showMessageAndCallback = () => callback && callback(); for (let p = messagePages.length - 1; p >= 0; p--) { const originalFunc = showMessageAndCallback; showMessageAndCallback = () => this.showText(messagePages[p], null, originalFunc, null, true); @@ -290,18 +290,18 @@ export default class UI extends Phaser.GameObjects.Container { } } - showDialogue(text: string, name: string, delay: integer = 0, callback: Function, callbackDelay?: integer, promptDelay?: integer): void { + showDialogue(text: string, name: string | undefined, delay: integer | null = 0, callback: Function, callbackDelay?: integer, promptDelay?: integer): void { // First get the gender of the player (default male) (also used if UNSET) let playerGenderPrefix = "PGM"; if ((this.scene as BattleScene).gameData.gender === PlayerGender.FEMALE) { playerGenderPrefix = "PGF"; } // Add the prefix to the text - const localizationKey = playerGenderPrefix + text; + const localizationKey: string = playerGenderPrefix + text; // Get localized dialogue (if available) let hasi18n = false; - if (i18next.exists(localizationKey as ParseKeys) ) { + if (i18next.exists(localizationKey) ) { text = i18next.t(localizationKey as ParseKeys); hasi18n = true; @@ -341,7 +341,7 @@ export default class UI extends Phaser.GameObjects.Container { const key = playerGenderPrefix + text; - if (i18next.exists(key as ParseKeys) ) { + if (i18next.exists(key) ) { if ((this.scene as BattleScene).skipSeenDialogues && (this.scene as BattleScene).gameData.getSeenDialogues()[key] === true) { return true; } @@ -371,8 +371,8 @@ export default class UI extends Phaser.GameObjects.Container { update(): void { if (this.tooltipContainer.visible) { - const reverse = this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12; - this.tooltipContainer.setPosition(!reverse ? this.scene.game.input.mousePointer.x / 6 + 2 : this.scene.game.input.mousePointer.x / 6 - this.tooltipBg.width - 2, this.scene.game.input.mousePointer.y / 6 + 2); + const reverse = this.scene.game.input.mousePointer && this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12; + this.tooltipContainer.setPosition(!reverse ? this.scene.game.input.mousePointer!.x / 6 + 2 : this.scene.game.input.mousePointer!.x / 6 - this.tooltipBg.width - 2, this.scene.game.input.mousePointer!.y / 6 + 2); // TODO: are these bangs correct? } } @@ -454,7 +454,7 @@ export default class UI extends Phaser.GameObjects.Container { this.modeChain.push(this.mode); } this.mode = mode; - const touchControls = document.getElementById("touchControls"); + const touchControls = document?.getElementById("touchControls"); if (touchControls) { touchControls.dataset.uiMode = Mode[mode]; } @@ -507,7 +507,7 @@ export default class UI extends Phaser.GameObjects.Container { const doRevertMode = () => { this.getHandler().clear(); - this.mode = this.modeChain.pop(); + this.mode = this.modeChain.pop()!; // TODO: is this bang correct? const touchControls = document.getElementById("touchControls"); if (touchControls) { touchControls.dataset.uiMode = Mode[this.mode]; diff --git a/src/ui/unavailable-modal-ui-handler.ts b/src/ui/unavailable-modal-ui-handler.ts index c864801d9b4..dab1a8c3be8 100644 --- a/src/ui/unavailable-modal-ui-handler.ts +++ b/src/ui/unavailable-modal-ui-handler.ts @@ -7,7 +7,7 @@ import * as Utils from "#app/utils"; import i18next from "i18next"; export default class UnavailableModalUiHandler extends ModalUiHandler { - private reconnectTimer: NodeJS.Timeout; + private reconnectTimer: NodeJS.Timeout | null; private reconnectDuration: number; private reconnectCallback: () => void; @@ -16,7 +16,7 @@ export default class UnavailableModalUiHandler extends ModalUiHandler { private readonly randVarianceTime = 1000 * 10; - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.reconnectDuration = this.minTime; } diff --git a/src/ui/vouchers-ui-handler.ts b/src/ui/vouchers-ui-handler.ts index 370859bed54..bb2dc2cd0be 100644 --- a/src/ui/vouchers-ui-handler.ts +++ b/src/ui/vouchers-ui-handler.ts @@ -22,9 +22,9 @@ export default class VouchersUiHandler extends MessageUiHandler { private itemsTotal: integer; private scrollCursor: integer; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; - constructor(scene: BattleScene, mode?: Mode) { + constructor(scene: BattleScene, mode: Mode | null = null) { super(scene, mode); this.itemsTotal = Object.keys(vouchers).length; @@ -192,7 +192,7 @@ export default class VouchersUiHandler extends MessageUiHandler { let updateVoucher = ret; if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight", null, 16, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight", undefined, 16, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.voucherIconsContainer.add(this.cursorObj); updateVoucher = true; diff --git a/src/utils.ts b/src/utils.ts index ede0a4bd78a..01a0b8faa42 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -116,7 +116,7 @@ export function randSeedWeightedItem(items: T[]): T { : Phaser.Math.RND.weightedPick(items); } -export function randSeedEasedWeightedItem(items: T[], easingFunction: string = "Sine.easeIn"): T { +export function randSeedEasedWeightedItem(items: T[], easingFunction: string = "Sine.easeIn"): T | null { if (!items.length) { return null; } @@ -246,16 +246,16 @@ export function formatStat(stat: integer, forHp: boolean = false): string { return formatLargeNumber(stat, forHp ? 100000 : 1000000); } -export function getEnumKeys(enumType): string[] { - return Object.values(enumType).filter(v => isNaN(parseInt(v.toString()))).map(v => v.toString()); +export function getEnumKeys(enumType: any): string[] { + return Object.values(enumType).filter(v => isNaN(parseInt(v!.toString()))).map(v => v!.toString()); } -export function getEnumValues(enumType): integer[] { - return Object.values(enumType).filter(v => !isNaN(parseInt(v.toString()))).map(v => parseInt(v.toString())); +export function getEnumValues(enumType: any): integer[] { + return Object.values(enumType).filter(v => !isNaN(parseInt(v!.toString()))).map(v => parseInt(v!.toString())); } -export function executeIf(condition: boolean, promiseFunc: () => Promise): Promise { - return condition ? promiseFunc() : new Promise(resolve => resolve(null)); +export function executeIf(condition: boolean, promiseFunc: () => Promise): Promise { + return condition ? promiseFunc() : new Promise(resolve => resolve(null)); } export const sessionIdKey = "pokerogue_sessionId"; @@ -432,7 +432,7 @@ export function deltaRgb(rgb1: integer[], rgb2: integer[]): integer { } export function rgbHexToRgba(hex: string) { - const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i); + const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i)!; // TODO: is this bang correct? return { r: parseInt(color[1], 16), g: parseInt(color[2], 16), @@ -466,6 +466,7 @@ export function verifyLang(lang?: string): boolean { case "zh-TW": case "pt-BR": case "ko": + case "ja": return true; default: return false; diff --git a/tsconfig.json b/tsconfig.json index 546ea2a9d12..f8e019a1b8b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "esModuleInterop": true, - "strictNullChecks": false, + "strictNullChecks": true, "sourceMap": false, "strict": false, "rootDir": "./src", diff --git a/vite.config.ts b/vite.config.ts index f5c95aa56bd..4bd013bff2e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -20,7 +20,8 @@ export const defaultConfig = { } warn(warning); }, - } + }, + appType: "mpa", };