merge up-to date

This commit is contained in:
Lylian 2024-08-08 14:56:16 +02:00
commit 9f73316f7e
518 changed files with 33841 additions and 8564 deletions

View File

@ -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.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the Feature**
<!-- 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 -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

View File

@ -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

View File

@ -5,12 +5,12 @@
## What are the changes? ## What are the changes?
<!-- Summarize what are the changes from a user perspective on the application --> <!-- Summarize what are the changes from a user perspective on the application -->
## Why am I doing these changes? ## Why am I doing these changes the user will see?
<!-- Explain why you decided to introduce these changes --> <!-- Explain why you decided to introduce these changes -->
<!-- Does it come from an issue or another PR? Please link it --> <!-- Does it come from an issue or another PR? Please link it -->
<!-- Explain why you believe this can enhance user experience --> <!-- Explain why you believe this can enhance user experience -->
## What did change? ## What are the changes from a developer perspective?
<!-- Explicitly state what are the changes introduced by the PR --> <!-- Explicitly state what are the changes introduced by the PR -->
<!-- You can make use of a comparison between what was the state before and after your PR changes --> <!-- You can make use of a comparison between what was the state before and after your PR changes -->
@ -30,6 +30,7 @@
- [ ] The PR is self-contained and cannot be split into smaller PRs? - [ ] The PR is self-contained and cannot be split into smaller PRs?
- [ ] Have I provided a clear explanation of the changes? - [ ] Have I provided a clear explanation of the changes?
- [ ] Have I considered writing automated tests for the issue? - [ ] 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)? - [ ] Have I tested the changes (manually)?
- [ ] Are all unit tests still passing? (`npm run test`) - [ ] Are all unit tests still passing? (`npm run test`)
- [ ] Are the changes visual? - [ ] Are the changes visual?

View File

@ -81,6 +81,7 @@ Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to
- kyledove - kyledove
- Brumirage - Brumirage
- pkmn_realidea (Paid Commissions) - pkmn_realidea (Paid Commissions)
- IceJkai
### 🎨 Trainer Portraits ### 🎨 Trainer Portraits
- pkmn_realidea (Paid Commissions) - pkmn_realidea (Paid Commissions)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -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$"
}
}

View File

@ -1,7 +1,7 @@
{ {
"textures": [ "textures": [
{ {
"image": "aqua_admin_m.png", "image": "archer.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 80, "w": 80,

View File

Before

Width:  |  Height:  |  Size: 645 B

After

Width:  |  Height:  |  Size: 645 B

View File

@ -1,7 +1,7 @@
{ {
"textures": [ "textures": [
{ {
"image": "rocket_admin_m.png", "image": "ariana.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 80, "w": 80,

View File

Before

Width:  |  Height:  |  Size: 736 B

After

Width:  |  Height:  |  Size: 736 B

View File

@ -1,7 +1,7 @@
{ {
"textures": [ "textures": [
{ {
"image": "magma_admin_m.png", "image": "bryony.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 80, "w": 80,

View File

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

View File

@ -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$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -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$"
}
}

View File

@ -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$"
}
}

View File

@ -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$"
}
}

View File

@ -1,7 +1,7 @@
{ {
"textures": [ "textures": [
{ {
"image": "flare_admin_f.png", "image": "jupiter.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 80, "w": 80,

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 847 B

View File

@ -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$"
}
}

View File

@ -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$"
}
}

View File

Before

Width:  |  Height:  |  Size: 621 B

After

Width:  |  Height:  |  Size: 621 B

View File

@ -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$"
}
}

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -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$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -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$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

View File

@ -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$"
}
}

View File

@ -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$"
}
}

View File

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

View File

@ -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$"
}
}

View File

Before

Width:  |  Height:  |  Size: 865 B

After

Width:  |  Height:  |  Size: 865 B

View File

@ -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$"
}
}

View File

Before

Width:  |  Height:  |  Size: 845 B

After

Width:  |  Height:  |  Size: 845 B

View File

@ -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$"
}
}

View File

Before

Width:  |  Height:  |  Size: 789 B

After

Width:  |  Height:  |  Size: 789 B

440
public/images/types_ja.json Normal file
View File

@ -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$"
}
}

BIN
public/images/types_ja.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -176,6 +176,27 @@
"w": 12, "w": 12,
"h": 6 "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
}
} }
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 451 B

View File

@ -281,6 +281,27 @@
"w": 9, "w": 9,
"h": 8 "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
}
} }
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 435 B

View File

@ -176,6 +176,27 @@
"w": 13, "w": 13,
"h": 7 "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
}
} }
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 496 B

View File

@ -281,6 +281,27 @@
"w": 9, "w": 9,
"h": 8 "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
}
} }
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B

After

Width:  |  Height:  |  Size: 499 B

3
src/@types/common.ts Normal file
View File

@ -0,0 +1,3 @@
import BattleScene from "#app/battle-scene.js";
export type ConditionFn = (scene: BattleScene, args?: any[]) => boolean;

View File

@ -8,7 +8,7 @@ export interface UserInfo {
googleId: string; 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 // 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); export const clientSessionId = Utils.randomString(32);
@ -30,11 +30,13 @@ export function updateUserInfo(): Promise<[boolean, integer]> {
loggedInUser.lastSessionSlot = lastSessionSlot; loggedInUser.lastSessionSlot = lastSessionSlot;
// Migrate old data from before the username was appended // Migrate old data from before the username was appended
[ "data", "sessionData", "sessionData1", "sessionData2", "sessionData3", "sessionData4" ].map(d => { [ "data", "sessionData", "sessionData1", "sessionData2", "sessionData3", "sessionData4" ].map(d => {
if (localStorage.hasOwnProperty(d)) { const lsItem = localStorage.getItem(d);
if (localStorage.hasOwnProperty(`${d}_${loggedInUser.username}`)) { if (lsItem && !!loggedInUser?.username) {
localStorage.setItem(`${d}_${loggedInUser.username}_bak`, localStorage.getItem(`${d}_${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); localStorage.removeItem(d);
} }
}); });

View File

@ -106,8 +106,8 @@ export default class BattleScene extends SceneBase {
public inputController: InputsController; public inputController: InputsController;
public uiInputs: UiInputs; public uiInputs: UiInputs;
public sessionPlayTime: integer = null; public sessionPlayTime: integer | null = null;
public lastSavePlayTime: integer = null; public lastSavePlayTime: integer | null = null;
public masterVolume: number = 0.5; public masterVolume: number = 0.5;
public bgmVolume: number = 1; public bgmVolume: number = 1;
public seVolume: 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 enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1";
public enableMoveInfo: boolean = true; public enableMoveInfo: boolean = true;
public enableRetries: boolean = false; public enableRetries: boolean = false;
public hideIvs: boolean = false;
/** /**
* Determines the condition for a notification should be shown for Candy Upgrades * Determines the condition for a notification should be shown for Candy Upgrades
* - 0 = 'Off' * - 0 = 'Off'
@ -192,8 +193,8 @@ export default class BattleScene extends SceneBase {
private phaseQueuePrependSpliceIndex: integer; private phaseQueuePrependSpliceIndex: integer;
private nextCommandPhaseQueue: Phase[]; private nextCommandPhaseQueue: Phase[];
private currentPhase: Phase; private currentPhase: Phase | null;
private standbyPhase: Phase; private standbyPhase: Phase | null;
public field: Phaser.GameObjects.Container; public field: Phaser.GameObjects.Container;
public fieldUI: Phaser.GameObjects.Container; public fieldUI: Phaser.GameObjects.Container;
public charSprite: CharSprite; public charSprite: CharSprite;
@ -213,7 +214,7 @@ export default class BattleScene extends SceneBase {
public score: integer; public score: integer;
public lockModifierTiers: boolean; public lockModifierTiers: boolean;
public trainer: Phaser.GameObjects.Sprite; public trainer: Phaser.GameObjects.Sprite;
public lastEnemyTrainer: Trainer; public lastEnemyTrainer: Trainer | null;
public currentBattle: Battle; public currentBattle: Battle;
public pokeballCounts: PokeballCounts; public pokeballCounts: PokeballCounts;
public money: integer; public money: integer;
@ -251,7 +252,7 @@ export default class BattleScene extends SceneBase {
public spritePipeline: SpritePipeline; public spritePipeline: SpritePipeline;
private bgm: AnySound; private bgm: AnySound;
private bgmResumeTimer: Phaser.Time.TimerEvent; private bgmResumeTimer: Phaser.Time.TimerEvent | null;
private bgmCache: Set<string> = new Set(); private bgmCache: Set<string> = new Set();
private playTimeTimer: Phaser.Time.TimerEvent; private playTimeTimer: Phaser.Time.TimerEvent;
@ -700,7 +701,11 @@ export default class BattleScene extends SceneBase {
hasExpSprite(key: string): boolean { hasExpSprite(key: string): boolean {
const keyMatch = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/g.exec(key); 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]) { if (keyMatch[2]) {
k += "s"; k += "s";
} }
@ -723,7 +728,7 @@ export default class BattleScene extends SceneBase {
return this.party; return this.party;
} }
getPlayerPokemon(): PlayerPokemon { getPlayerPokemon(): PlayerPokemon | undefined {
return this.getPlayerField().find(p => p.isActive()); return this.getPlayerField().find(p => p.isActive());
} }
@ -740,7 +745,7 @@ export default class BattleScene extends SceneBase {
return this.currentBattle?.enemyParty || []; return this.currentBattle?.enemyParty || [];
} }
getEnemyPokemon(): EnemyPokemon { getEnemyPokemon(): EnemyPokemon | undefined {
return this.getEnemyField().find(p => p.isActive()); return this.getEnemyField().find(p => p.isActive());
} }
@ -764,6 +769,27 @@ export default class BattleScene extends SceneBase {
: ret; : 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 the ModifierBar of this scene, which is declared private and therefore not accessible elsewhere
* @returns {ModifierBar} * @returns {ModifierBar}
@ -782,12 +808,12 @@ export default class BattleScene extends SceneBase {
return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles; 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); 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); const pokemon = new PlayerPokemon(this, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource);
if (postProcess) { if (postProcess) {
postProcess(pokemon); postProcess(pokemon);
@ -957,7 +983,8 @@ export default class BattleScene extends SceneBase {
p.destroy(); 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.setText(startingWave.toString());
this.biomeWaveText.setVisible(false); 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 _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1); const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
let newDouble: boolean; let newDouble: boolean | undefined;
let newBattleType: BattleType; let newBattleType: BattleType;
let newTrainer: Trainer; let newTrainer: Trainer | undefined;
let battleConfig: FixedBattleConfig = null; let battleConfig: FixedBattleConfig | null = null;
this.resetSeed(newWaveIndex); this.resetSeed(newWaveIndex);
@ -1038,7 +1065,7 @@ export default class BattleScene extends SceneBase {
battleConfig = this.gameMode.getFixedBattle(newWaveIndex); battleConfig = this.gameMode.getFixedBattle(newWaveIndex);
newDouble = battleConfig.double; newDouble = battleConfig.double;
newBattleType = battleConfig.battleType; 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) { if (newTrainer) {
this.field.add(newTrainer); this.field.add(newTrainer);
} }
@ -1078,7 +1105,7 @@ export default class BattleScene extends SceneBase {
playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance));
newDouble = !Utils.randSeedInt(doubleChance.value); newDouble = !Utils.randSeedInt(doubleChance.value);
} else if (newBattleType === BattleType.TRAINER) { } else if (newBattleType === BattleType.TRAINER) {
newDouble = newTrainer.variant === TrainerVariant.DOUBLE; newDouble = newTrainer?.variant === TrainerVariant.DOUBLE;
} }
} else if (!battleConfig) { } else if (!battleConfig) {
newDouble = !!double; 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)); //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) { if (!waveIndex && lastBattle) {
let isNewBiome = !(lastBattle.waveIndex % 10) || ((this.gameMode.hasShortBiomes || this.gameMode.isDaily) && (lastBattle.waveIndex % 50) === 49); const isWaveIndexMultipleOfTen = !(lastBattle.waveIndex % 10);
if (!isNewBiome && this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 10) < 9) { const isEndlessOrDaily = this.gameMode.hasShortBiomes || this.gameMode.isDaily;
let w = lastBattle.waveIndex - ((lastBattle.waveIndex % 10) - 1); const isEndlessFifthWave = this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 5) === 0;
let biomeWaves = 1; const isWaveIndexMultipleOfFiftyMinusOne = (lastBattle.waveIndex % 50) === 49;
while (w < lastBattle.waveIndex) { const isNewBiome = isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne);
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 resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS; const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy()); this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
this.trySpreadPokerus(); this.trySpreadPokerus();
@ -1309,7 +1320,7 @@ export default class BattleScene extends SceneBase {
return 5; return 5;
} }
let isBoss: boolean; let isBoss: boolean | undefined;
if (forceBoss || (species && (species.subLegendary || species.legendary || species.mythical))) { if (forceBoss || (species && (species.subLegendary || species.legendary || species.mythical))) {
isBoss = true; isBoss = true;
} else { } else {
@ -1608,7 +1619,7 @@ export default class BattleScene extends SceneBase {
randomSpecies(waveIndex: integer, level: integer, fromArenaPool?: boolean, speciesFilter?: PokemonSpeciesFilter, filterAllEvolutions?: boolean): PokemonSpecies { randomSpecies(waveIndex: integer, level: integer, fromArenaPool?: boolean, speciesFilter?: PokemonSpeciesFilter, filterAllEvolutions?: boolean): PokemonSpecies {
if (fromArenaPool) { 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 => { const filteredSpecies = speciesFilter ? [...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => {
if (!filterAllEvolutions) { if (!filterAllEvolutions) {
@ -1792,8 +1803,10 @@ export default class BattleScene extends SceneBase {
return 13.950; return 13.950;
case "battle_johto_champion": //B2W2 Johto Champion Battle case "battle_johto_champion": //B2W2 Johto Champion Battle
return 23.498; return 23.498;
case "battle_hoenn_champion": //B2W2 Hoenn Champion Battle case "battle_hoenn_champion_g5": //B2W2 Hoenn Champion Battle
return 11.328; return 11.328;
case "battle_hoenn_champion_g6": //ORAS Hoenn Champion Battle
return 11.762;
case "battle_sinnoh_champion": //B2W2 Sinnoh Champion Battle case "battle_sinnoh_champion": //B2W2 Sinnoh Champion Battle
return 12.235; return 12.235;
case "battle_champion_alder": //BW Unova Champion Battle case "battle_champion_alder": //BW Unova Champion Battle
@ -1964,11 +1977,11 @@ export default class BattleScene extends SceneBase {
} }
/* Phase Functions */ /* Phase Functions */
getCurrentPhase(): Phase { getCurrentPhase(): Phase | null {
return this.currentPhase; return this.currentPhase;
} }
getStandbyPhase(): Phase { getStandbyPhase(): Phase | null {
return this.standbyPhase; return this.standbyPhase;
} }
@ -2046,7 +2059,10 @@ export default class BattleScene extends SceneBase {
} }
if (this.phaseQueuePrepend.length) { if (this.phaseQueuePrepend.length) {
while (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) { 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 // Clear the conditionalQueue if there are no phases left in the phaseQueue
this.conditionalQueue = []; this.conditionalQueue = [];
} }
this.currentPhase = this.phaseQueue.shift();
this.currentPhase = this.phaseQueue.shift() ?? null;
// Check if there are any conditional phases queued // Check if there are any conditional phases queued
if (this.conditionalQueue?.length) { if (this.conditionalQueue?.length) {
// Retrieve the first conditional phase from the queue // Retrieve the first conditional phase from the queue
const conditionalPhase = this.conditionalQueue.shift(); const conditionalPhase = this.conditionalQueue.shift();
// Evaluate the condition associated with the phase // 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 // If the condition is met, add the phase to the phase queue
this.pushPhase(conditionalPhase[1]); 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 // If the condition is not met, re-add the phase back to the front of the conditional queue
this.conditionalQueue.unshift(conditionalPhase); this.conditionalQueue.unshift(conditionalPhase);
} else {
console.warn("condition phase is undefined/null!", conditionalPhase);
} }
} }
this.currentPhase.start(); this.currentPhase?.start();
} }
overridePhase(phase: Phase): boolean { overridePhase(phase: Phase): boolean {
@ -2085,7 +2104,7 @@ export default class BattleScene extends SceneBase {
return true; return true;
} }
findPhase(phaseFilter: (phase: Phase) => boolean): Phase { findPhase(phaseFilter: (phase: Phase) => boolean): Phase | undefined {
return this.phaseQueue.find(phaseFilter); return this.phaseQueue.find(phaseFilter);
} }
@ -2144,7 +2163,7 @@ export default class BattleScene extends SceneBase {
* @param promptDelay optional param for MessagePhase constructor * @param promptDelay optional param for MessagePhase constructor
* @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue * @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); const phase = new MessagePhase(this, message, callbackDelay, prompt, promptDelay);
if (!defer) { if (!defer) {
// adds to the end of PhaseQueuePrepend // adds to the end of PhaseQueuePrepend
@ -2180,7 +2199,10 @@ export default class BattleScene extends SceneBase {
return Math.floor(moneyValue / 10) * 10; return Math.floor(moneyValue / 10) * 10;
} }
addModifier(modifier: Modifier, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean): Promise<boolean> { addModifier(modifier: Modifier | null, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean): Promise<boolean> {
if (!modifier) {
return Promise.resolve(false);
}
return new Promise(resolve => { return new Promise(resolve => {
let success = false; let success = false;
const soundName = modifier.type.soundName; const soundName = modifier.type.soundName;
@ -2200,7 +2222,7 @@ export default class BattleScene extends SceneBase {
} }
} else if (!virtual) { } else if (!virtual) {
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier); 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)); 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 => { return new Promise(resolve => {
const source = itemModifier.pokemonId ? itemModifier.getPokemon(target.scene) : null; const source = itemModifier.pokemonId ? itemModifier.getPokemon(target.scene) : null;
const cancelled = new Utils.BooleanHolder(false); 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) { if (cancelled.value) {
return resolve(false); return resolve(false);
} }
@ -2381,7 +2403,7 @@ export default class BattleScene extends SceneBase {
} }
party.forEach((enemyPokemon: EnemyPokemon, i: integer) => { 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; let upgradeChance = 32;
if (isBoss) { if (isBoss) {
upgradeChance /= 2; upgradeChance /= 2;
@ -2389,9 +2411,9 @@ export default class BattleScene extends SceneBase {
if (isFinalBoss) { if (isFinalBoss) {
upgradeChance /= 8; upgradeChance /= 8;
} }
const modifierChance = this.gameMode.getEnemyModifierChance(isBoss); const modifierChance = this.gameMode.getEnemyModifierChance(isBoss!); // TODO: is this bang correct?
let pokemonModifierChance = modifierChance; 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 pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); // eslint-disable-line
let count = 0; let count = 0;
for (let c = 0; c < chances; c++) { 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)); 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)); return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m));
} }
@ -2546,7 +2568,7 @@ export default class BattleScene extends SceneBase {
return appliedModifiers; return appliedModifiers;
} }
applyModifier(modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier { applyModifier(modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier | null {
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (modifier.apply(args)) { if (modifier.apply(args)) {
@ -2633,7 +2655,7 @@ export default class BattleScene extends SceneBase {
initFinalBossPhaseTwo(pokemon: Pokemon): void { initFinalBossPhaseTwo(pokemon: Pokemon): void {
if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) { if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
this.fadeOutBgm(Utils.fixedInt(2000), false); 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); this.addEnemyModifier(getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as PersistentModifier, false, true);
pokemon.generateAndPopulateMoveset(1); pokemon.generateAndPopulateMoveset(1);
this.setFieldScale(0.75); this.setFieldScale(0.75);

View File

@ -39,7 +39,7 @@ export interface TurnCommand {
} }
interface TurnCommands { interface TurnCommands {
[key: integer]: TurnCommand [key: integer]: TurnCommand | null
} }
export default class Battle { export default class Battle {
@ -47,8 +47,8 @@ export default class Battle {
public waveIndex: integer; public waveIndex: integer;
public battleType: BattleType; public battleType: BattleType;
public battleSpec: BattleSpec; public battleSpec: BattleSpec;
public trainer: Trainer; public trainer: Trainer | null;
public enemyLevels: integer[]; public enemyLevels: integer[] | undefined;
public enemyParty: EnemyPokemon[]; public enemyParty: EnemyPokemon[];
public seenEnemyPartyMemberIds: Set<integer>; public seenEnemyPartyMemberIds: Set<integer>;
public double: boolean; public double: boolean;
@ -62,26 +62,26 @@ export default class Battle {
public escapeAttempts: integer; public escapeAttempts: integer;
public lastMove: Moves; public lastMove: Moves;
public battleSeed: string; public battleSeed: string;
private battleSeedState: string; private battleSeedState: string | null;
public moneyScattered: number; 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 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 public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted
private rngCounter: integer = 0; 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.gameMode = gameMode;
this.waveIndex = waveIndex; this.waveIndex = waveIndex;
this.battleType = battleType; this.battleType = battleType;
this.trainer = trainer; this.trainer = trainer!; //TODO: is this bang correct?
this.initBattleSpec(); this.initBattleSpec();
this.enemyLevels = battleType !== BattleType.TRAINER this.enemyLevels = battleType !== BattleType.TRAINER
? new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave()) ? new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave())
: trainer.getPartyLevels(this.waveIndex); : trainer?.getPartyLevels(this.waveIndex);
this.enemyParty = []; this.enemyParty = [];
this.seenEnemyPartyMemberIds = new Set<integer>(); this.seenEnemyPartyMemberIds = new Set<integer>();
this.double = double; this.double = double!; //TODO: is this bang correct?
this.enemySwitchCounter = 0; this.enemySwitchCounter = 0;
this.turn = 0; this.turn = 0;
this.playerParticipantIds = new Set<integer>(); this.playerParticipantIds = new Set<integer>();
@ -159,6 +159,7 @@ export default class Battle {
addPostBattleLoot(enemyPokemon: EnemyPokemon): void { addPostBattleLoot(enemyPokemon: EnemyPokemon): void {
this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferrable, false).map(i => { this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferrable, false).map(i => {
const ret = i as PokemonHeldItemModifier; const ret = i as PokemonHeldItemModifier;
//@ts-ignore - this is awful to fix/change
ret.pokemonId = null; ret.pokemonId = null;
return ret; return ret;
})); }));
@ -177,7 +178,7 @@ export default class Battle {
const userLocale = navigator.language || "en-US"; const userLocale = navigator.language || "en-US";
const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale); const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale);
const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount }); const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount });
scene.queueMessage(message, null, true); scene.queueMessage(message, undefined, true);
scene.currentBattle.moneyScattered = 0; scene.currentBattle.moneyScattered = 0;
} }
@ -200,16 +201,16 @@ export default class Battle {
scene.updateScoreText(); scene.updateScoreText();
} }
getBgmOverride(scene: BattleScene): string { getBgmOverride(scene: BattleScene): string | null {
const battlers = this.enemyParty.slice(0, this.getBattlerCount()); const battlers = this.enemyParty.slice(0, this.getBattlerCount());
if (this.battleType === BattleType.TRAINER) { if (this.battleType === BattleType.TRAINER) {
if (!this.started && this.trainer.config.encounterBgm && this.trainer.getEncounterMessages()?.length) { if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
return `encounter_${this.trainer.getEncounterBgm()}`; return `encounter_${this.trainer?.getEncounterBgm()}`;
} }
if (scene.musicPreference === 0) { if (scene.musicPreference === 0) {
return this.trainer.getBattleBgm(); return this.trainer?.getBattleBgm()!; // TODO: is this bang correct?
} else { } 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) { } else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) {
return "end_summit"; return "end_summit";
@ -382,7 +383,7 @@ export default class Battle {
export class FixedBattle extends Battle { export class FixedBattle extends Battle {
constructor(scene: BattleScene, waveIndex: integer, config: FixedBattleConfig) { 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) { if (config.getEnemyParty) {
this.enemyParty = config.getEnemyParty(scene); 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 * 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 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 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 * @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) => { return (scene: BattleScene) => {
const rand = Utils.randSeedInt(trainerPool.length); const rand = Utils.randSeedInt(trainerPool.length);
const trainerTypes: TrainerType[] = []; const trainerTypes: TrainerType[] = [];
scene.executeWithSeedOffset(() => {
for (const trainerPoolEntry of trainerPool) { for (const trainerPoolEntry of trainerPool) {
const trainerType = Array.isArray(trainerPoolEntry) const trainerType = Array.isArray(trainerPoolEntry)
? Utils.randSeedItem(trainerPoolEntry) ? Utils.randSeedItem(trainerPoolEntry)
: trainerPoolEntry; : trainerPoolEntry;
trainerTypes.push(trainerType); trainerTypes.push(trainerType);
} }
}, seedOffset);
let trainerGender = TrainerVariant.DEFAULT; let trainerGender = TrainerVariant.DEFAULT;
if (randomGender) { if (randomGender) {
trainerGender = (Utils.randInt(2) === 0) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT; 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) [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)), .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) [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) [95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .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) [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)), .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) [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) [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 ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE ])),
[145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)

View File

@ -20,7 +20,7 @@ export function getKeyWithKeycode(config, keycode) {
*/ */
export function getSettingNameWithKeycode(config, keycode) { export function getSettingNameWithKeycode(config, keycode) {
const key = getKeyWithKeycode(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) { export function getIconWithKeycode(config, keycode) {
const key = getKeyWithKeycode(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 it was already bound, we delete the bind
if (previousSettingName) { if (previousSettingName) {
const previousKey = getKeyWithSettingName(config, previousSettingName); const previousKey = getKeyWithSettingName(config, previousSettingName);
if (previousKey) {
config.custom[previousKey] = -1; config.custom[previousKey] = -1;
} }
}
// then, we need to delete the current key for this settingName // then, we need to delete the current key for this settingName
const currentKey = getKeyWithSettingName(config, settingNameTarget); const currentKey = getKeyWithSettingName(config, settingNameTarget);
if (currentKey) {
config.custom[currentKey] = -1; config.custom[currentKey] = -1;
}
// then, the new key is assigned to the new settingName // then, the new key is assigned to the new settingName
const newKey = getKeyWithKeycode(config, keycode); const newKey = getKeyWithKeycode(config, keycode);
if (newKey) {
config.custom[newKey] = settingNameTarget; config.custom[newKey] = settingNameTarget;
}
return true; return true;
} }
@ -145,8 +151,12 @@ export function swap(config, settingNameTarget, keycode) {
const new_key = getKeyWithKeycode(config, keycode); const new_key = getKeyWithKeycode(config, keycode);
const new_settingName = getSettingNameWithKey(config, new_key); const new_settingName = getSettingNameWithKey(config, new_key);
if (prev_key) {
config.custom[prev_key] = new_settingName; config.custom[prev_key] = new_settingName;
}
if (new_key) {
config.custom[new_key] = prev_settingName; config.custom[new_key] = prev_settingName;
}
return true; return true;
} }
@ -161,7 +171,9 @@ export function deleteBind(config, settingName) {
if (config.blacklist.includes(key)) { if (config.blacklist.includes(key)) {
return false; return false;
} }
if (key) {
config.custom[key] = -1; config.custom[key] = -1;
}
return true; return true;
} }

View File

@ -6,7 +6,7 @@ import { BattleStat, getBattleStatName } from "./battle-stat";
import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases";
import { getPokemonNameWithAffix } from "../messages"; import { getPokemonNameWithAffix } from "../messages";
import { Weather, WeatherType } from "./weather"; 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 { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
import { Gender } from "./gender"; 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"; 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<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>; type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean | Promise<boolean>;
type AbAttrCondition = (pokemon: Pokemon) => boolean; 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 PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
type PokemonStatChangeCondition = (target: Pokemon, statsChanged: BattleStat[], levels: integer) => boolean; type PokemonStatChangeCondition = (target: Pokemon, statsChanged: BattleStat[], levels: integer) => boolean;
@ -132,11 +132,11 @@ export abstract class AbAttr {
this.showAbility = showAbility; this.showAbility = showAbility;
} }
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> { apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null {
return null; return null;
} }
@ -226,7 +226,7 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr {
} }
for (const statChangePhase of statChangePhases) { for (const statChangePhase of statChangePhases) {
if (!this.selfTarget && !statChangePhase.getPokemon().summonData) { if (!this.selfTarget && !statChangePhase.getPokemon()?.summonData) {
pokemon.scene.pushPhase(statChangePhase); pokemon.scene.pushPhase(statChangePhase);
} else { // TODO: This causes the ability bar to be shown at the wrong time } else { // TODO: This causes the ability bar to be shown at the wrong time
pokemon.scene.unshiftPhase(statChangePhase); pokemon.scene.unshiftPhase(statChangePhase);
@ -240,7 +240,7 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr {
type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean; type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean;
export class PreDefendAbAttr extends AbAttr { export class PreDefendAbAttr extends AbAttr {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> { applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
} }
@ -352,14 +352,14 @@ export class PreDefendMoveDamageToOneAbAttr extends ReceivedMoveDamageMultiplier
* @see {@linkcode getCondition} * @see {@linkcode getCondition}
*/ */
export class TypeImmunityAbAttr extends PreDefendAbAttr { export class TypeImmunityAbAttr extends PreDefendAbAttr {
private immuneType: Type; private immuneType: Type | null;
private condition: AbAttrCondition; private condition: AbAttrCondition | null;
constructor(immuneType: Type, condition?: AbAttrCondition) { constructor(immuneType: Type | null, condition?: AbAttrCondition) {
super(); super();
this.immuneType = immuneType; 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; return false;
} }
getCondition(): AbAttrCondition { override getCondition(): AbAttrCondition | null {
return this.condition; return this.condition;
} }
} }
@ -491,11 +491,54 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
} }
export class PostDefendAbAttr extends AbAttr { export class PostDefendAbAttr extends AbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise<boolean> {
return false; 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<boolean> {
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 { export class PostDefendDisguiseAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { 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 { export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
private chance: integer; private chance: integer;
private tagType: BattlerTagType; private tagType: BattlerTagType;
private turnCount: integer; private turnCount: integer | undefined;
constructor(chance: integer, tagType: BattlerTagType, turnCount?: integer) { constructor(chance: integer, tagType: BattlerTagType, turnCount?: integer) {
super(); super();
@ -875,7 +918,7 @@ export class PostDefendCritStatChangeAbAttr extends PostDefendAbAttr {
} }
getCondition(): AbAttrCondition { 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 { export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
private weatherType: WeatherType; private weatherType: WeatherType;
protected condition: PokemonDefendCondition | null;
constructor(weatherType: WeatherType) { constructor(weatherType: WeatherType, condition?: PokemonDefendCondition) {
super(); super();
this.weatherType = weatherType; this.weatherType = weatherType;
this.condition = condition ?? null;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { 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()) { if (!pokemon.scene.arena.weather?.isImmutable()) {
return pokemon.scene.arena.trySetWeather(this.weatherType, true); return pokemon.scene.arena.trySetWeather(this.weatherType, true);
} }
@ -1061,7 +1109,7 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr {
} }
export class PreAttackAbAttr extends AbAttr { export class PreAttackAbAttr extends AbAttr {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> { applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
} }
@ -1074,7 +1122,7 @@ export class PreAttackAbAttr extends AbAttr {
export class MoveEffectChanceMultiplierAbAttr extends AbAttr { export class MoveEffectChanceMultiplierAbAttr extends AbAttr {
private chanceMultiplier: number; private chanceMultiplier: number;
constructor(chanceMultiplier?: number) { constructor(chanceMultiplier: number) {
super(true); super(true);
this.chanceMultiplier = chanceMultiplier; this.chanceMultiplier = chanceMultiplier;
} }
@ -1455,7 +1503,7 @@ export class FieldMovePowerBoostAbAttr extends AbAttr {
this.powerMultiplier = powerMultiplier; 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)) { if (this.condition(pokemon, defender, move)) {
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier; (args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
@ -1509,14 +1557,14 @@ export class AllyMoveCategoryPowerBoostAbAttr extends FieldMovePowerBoostAbAttr
export class BattleStatMultiplierAbAttr extends AbAttr { export class BattleStatMultiplierAbAttr extends AbAttr {
private battleStat: BattleStat; private battleStat: BattleStat;
private multiplier: number; private multiplier: number;
private condition: PokemonAttackCondition; private condition: PokemonAttackCondition | null;
constructor(battleStat: BattleStat, multiplier: number, condition?: PokemonAttackCondition) { constructor(battleStat: BattleStat, multiplier: number, condition?: PokemonAttackCondition) {
super(false); super(false);
this.battleStat = battleStat; this.battleStat = battleStat;
this.multiplier = multiplier; 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<boolean> { applyBattleStat(pokemon: Pokemon, passive: boolean, battleStat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise<boolean> {
@ -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} * 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. * 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<boolean> { applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise<boolean> {
// When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. // 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 attackRequired is false, we always defer to the secondary requirements.
if (this.attackCondition(pokemon, defender, move)) { 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. * 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<boolean> { applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
} }
export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
private stealCondition: PokemonAttackCondition; private stealCondition: PokemonAttackCondition | null;
constructor(stealCondition?: PokemonAttackCondition) { constructor(stealCondition?: PokemonAttackCondition) {
super(); 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<boolean> { applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
@ -1653,12 +1701,12 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
} }
export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
private condition: PokemonDefendCondition; private condition: PokemonDefendCondition | null;
constructor(condition?: PokemonDefendCondition) { constructor(condition?: PokemonDefendCondition) {
super(); 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<boolean> { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
@ -2153,17 +2201,17 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr {
} }
if ( if (
target.getAbility().hasAttr(UncopiableAbilityAbAttr) && target!.getAbility().hasAttr(UncopiableAbilityAbAttr) &&
// Wonder Guard is normally uncopiable so has the attribute, but Trace specifically can copy it // 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; return false;
} }
this.target = target; this.target = target!;
this.targetAbilityName = allAbilities[target.getAbility().id].name; this.targetAbilityName = allAbilities[target!.getAbility().id].name;
pokemon.summonData.ability = target.getAbility().id; pokemon.summonData.ability = target!.getAbility().id;
setAbilityRevealed(target); setAbilityRevealed(target!);
pokemon.updateInfo(); pokemon.updateInfo();
return true; return true;
@ -2210,7 +2258,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt
} }
for (const pokemon of allowedParty) { 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.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)));
pokemon.resetStatus(false); pokemon.resetStatus(false);
pokemon.updateInfo(); pokemon.updateInfo();
@ -2259,17 +2307,18 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
return false; return false;
} }
let target: Pokemon; let target: Pokemon = targets[0];
if (targets.length > 1) { if (targets.length > 1) {
pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex); pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex);
} else { } else if (targets.length === 1) {
target = targets[0]; target = targets[0];
} else {
return false;
} }
if (target.illusion.active) { if (target.illusion.active) {
return false; return false;
} }
pokemon.summonData.speciesForm = target.getSpeciesForm(); pokemon.summonData.speciesForm = target.getSpeciesForm();
pokemon.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); pokemon.summonData.fusionSpeciesForm = target.getFusionSpeciesForm();
pokemon.summonData.ability = target.getAbility().id; pokemon.summonData.ability = target.getAbility().id;
@ -2277,7 +2326,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
pokemon.summonData.fusionGender = target.getFusionGender(); pokemon.summonData.fusionGender = target.getFusionGender();
pokemon.summonData.stats = [ pokemon.stats[Stat.HP] ].concat(target.stats.slice(1)); pokemon.summonData.stats = [ pokemon.stats[Stat.HP] ].concat(target.stats.slice(1));
pokemon.summonData.battleStats = target.summonData.battleStats.slice(0); 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.summonData.types = target.getTypes();
pokemon.scene.playSound("PRSFX- Transform"); pokemon.scene.playSound("PRSFX- Transform");
@ -2324,7 +2373,7 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
* @returns {boolean} Returns true if the weather clears, otherwise false. * @returns {boolean} Returns true if the weather clears, otherwise false.
*/ */
applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> { applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
const weatherType = pokemon.scene.arena.weather.weatherType; const weatherType = pokemon.scene.arena.weather?.weatherType;
let turnOffWeather = false; let turnOffWeather = false;
// Clear weather only if user's ability matches the weather and no other pokemon has the ability. // 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 { export class PreStatChangeAbAttr extends AbAttr {
applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> { applyPreStatChange(pokemon: Pokemon | null, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
} }
export class ProtectStatAbAttr extends PreStatChangeAbAttr { export class ProtectStatAbAttr extends PreStatChangeAbAttr {
private protectedStat: BattleStat; private protectedStat: BattleStat | null;
constructor(protectedStat?: BattleStat) { constructor(protectedStat?: BattleStat) {
super(); 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 { 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", { return i18next.t("abilityTriggers:protectStat", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
abilityName, 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 { export class PreSetStatusAbAttr extends AbAttr {
applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> { applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
} }
@ -2683,7 +2732,7 @@ export class BlockStatusDamageAbAttr extends AbAttr {
* @returns Returns true if status damage is blocked * @returns Returns true if status damage is blocked
*/ */
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { 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; cancelled.value = true;
return true; return true;
} }
@ -2722,7 +2771,7 @@ export class IncrementMovePriorityAbAttr extends AbAttr {
export class IgnoreContactAbAttr extends AbAttr { } export class IgnoreContactAbAttr extends AbAttr { }
export class PreWeatherEffectAbAttr extends AbAttr { export class PreWeatherEffectAbAttr extends AbAttr {
applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> { applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather | null, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
} }
@ -2753,7 +2802,7 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr {
constructor(affectsImmutable?: boolean) { constructor(affectsImmutable?: boolean) {
super(); 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 { applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean {
@ -2804,7 +2853,7 @@ function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition {
return false; return false;
} }
const weatherType = pokemon.scene.arena.weather?.weatherType; 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 opponent of pokemon.getOpponents()) {
for (const move of opponent.moveset) { for (const move of opponent.moveset) {
// move is super effective // 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; return true;
} }
// move is a OHKO // move is a OHKO
if (move.getMove().hasAttr(OneHitKOAttr)) { if (move!.getMove().hasAttr(OneHitKOAttr)) { // TODO: is this bang correct?
return true; return true;
} }
// edge case for hidden power, type is computed // 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) const iv_val = Math.floor(((opponent.ivs[Stat.HP] & 1)
+(opponent.ivs[Stat.ATK] & 1) * 2 +(opponent.ivs[Stat.ATK] & 1) * 2
+(opponent.ivs[Stat.DEF] & 1) * 4 +(opponent.ivs[Stat.DEF] & 1) * 4
@ -2869,21 +2918,21 @@ export class ForewarnAbAttr extends PostSummonAbAttr {
let movePower = 0; let movePower = 0;
for (const opponent of pokemon.getOpponents()) { for (const opponent of pokemon.getOpponents()) {
for (const move of opponent.moveset) { for (const move of opponent.moveset) {
if (move.getMove() instanceof StatusMove) { if (move!.getMove() instanceof StatusMove) { // TODO: is this bang correct?
movePower = 1; movePower = 1;
} else if (move.getMove().hasAttr(OneHitKOAttr)) { } else if (move!.getMove().hasAttr(OneHitKOAttr)) { // TODO: is this bang correct?
movePower = 150; 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; movePower = 120;
} else if (move.getMove().power === -1) { } else if (move!.getMove().power === -1) { // TODO: is this bang correct?
movePower = 80; movePower = 80;
} else { } else {
movePower = move.getMove().power; movePower = move!.getMove().power; // TODO: is this bang correct?
} }
if (movePower > maxPowerSeen) { if (movePower > maxPowerSeen) {
maxPowerSeen = movePower; 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; this.weatherTypes = weatherTypes;
} }
applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather, args: any[]): boolean | Promise<boolean> { applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather | null, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
@ -3027,7 +3076,7 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr
function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition {
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
const terrainType = pokemon.scene.arena.terrain?.terrainType; 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 * @returns Returns true if healed from status, false if not
*/ */
applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> { applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
if (this.effects.includes(pokemon.status?.effect)) { if (pokemon.status && this.effects.includes(pokemon.status.effect)) {
if (!pokemon.isFullHp()) { if (!pokemon.isFullHp()) {
const scene = pokemon.scene; const scene = pokemon.scene;
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; 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 { applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
const lastUsed = pokemon.scene.currentBattle.lastUsedPokeball; const lastUsed = pokemon.scene.currentBattle.lastUsedPokeball;
if (lastUsed !== null && pokemon.isPlayer) { if (lastUsed !== null && !!pokemon.isPlayer) {
pokemon.scene.pokeballCounts[lastUsed]++; pokemon.scene.pokeballCounts[lastUsed]++;
pokemon.scene.currentBattle.lastUsedPokeball = null; pokemon.scene.currentBattle.lastUsedPokeball = null;
pokemon.scene.queueMessage(i18next.t("abilityTriggers:fetchBall", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokeballName: getPokeballName(lastUsed) })); 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; const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot;
if (postBattleLoot.length) { if (postBattleLoot.length) {
const randItem = Utils.randSeedItem(postBattleLoot); 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); postBattleLoot.splice(postBattleLoot.indexOf(randItem), 1);
pokemon.scene.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: randItem.type.name })); pokemon.scene.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: randItem.type.name }));
return true; return true;
@ -3608,7 +3658,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr {
* @returns {boolean} Returns true if the weather clears, otherwise false. * @returns {boolean} Returns true if the weather clears, otherwise false.
*/ */
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { 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; let turnOffWeather = false;
// Clear weather only if user's ability matches the weather and no other pokemon has the ability. // 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 = const turnCommand =
pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
const isCommandFight = turnCommand?.command === Command.FIGHT; 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; const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL;
if (isCommandFight && isDamageMove) { if (isCommandFight && isDamageMove) {
@ -4190,14 +4240,14 @@ export class BypassSpeedChanceAbAttr extends AbAttr {
async function applyAbAttrsInternal<TAttr extends AbAttr>( async function applyAbAttrsInternal<TAttr extends AbAttr>(
attrType: Constructor<TAttr>, attrType: Constructor<TAttr>,
pokemon: Pokemon, pokemon: Pokemon | null,
applyFunc: AbAttrApplyFunc<TAttr>, applyFunc: AbAttrApplyFunc<TAttr>,
args: any[], args: any[],
showAbilityInstant: boolean = false, showAbilityInstant: boolean = false,
quiet: boolean = false, quiet: boolean = false,
) { ) {
for (const passive of [false, true]) { for (const passive of [false, true]) {
if (!pokemon.canApplyAbility(passive)) { if (!pokemon?.canApplyAbility(passive)) {
continue; continue;
} }
@ -4245,7 +4295,7 @@ async function applyAbAttrsInternal<TAttr extends AbAttr>(
} }
} }
export function applyAbAttrs(attrType: Constructor<AbAttr>, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> { export function applyAbAttrs(attrType: Constructor<AbAttr>, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<AbAttr>(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, cancelled, args), args); return applyAbAttrsInternal<AbAttr>(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, cancelled, args), args);
} }
@ -4255,13 +4305,13 @@ export function applyPostBattleInitAbAttrs(attrType: Constructor<PostBattleInitA
} }
export function applyPreDefendAbAttrs(attrType: Constructor<PreDefendAbAttr>, export function applyPreDefendAbAttrs(attrType: Constructor<PreDefendAbAttr>,
pokemon: Pokemon, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> { pokemon: Pokemon, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise<void> {
const simulated = args.length > 1 && args[1]; const simulated = args.length > 1 && args[1];
return applyAbAttrsInternal<PreDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, simulated); return applyAbAttrsInternal<PreDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, simulated);
} }
export function applyPostDefendAbAttrs(attrType: Constructor<PostDefendAbAttr>, export function applyPostDefendAbAttrs(attrType: Constructor<PostDefendAbAttr>,
pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> { pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args); return applyAbAttrsInternal<PostDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args);
} }
@ -4291,12 +4341,12 @@ export function applyFieldBattleStatMultiplierAbAttrs(attrType: Constructor<Fiel
} }
export function applyPreAttackAbAttrs(attrType: Constructor<PreAttackAbAttr>, export function applyPreAttackAbAttrs(attrType: Constructor<PreAttackAbAttr>,
pokemon: Pokemon, defender: Pokemon, move: Move, ...args: any[]): Promise<void> { pokemon: Pokemon, defender: Pokemon | null, move: Move, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PreAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args); return applyAbAttrsInternal<PreAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args);
} }
export function applyPostAttackAbAttrs(attrType: Constructor<PostAttackAbAttr>, export function applyPostAttackAbAttrs(attrType: Constructor<PostAttackAbAttr>,
pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> { pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, defender, move, hitResult, args), args); return applyAbAttrsInternal<PostAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, defender, move, hitResult, args), args);
} }
@ -4326,7 +4376,7 @@ export function applyPreSwitchOutAbAttrs(attrType: Constructor<PreSwitchOutAbAtt
} }
export function applyPreStatChangeAbAttrs(attrType: Constructor<PreStatChangeAbAttr>, export function applyPreStatChangeAbAttrs(attrType: Constructor<PreStatChangeAbAttr>,
pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> { pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PreStatChangeAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args); return applyAbAttrsInternal<PreStatChangeAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args);
} }
@ -4336,7 +4386,7 @@ export function applyPostStatChangeAbAttrs(attrType: Constructor<PostStatChangeA
} }
export function applyPreSetStatusAbAttrs(attrType: Constructor<PreSetStatusAbAttr>, export function applyPreSetStatusAbAttrs(attrType: Constructor<PreSetStatusAbAttr>,
pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> { pokemon: Pokemon, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
const simulated = args.length > 1 && args[1]; const simulated = args.length > 1 && args[1];
return applyAbAttrsInternal<PreSetStatusAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, effect, cancelled, args), args, false, !simulated); return applyAbAttrsInternal<PreSetStatusAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, effect, cancelled, args), args, false, !simulated);
} }
@ -4347,7 +4397,7 @@ export function applyPreApplyBattlerTagAbAttrs(attrType: Constructor<PreApplyBat
} }
export function applyPreWeatherEffectAbAttrs(attrType: Constructor<PreWeatherEffectAbAttr>, export function applyPreWeatherEffectAbAttrs(attrType: Constructor<PreWeatherEffectAbAttr>,
pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> { pokemon: Pokemon, weather: Weather | null, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PreWeatherDamageAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, weather, cancelled, args), args, true); return applyAbAttrsInternal<PreWeatherDamageAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, weather, cancelled, args), args, true);
} }
@ -4362,7 +4412,7 @@ export function applyPostWeatherChangeAbAttrs(attrType: Constructor<PostWeatherC
} }
export function applyPostWeatherLapseAbAttrs(attrType: Constructor<PostWeatherLapseAbAttr>, export function applyPostWeatherLapseAbAttrs(attrType: Constructor<PostWeatherLapseAbAttr>,
pokemon: Pokemon, weather: Weather, ...args: any[]): Promise<void> { pokemon: Pokemon, weather: Weather | null, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostWeatherLapseAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostWeatherLapse(pokemon, passive, weather, args), args); return applyAbAttrsInternal<PostWeatherLapseAbAttr>(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) .attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPD, 1)
.ignorable(), .ignorable(),
new Ability(Abilities.RIVALRY, 4) 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, 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, 0.75),
new Ability(Abilities.STEADFAST, 4) new Ability(Abilities.STEADFAST, 4)
.attr(FlinchStatChangeAbAttr, BattleStat.SPD, 1), .attr(FlinchStatChangeAbAttr, BattleStat.SPD, 1),
new Ability(Abilities.SNOW_CLOAK, 4) new Ability(Abilities.SNOW_CLOAK, 4)
@ -4742,7 +4792,8 @@ export function initAbilities() {
.attr(IgnoreOpponentStatChangesAbAttr) .attr(IgnoreOpponentStatChangesAbAttr)
.ignorable(), .ignorable(),
new Ability(Abilities.TINTED_LENS, 4) 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) new Ability(Abilities.FILTER, 4)
.attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75) .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75)
.ignorable(), .ignorable(),
@ -4824,9 +4875,9 @@ export function initAbilities() {
.attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.isFullHp(), 0.5) .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.isFullHp(), 0.5)
.ignorable(), .ignorable(),
new Ability(Abilities.TOXIC_BOOST, 5) 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) 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) new Ability(Abilities.HARVEST, 5)
.attr( .attr(
PostTurnLootAbAttr, PostTurnLootAbAttr,
@ -4859,7 +4910,8 @@ export function initAbilities() {
.attr(WonderSkinAbAttr) .attr(WonderSkinAbAttr)
.ignorable(), .ignorable(),
new Ability(Abilities.ANALYTIC, 5) 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) new Ability(Abilities.ILLUSION, 5)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
@ -5017,7 +5069,7 @@ export function initAbilities() {
new Ability(Abilities.WATER_COMPACTION, 7) new Ability(Abilities.WATER_COMPACTION, 7)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, BattleStat.DEF, 2), .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, BattleStat.DEF, 2),
new Ability(Abilities.MERCILESS, 7) 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) new Ability(Abilities.SHIELDS_DOWN, 7)
.attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostBattleInitFormChangeAbAttr, () => 0)
.attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) .attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
@ -5029,7 +5081,8 @@ export function initAbilities() {
.bypassFaint() .bypassFaint()
.partial(), .partial(),
new Ability(Abilities.STAKEOUT, 7) 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) new Ability(Abilities.WATER_BUBBLE, 7)
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
.attr(MoveTypePowerBoostAbAttr, Type.WATER, 2) .attr(MoveTypePowerBoostAbAttr, Type.WATER, 2)
@ -5169,7 +5222,8 @@ export function initAbilities() {
new Ability(Abilities.PRISM_ARMOR, 7) new Ability(Abilities.PRISM_ARMOR, 7)
.attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75), .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75),
new Ability(Abilities.NEUROFORCE, 7) 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) new Ability(Abilities.INTREPID_SWORD, 8)
.attr(PostSummonStatChangeAbAttr, BattleStat.ATK, 1, true) .attr(PostSummonStatChangeAbAttr, BattleStat.ATK, 1, true)
.condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)), .condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)),
@ -5194,7 +5248,11 @@ export function initAbilities() {
.attr(UnsuppressableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.unimplemented(), .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(PostDefendGulpMissileAbAttr)
// Does not transform when Surf/Dive misses/is protected
.partial(),
new Ability(Abilities.STALWART, 8) new Ability(Abilities.STALWART, 8)
.attr(BlockRedirectAbAttr), .attr(BlockRedirectAbAttr),
new Ability(Abilities.STEAM_ENGINE, 8) 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) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5)
.ignorable(), .ignorable(),
new Ability(Abilities.SAND_SPIT, 8) 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) new Ability(Abilities.ICE_SCALES, 8)
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.category === MoveCategory.SPECIAL, 0.5) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.category === MoveCategory.SPECIAL, 0.5)
.ignorable(), .ignorable(),
@ -5252,8 +5310,10 @@ export function initAbilities() {
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.ignorable(), .ignorable(),
new Ability(Abilities.HUNGER_SWITCH, 8) new Ability(Abilities.HUNGER_SWITCH, 8)
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1) //@ts-ignore
.attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0) .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(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)

View File

@ -25,12 +25,12 @@ export enum ArenaTagSide {
export abstract class ArenaTag { export abstract class ArenaTag {
public tagType: ArenaTagType; public tagType: ArenaTagType;
public turnCount: integer; public turnCount: integer;
public sourceMove: Moves; public sourceMove?: Moves;
public sourceId: integer; public sourceId?: integer;
public side: ArenaTagSide; 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.tagType = tagType;
this.turnCount = turnCount; this.turnCount = turnCount;
this.sourceMove = sourceMove; this.sourceMove = sourceMove;
@ -56,7 +56,7 @@ export abstract class ArenaTag {
return this.turnCount < 1 || !!(--this.turnCount); return this.turnCount < 1 || !!(--this.turnCount);
} }
getMoveName(): string { getMoveName(): string | null {
return this.sourceMove return this.sourceMove
? allMoves[this.sourceMove].name ? allMoves[this.sourceMove].name
: null; : null;
@ -75,9 +75,14 @@ export class MistTag extends ArenaTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
if (this.sourceId) {
const source = arena.scene.getPokemonById(this.sourceId); const source = arena.scene.getPokemonById(this.sourceId);
if (!quiet) {
if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(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) { onAdd(arena: Arena) {
if (this.sourceId) {
const source = arena.scene.getPokemonById(this.sourceId); const source = arena.scene.getPokemonById(this.sourceId);
if (source) {
arena.scene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(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}. * 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. * 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 { onAdd(arena: Arena): void {
if (this.sourceId) {
const user = arena.scene.getPokemonById(this.sourceId); const user = arena.scene.getPokemonById(this.sourceId);
if (user) {
this.battlerIndex = user.getBattlerIndex(); this.battlerIndex = user.getBattlerIndex();
this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) });
this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1); this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1);
} else {
console.warn("Failed to get source for WishTag onAdd");
}
}
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
@ -461,8 +511,8 @@ class SpikesTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId); const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null;
if (!quiet) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:spikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); 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 { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId); const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null;
if (!quiet) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:toxicSpikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); 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 { class DelayedAttackTag extends ArenaTag {
public targetIndex: BattlerIndex; 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); super(tagType, 3, sourceMove, sourceId);
this.targetIndex = targetIndex; this.targetIndex = targetIndex;
@ -566,7 +616,7 @@ class DelayedAttackTag extends ArenaTag {
const ret = super.lapse(arena); const ret = super.lapse(arena);
if (!ret) { 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; return ret;
@ -588,8 +638,8 @@ class StealthRockTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId); const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null;
if (!quiet) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor() })); arena.scene.queueMessage(i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -597,7 +647,7 @@ class StealthRockTag extends ArenaTrapTag {
getDamageHpRatio(pokemon: Pokemon): number { getDamageHpRatio(pokemon: Pokemon): number {
const effectiveness = pokemon.getAttackTypeEffectiveness(Type.ROCK, undefined, true); const effectiveness = pokemon.getAttackTypeEffectiveness(Type.ROCK, undefined, true);
let damageHpRatio: number; let damageHpRatio: number = 0;
switch (effectiveness) { switch (effectiveness) {
case 0: case 0:
@ -663,8 +713,8 @@ class StickyWebTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId); const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null;
if (!quiet) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:stickyWebOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); 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 { 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 { 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" : ""}`)); 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 source = arena.scene.getPokemonById(this.sourceId!); //TODO: this bang is questionable!
const party = source.isPlayer() ? source.scene.getPlayerField() : source.scene.getEnemyField(); const party = (source?.isPlayer() ? source.scene.getPlayerField() : source?.scene.getEnemyField()) ?? [];
for (const pokemon of party) { for (const pokemon of party) {
// Apply the CHARGED tag to party members with the WIND_POWER ability // 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) { switch (tagType) {
case ArenaTagType.MIST: case ArenaTagType.MIST:
return new MistTag(turnCount, sourceId, side); return new MistTag(turnCount, sourceId, side);
@ -803,6 +856,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
return new MatBlockTag(sourceId, side); return new MatBlockTag(sourceId, side);
case ArenaTagType.CRAFTY_SHIELD: case ArenaTagType.CRAFTY_SHIELD:
return new CraftyShieldTag(sourceId, side); 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: case ArenaTagType.MUD_SPORT:
return new MudSportTag(turnCount, sourceId); return new MudSportTag(turnCount, sourceId);
case ArenaTagType.WATER_SPORT: case ArenaTagType.WATER_SPORT:
@ -813,7 +868,7 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
return new ToxicSpikesTag(sourceId, side); return new ToxicSpikesTag(sourceId, side);
case ArenaTagType.FUTURE_SIGHT: case ArenaTagType.FUTURE_SIGHT:
case ArenaTagType.DOOM_DESIRE: case ArenaTagType.DOOM_DESIRE:
return new DelayedAttackTag(tagType, sourceMove, sourceId, targetIndex); return new DelayedAttackTag(tagType, sourceMove, sourceId, targetIndex!); // TODO:questionable bang
case ArenaTagType.WISH: case ArenaTagType.WISH:
return new WishTag(turnCount, sourceId, side); return new WishTag(turnCount, sourceId, side);
case ArenaTagType.STEALTH_ROCK: case ArenaTagType.STEALTH_ROCK:
@ -834,5 +889,7 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
return new TailwindTag(turnCount, sourceId, side); return new TailwindTag(turnCount, sourceId, side);
case ArenaTagType.HAPPY_HOUR: case ArenaTagType.HAPPY_HOUR:
return new HappyHourTag(turnCount, sourceId, side); return new HappyHourTag(turnCount, sourceId, side);
default:
return null;
} }
} }

View File

@ -128,7 +128,7 @@ export class AnimConfig {
for (const fte of Object.keys(frameTimedEvents)) { for (const fte of Object.keys(frameTimedEvents)) {
const timedEvents: AnimTimedEvent[] = []; const timedEvents: AnimTimedEvent[] = [];
for (const te of frameTimedEvents[fte]) { for (const te of frameTimedEvents[fte]) {
let timedEvent: AnimTimedEvent; let timedEvent: AnimTimedEvent | undefined;
switch (te.eventType) { switch (te.eventType) {
case "AnimTimedSoundEvent": case "AnimTimedSoundEvent":
timedEvent = new AnimTimedSoundEvent(te.frameIndex, te.resourceName, te); timedEvent = new AnimTimedSoundEvent(te.frameIndex, te.resourceName, te);
@ -140,7 +140,8 @@ export class AnimConfig {
timedEvent = new AnimTimedUpdateBgEvent(te.frameIndex, te.resourceName, te); timedEvent = new AnimTimedUpdateBgEvent(te.frameIndex, te.resourceName, te);
break; break;
} }
timedEvents.push(timedEvent);
timedEvent && timedEvents.push(timedEvent);
} }
this.frameTimedEvents.set(parseInt(fte), timedEvents); 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); return Math.ceil((scene.sound.get(this.resourceName).totalDuration * 1000) / 33.33);
} else { } 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<Moves, AnimConfig | [AnimConfig, AnimConfig]>(); export const moveAnims = new Map<Moves, AnimConfig | [AnimConfig, AnimConfig] | null>();
export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig]>(); export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig] | null>();
export const commonAnims = new Map<CommonAnim, AnimConfig>(); export const commonAnims = new Map<CommonAnim, AnimConfig>();
export function initCommonAnims(scene: BattleScene): Promise<void> { export function initCommonAnims(scene: BattleScene): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const commonAnimNames = Utils.getEnumKeys(CommonAnim); const commonAnimNames = Utils.getEnumKeys(CommonAnim);
const commonAnimIds = Utils.getEnumValues(CommonAnim); const commonAnimIds = Utils.getEnumValues(CommonAnim);
const commonAnimFetches = []; const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = [];
for (let ca = 0; ca < commonAnimIds.length; ca++) { for (let ca = 0; ca < commonAnimIds.length; ca++) {
const commonAnimId = commonAnimIds[ca]; const commonAnimId = commonAnimIds[ca];
commonAnimFetches.push(scene.cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`) 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]; const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr)[0] || allMoves[moveId].getAttrs(DelayedAttackAttr)[0];
if (chargeAttr) { if (chargeAttr) {
const moveChargeAnims = chargeAnims.get(chargeAttr.chargeAnim); 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)) { if (Array.isArray(moveChargeAnims)) {
moveAnimations.push(moveChargeAnims[1]); moveAnimations.push(moveChargeAnims[1]);
} }
@ -668,21 +669,21 @@ interface SpriteCache {
} }
export abstract class BattleAnim { export abstract class BattleAnim {
public user: Pokemon; public user: Pokemon | null;
public target: Pokemon; public target: Pokemon | null;
public sprites: Phaser.GameObjects.Sprite[]; public sprites: Phaser.GameObjects.Sprite[];
public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle; public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle;
private srcLine: number[]; private srcLine: number[];
private dstLine: number[]; private dstLine: number[];
constructor(user: Pokemon, target: Pokemon) { constructor(user?: Pokemon, target?: Pokemon) {
this.user = user; this.user = user!; // TODO: is this bang correct?
this.target = target; this.target = target!; // TODO: is this bang correct?
this.sprites = []; this.sprites = [];
} }
abstract getAnim(): AnimConfig; abstract getAnim(): AnimConfig | null;
abstract isOppAnim(): boolean; abstract isOppAnim(): boolean;
@ -705,12 +706,12 @@ export abstract class BattleAnim {
const user = !isOppAnim ? this.user : this.target; const user = !isOppAnim ? this.user : this.target;
const target = !isOppAnim ? this.target : this.user; const target = !isOppAnim ? this.target : this.user;
const userInitialX = user.x; const userInitialX = user!.x; // TODO: is this bang correct?
const userInitialY = user.y; const userInitialY = user!.y; // TODO: is this bang correct?
const userHalfHeight = user.getSprite().displayHeight / 2; const userHalfHeight = user!.getSprite().displayHeight! / 2; // TODO: is this bang correct?
const targetInitialX = target.x; const targetInitialX = target!.x; // TODO: is this bang correct?
const targetInitialY = target.y; const targetInitialY = target!.y; // TODO: is this bang correct?
const targetHalfHeight = target.getSprite().displayHeight / 2; const targetHalfHeight = target!.getSprite().displayHeight! / 2; // TODO: is this bang correct?
let g = 0; let g = 0;
let u = 0; let u = 0;
@ -742,7 +743,7 @@ export abstract class BattleAnim {
} }
const angle = -frame.angle; const angle = -frame.angle;
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; 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; return ret;
@ -750,10 +751,10 @@ export abstract class BattleAnim {
play(scene: BattleScene, callback?: Function) { play(scene: BattleScene, callback?: Function) {
const isOppAnim = this.isOppAnim(); 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; const target = !isOppAnim ? this.target : this.user;
if (!target.isOnField()) { if (!target?.isOnField()) {
if (callback) { if (callback) {
callback(); callback();
} }
@ -781,7 +782,7 @@ export abstract class BattleAnim {
targetSprite.setAlpha(1); targetSprite.setAlpha(1);
targetSprite.pipelineData["tone"] = [ 0.0, 0.0, 0.0, 0.0 ]; targetSprite.pipelineData["tone"] = [ 0.0, 0.0, 0.0, 0.0 ];
targetSprite.setAngle(0); targetSprite.setAngle(0);
if (!this.isHideUser()) { if (!this.isHideUser() && userSprite) {
userSprite.setVisible(true); userSprite.setVisible(true);
} }
if (!this.isHideTarget() && (targetSprite !== userSprite || !this.isHideUser())) { if (!this.isHideTarget() && (targetSprite !== userSprite || !this.isHideUser())) {
@ -814,20 +815,20 @@ export abstract class BattleAnim {
this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ]; this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ];
this.dstLine = [ userInitialX, userInitialY, targetInitialX, targetInitialY ]; this.dstLine = [ userInitialX, userInitialY, targetInitialX, targetInitialY ];
let r = anim.frames.length; let r = anim!.frames.length; // TODO: is this bang correct?
let f = 0; let f = 0;
scene.tweens.addCounter({ scene.tweens.addCounter({
duration: Utils.getFrameMs(3), duration: Utils.getFrameMs(3),
repeat: anim.frames.length, repeat: anim!.frames.length, // TODO: is this bang correct?
onRepeat: () => { onRepeat: () => {
if (!f) { if (!f) {
userSprite.setVisible(false); userSprite.setVisible(false);
targetSprite.setVisible(false); targetSprite.setVisible(false);
} }
const spriteFrames = anim.frames[f]; const spriteFrames = anim!.frames[f]; // TODO: is the bang correcT?
const frameData = this.getGraphicFrameData(scene, anim.frames[f]); const frameData = this.getGraphicFrameData(scene, anim!.frames[f]); // TODO: is the bang correct?
let u = 0; let u = 0;
let t = 0; let t = 0;
let g = 0; let g = 0;
@ -840,9 +841,9 @@ export abstract class BattleAnim {
const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET]; const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET];
const spriteSource = isUser ? userSprite : targetSprite; const spriteSource = isUser ? userSprite : targetSprite;
if ((isUser ? u : t) === sprites.length) { if ((isUser ? u : t) === sprites.length) {
const sprite = scene.addPokemonSprite(isUser ? user : target, 0, 0, spriteSource.texture, spriteSource.frame.name, true); 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]); [ "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("spriteKey", (isUser ? user! : target).getBattleSpriteKey());
sprite.setPipelineData("shiny", (isUser ? user : target).shiny); sprite.setPipelineData("shiny", (isUser ? user : target).shiny);
sprite.setPipelineData("variant", (isUser ? user : target).variant); sprite.setPipelineData("variant", (isUser ? user : target).variant);
sprite.setPipelineData("ignoreFieldPos", true); sprite.setPipelineData("ignoreFieldPos", true);
@ -853,7 +854,7 @@ export abstract class BattleAnim {
const spriteIndex = isUser ? u++ : t++; const spriteIndex = isUser ? u++ : t++;
const pokemonSprite = sprites[spriteIndex]; 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.setPosition(graphicFrameData.x, graphicFrameData.y - ((spriteSource.height / 2) * (spriteSource.parentContainer.scale - 1)));
pokemonSprite.setAngle(graphicFrameData.angle); pokemonSprite.setAngle(graphicFrameData.angle);
@ -868,7 +869,7 @@ export abstract class BattleAnim {
} else { } else {
const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
if (g === sprites.length) { 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); sprites.push(newSprite);
scene.field.add(newSprite); scene.field.add(newSprite);
spritePriorities.push(1); spritePriorities.push(1);
@ -881,7 +882,7 @@ export abstract class BattleAnim {
const setSpritePriority = (priority: integer) => { const setSpritePriority = (priority: integer) => {
switch (priority) { switch (priority) {
case 0: 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; break;
case 1: case 1:
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);
@ -892,11 +893,11 @@ export abstract class BattleAnim {
if (this.bgSprite) { if (this.bgSprite) {
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite); scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite);
} else { } 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; break;
case AnimFocus.TARGET: 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; break;
default: default:
setSpritePriority(1); setSpritePriority(1);
@ -906,10 +907,10 @@ export abstract class BattleAnim {
case 3: case 3:
switch (frame.focus) { switch (frame.focus) {
case AnimFocus.USER: 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; break;
case AnimFocus.TARGET: 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; break;
default: default:
setSpritePriority(1); setSpritePriority(1);
@ -925,7 +926,7 @@ export abstract class BattleAnim {
moveSprite.setFrame(frame.graphicFrame); moveSprite.setFrame(frame.graphicFrame);
//console.log(AnimFocus[frame.focus]); //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.setPosition(graphicFrameData.x, graphicFrameData.y);
moveSprite.setAngle(graphicFrameData.angle); moveSprite.setAngle(graphicFrameData.angle);
moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY); 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); moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE);
} }
} }
if (anim.frameTimedEvents.has(f)) { if (anim?.frameTimedEvents.has(f)) {
for (const event of anim.frameTimedEvents.get(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); 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 { 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); super(user, target || user);
this.commonAnim = commonAnim; this.commonAnim = commonAnim;
} }
getAnim(): AnimConfig { getAnim(): AnimConfig | null {
return commonAnims.get(this.commonAnim); return this.commonAnim ? commonAnims.get(this.commonAnim)! : null; // TODO: is this bang correct?
} }
isOppAnim(): boolean { isOppAnim(): boolean {
@ -1009,11 +1010,11 @@ export class MoveAnim extends BattleAnim {
getAnim(): AnimConfig { getAnim(): AnimConfig {
return moveAnims.get(this.move) instanceof AnimConfig return moveAnims.get(this.move) instanceof AnimConfig
? moveAnims.get(this.move) as 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 { 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 { protected isHideUser(): boolean {
@ -1035,13 +1036,13 @@ export class MoveChargeAnim extends MoveAnim {
} }
isOppAnim(): boolean { isOppAnim(): boolean {
return !this.user.isPlayer() && Array.isArray(chargeAnims.get(this.chargeAnim)); return !this.user?.isPlayer() && Array.isArray(chargeAnims.get(this.chargeAnim));
} }
getAnim(): AnimConfig { getAnim(): AnimConfig {
return chargeAnims.get(this.chargeAnim) instanceof AnimConfig return chargeAnims.get(this.chargeAnim) instanceof AnimConfig
? chargeAnims.get(this.chargeAnim) as 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; 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++) { for (let a = 0; a < animsData.length; a++) {
const fields = animsData[a].split("@").slice(1); const fields = animsData[a].split("@").slice(1);
const nameField = fields.find(f => f.startsWith("name: ")); const nameField = fields.find(f => f.startsWith("name: "));
let isOppMove: boolean; let isOppMove: boolean | undefined;
let commonAnimId: CommonAnim; let commonAnimId: CommonAnim | undefined;
let chargeAnimId: ChargeAnim; let chargeAnimId: ChargeAnim | undefined;
if (!nameField.startsWith("name: Move:") && !(isOppMove = nameField.startsWith("name: OppMove:"))) { 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(); const name = nameMatch[2].toLowerCase();
if (commonAnimMatchNames.indexOf(name) > -1) { if (commonAnimMatchNames.indexOf(name) > -1) {
commonAnimId = commonAnimIds[commonAnimMatchNames.indexOf(name)]; commonAnimId = commonAnimIds[commonAnimMatchNames.indexOf(name)];
@ -1128,14 +1129,14 @@ export async function populateAnims() {
for (let t = 0; t < timingEntries.length; t++) { 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\",") 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"); .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]); const frameIndex = parseInt(/frame: (\d+)/.exec(timingData)![1]); // TODO: is the bang correct?
let resourceName = /name: "(.*?)"/.exec(timingData)[1].replace("''", ""); let resourceName = /name: "(.*?)"/.exec(timingData)![1].replace("''", ""); // TODO: is the bang correct?
const timingType = parseInt(/timingType: (\d)/.exec(timingData)[1]); const timingType = parseInt(/timingType: (\d)/.exec(timingData)![1]); // TODO: is the bang correct?
let timedEvent: AnimTimedEvent; let timedEvent: AnimTimedEvent | undefined;
switch (timingType) { switch (timingType) {
case 0: case 0:
if (resourceName && resourceName.indexOf(".") === -1) { if (resourceName && resourceName.indexOf(".") === -1) {
let ext: string; let ext: string | undefined;
[ "wav", "mp3", "m4a" ].every(e => { [ "wav", "mp3", "m4a" ].every(e => {
if (seNames.indexOf(`${resourceName}.${e}`) > -1) { if (seNames.indexOf(`${resourceName}.${e}`) > -1) {
ext = e; ext = e;
@ -1162,7 +1163,7 @@ export async function populateAnims() {
} }
const propPattern = /([a-z]+): (.*?)(?:,|\})/ig; const propPattern = /([a-z]+): (.*?)(?:,|\})/ig;
let propMatch: RegExpExecArray; let propMatch: RegExpExecArray;
while ((propMatch = propPattern.exec(timingData))) { while ((propMatch = propPattern.exec(timingData)!)) { // TODO: is this bang correct?
const prop = propMatch[1]; const prop = propMatch[1];
let value: any = propMatch[2]; let value: any = propMatch[2];
switch (prop) { switch (prop) {
@ -1194,7 +1195,7 @@ export async function populateAnims() {
if (!anim.frameTimedEvents.has(frameIndex)) { if (!anim.frameTimedEvents.has(frameIndex)) {
anim.frameTimedEvents.set(frameIndex, []); anim.frameTimedEvents.set(frameIndex, []);
} }
anim.frameTimedEvents.get(frameIndex).push(timedEvent); anim.frameTimedEvents.get(frameIndex)!.push(timedEvent); // TODO: is this bang correct?
} }
break; break;
case "position": case "position":

View File

@ -8,7 +8,8 @@ export enum BattleStat {
SPD, SPD,
ACC, ACC,
EVA, EVA,
RAND RAND,
HP
} }
export function getBattleStatName(stat: BattleStat) { export function getBattleStatName(stat: BattleStat) {
@ -27,6 +28,8 @@ export function getBattleStatName(stat: BattleStat) {
return i18next.t("pokemonInfo:Stat.ACC"); return i18next.t("pokemonInfo:Stat.ACC");
case BattleStat.EVA: case BattleStat.EVA:
return i18next.t("pokemonInfo:Stat.EVA"); return i18next.t("pokemonInfo:Stat.EVA");
case BattleStat.HP:
return i18next.t("pokemonInfo:Stat.HPStat");
default: default:
return "???"; return "???";
} }

View File

@ -36,11 +36,11 @@ export class BattlerTag {
public sourceMove: Moves; public sourceMove: Moves;
public sourceId?: number; 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.tagType = tagType;
this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ]; this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ];
this.turnCount = turnCount; this.turnCount = turnCount;
this.sourceMove = sourceMove; this.sourceMove = sourceMove!; // TODO: is this bang correct?
this.sourceId = sourceId; this.sourceId = sourceId;
} }
@ -66,7 +66,7 @@ export class BattlerTag {
return false; return false;
} }
getMoveName(): string { getMoveName(): string | null {
return this.sourceMove return this.sourceMove
? allMoves[this.sourceMove].name ? allMoves[this.sourceMove].name
: null; : null;
@ -299,12 +299,12 @@ export class DestinyBondTag extends BattlerTag {
if (lapseType !== BattlerTagLapseType.CUSTOM) { if (lapseType !== BattlerTagLapseType.CUSTOM) {
return super.lapse(pokemon, lapseType); return super.lapse(pokemon, lapseType);
} }
const source = pokemon.scene.getPokemonById(this.sourceId); const source = this.sourceId ? pokemon.scene.getPokemonById(this.sourceId) : null;
if (!source.isFainted()) { if (!source?.isFainted()) {
return true; return true;
} }
if (source.getAlly() === pokemon) { if (source?.getAlly() === pokemon) {
return false; return false;
} }
@ -330,7 +330,19 @@ export class InfatuatedTag extends BattlerTag {
} }
canAdd(pokemon: Pokemon): boolean { 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 { onAdd(pokemon: Pokemon): void {
@ -339,7 +351,7 @@ export class InfatuatedTag extends BattlerTag {
pokemon.scene.queueMessage( pokemon.scene.queueMessage(
i18next.t("battle:battlerTagsInfatuatedOnAdd", { i18next.t("battle:battlerTagsInfatuatedOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), 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( pokemon.scene.queueMessage(
i18next.t("battle:battlerTagsInfatuatedLapse", { i18next.t("battle:battlerTagsInfatuatedLapse", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), 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)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT));
@ -410,7 +422,7 @@ export class SeedTag extends BattlerTag {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.queueMessage(i18next.t("battle:battlerTagsSeededOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(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 { 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); const movePhase = pokemon.scene.findPhase(m => m instanceof MovePhase && m.pokemon === pokemon);
if (movePhase) { 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) { if (movesetMove) {
const lastMove = pokemon.getLastXMoves(1)[0]; const lastMove = pokemon.getLastXMoves(1)[0];
pokemon.scene.tryReplacePhase((m => m instanceof MovePhase && m.pokemon === pokemon), 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 { onAdd(pokemon: Pokemon): void {
pokemon.scene.queueMessage( pokemon.scene.queueMessage(
i18next.t("battle:battlerTagsHelpingHandOnAdd", { 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) pokemonName: getPokemonNameWithAffix(pokemon)
}) })
); );
@ -800,7 +812,7 @@ export class BindTag extends DamagingTrapTag {
getTrapMessage(pokemon: Pokemon): string { getTrapMessage(pokemon: Pokemon): string {
return i18next.t("battle:battlerTagsBindOnTrap", { return i18next.t("battle:battlerTagsBindOnTrap", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), 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() moveName: this.getMoveName()
}); });
} }
@ -814,7 +826,7 @@ export class WrapTag extends DamagingTrapTag {
getTrapMessage(pokemon: Pokemon): string { getTrapMessage(pokemon: Pokemon): string {
return i18next.t("battle:battlerTagsWrapOnTrap", { return i18next.t("battle:battlerTagsWrapOnTrap", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), 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 { getTrapMessage(pokemon: Pokemon): string {
return i18next.t("battle:battlerTagsClampOnTrap", { 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), pokemonName: getPokemonNameWithAffix(pokemon),
}); });
} }
@ -895,7 +907,7 @@ export class ThunderCageTag extends DamagingTrapTag {
getTrapMessage(pokemon: Pokemon): string { getTrapMessage(pokemon: Pokemon): string {
return i18next.t("battle:battlerTagsThunderCageOnTrap", { return i18next.t("battle:battlerTagsThunderCageOnTrap", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), 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 { getTrapMessage(pokemon: Pokemon): string {
return i18next.t("battle:battlerTagsInfestationOnTrap", { return i18next.t("battle:battlerTagsInfestationOnTrap", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), 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; return highestValue;
}, 0); }, 0);
highestStat = highestStat!; // tell TS compiler it's defined!
this.stat = highestStat; this.stat = highestStat;
switch (this.stat) { switch (this.stat) {
@ -1427,7 +1440,7 @@ export class SaltCuredTag extends BattlerTag {
super.onAdd(pokemon); super.onAdd(pokemon);
pokemon.scene.queueMessage(i18next.t("battle:battlerTagsSaltCuredOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(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 { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
@ -1474,7 +1487,7 @@ export class CursedTag extends BattlerTag {
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon); 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 { 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 { export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag {
switch (tagType) { switch (tagType) {
case BattlerTagType.RECHARGING: case BattlerTagType.RECHARGING:
@ -1741,13 +1828,11 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
case BattlerTagType.ALWAYS_CRIT: case BattlerTagType.ALWAYS_CRIT:
case BattlerTagType.IGNORE_ACCURACY: case BattlerTagType.IGNORE_ACCURACY:
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove); 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.ALWAYS_GET_HIT:
case BattlerTagType.RECEIVE_DOUBLE_DAMAGE: case BattlerTagType.RECEIVE_DOUBLE_DAMAGE:
return new BattlerTag(tagType, BattlerTagLapseType.PRE_MOVE, 1, sourceMove); return new BattlerTag(tagType, BattlerTagLapseType.PRE_MOVE, 1, sourceMove);
case BattlerTagType.BYPASS_SLEEP: 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: case BattlerTagType.IGNORE_FLYING:
return new GroundedTag(tagType, BattlerTagLapseType.CUSTOM, sourceMove); return new GroundedTag(tagType, BattlerTagLapseType.CUSTOM, sourceMove);
case BattlerTagType.ROOSTED: case BattlerTagType.ROOSTED:
@ -1770,6 +1855,13 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new StockpilingTag(sourceMove); return new StockpilingTag(sourceMove);
case BattlerTagType.OCTOLOCK: case BattlerTagType.OCTOLOCK:
return new OctolockTag(sourceId); 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: case BattlerTagType.NONE:
default: default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);

View File

@ -54,7 +54,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25); const threshold = new Utils.NumberHolder(0.25);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); 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) { if (pokemon.battleData) {
pokemon.battleData.berriesEaten.push(berryType); 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) { if (ppRestoreMove !== undefined) {
ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0);
pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove.getName(), berryName: getBerryName(berryType) })); pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) }));
} }
}; };
} }

View File

@ -7705,7 +7705,7 @@ export function initBiomes() {
? pokemonEvolutions[speciesId] ? 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); uncatchableSpecies.push(speciesId);
} }

View File

@ -412,7 +412,7 @@ export class SingleGenerationChallenge extends Challenge {
const speciesToCheck = [pokemon.speciesId]; const speciesToCheck = [pokemon.speciesId];
while (speciesToCheck.length) { while (speciesToCheck.length) {
const checking = speciesToCheck.pop(); const checking = speciesToCheck.pop();
if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { if (checking && pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) {
pokemonEvolutions[checking].forEach(e => { pokemonEvolutions[checking].forEach(e => {
speciesToCheck.push(e.speciesId); speciesToCheck.push(e.speciesId);
generations.push(getPokemonSpecies(e.speciesId).generation); generations.push(getPokemonSpecies(e.speciesId).generation);
@ -430,7 +430,7 @@ export class SingleGenerationChallenge extends Challenge {
applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean {
const baseGeneration = pokemon.species.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.species.speciesId).generation; 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))) { if (pokemon.isPlayer() && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))) {
valid.value = false; valid.value = false;
return true; return true;
@ -542,13 +542,13 @@ export class SingleTypeChallenge extends Challenge {
const speciesToCheck = [pokemon.speciesId]; const speciesToCheck = [pokemon.speciesId];
while (speciesToCheck.length) { while (speciesToCheck.length) {
const checking = speciesToCheck.pop(); const checking = speciesToCheck.pop();
if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { if (checking && pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) {
pokemonEvolutions[checking].forEach(e => { pokemonEvolutions[checking].forEach(e => {
speciesToCheck.push(e.speciesId); speciesToCheck.push(e.speciesId);
types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2); 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 => { pokemonFormChanges[checking].forEach(f1 => {
getPokemonSpecies(checking).forms.forEach(f2 => { getPokemonSpecies(checking).forms.forEach(f2 => {
if (f1.formKey === f2.formKey) { if (f1.formKey === f2.formKey) {
@ -568,10 +568,11 @@ export class SingleTypeChallenge extends Challenge {
applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean {
if (pokemon.isPlayer() && !pokemon.isOfType(this.value - 1, false, false, true) 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; valid.value = false;
return true; return true;
} }
return false;
} }
/** /**

View File

@ -11,15 +11,15 @@ export interface DailyRunConfig {
starters: Starter; starters: Starter;
} }
export function fetchDailyRunSeed(): Promise<string> { export function fetchDailyRunSeed(): Promise<string | null> {
return new Promise<string>((resolve, reject) => { return new Promise<string | null>((resolve, reject) => {
Utils.apiFetch("daily/seed").then(response => { Utils.apiFetch("daily/seed").then(response => {
if (!response.ok) { if (!response.ok) {
resolve(null); resolve(null);
return; return;
} }
return response.text(); return response.text();
}).then(seed => resolve(seed)) }).then(seed => resolve(seed!)) // TODO: is this bang correct?
.catch(err => reject(err)); .catch(err => reject(err));
}); });
} }

View File

@ -452,144 +452,304 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
[TrainerType.ROCKET_GRUNT]: [ [TrainerType.ROCKET_GRUNT]: [
{ {
encounter: [ 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: [ 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: [ encounter: [
"dialogue:rocket_admin.encounter.1", "dialogue:archer.encounter.1",
"dialogue:rocket_admin.encounter.2", "dialogue:archer.encounter.2",
"dialogue:rocket_admin.encounter.3", "dialogue:archer.encounter.3",
], ],
victory: [ victory: [
"dialogue:rocket_admin.victory.1", "dialogue:archer.victory.1",
"dialogue:rocket_admin.victory.2", "dialogue:archer.victory.2",
"dialogue:rocket_admin.victory.3", "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]: [ [TrainerType.MAGMA_GRUNT]: [
{ {
encounter: [ 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: [ 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: [ encounter: [
"dialogue:magma_admin.encounter.1", "dialogue:tabitha.encounter.1",
"dialogue:magma_admin.encounter.2", "dialogue:tabitha.encounter.2",
"dialogue:magma_admin.encounter.3", "dialogue:tabitha.encounter.3",
], ],
victory: [ victory: [
"dialogue:magma_admin.victory.1", "dialogue:tabitha.victory.1",
"dialogue:magma_admin.victory.2", "dialogue:tabitha.victory.2",
"dialogue:magma_admin.victory.3", "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]: [ [TrainerType.AQUA_GRUNT]: [
{ {
encounter: [ 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: [ 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: [ encounter: [
"dialogue:aqua_admin.encounter.1", "dialogue:matt.encounter.1",
"dialogue:aqua_admin.encounter.2", "dialogue:matt.encounter.2",
"dialogue:aqua_admin.encounter.3", "dialogue:matt.encounter.3",
], ],
victory: [ victory: [
"dialogue:aqua_admin.victory.1", "dialogue:matt.victory.1",
"dialogue:aqua_admin.victory.2", "dialogue:matt.victory.2",
"dialogue:aqua_admin.victory.3", "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]: [ [TrainerType.GALACTIC_GRUNT]: [
{ {
encounter: [ 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: [ 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: [ encounter: [
"dialogue:galactic_admin.encounter.1", "dialogue:jupiter.encounter.1",
"dialogue:galactic_admin.encounter.2", "dialogue:jupiter.encounter.2",
"dialogue:galactic_admin.encounter.3", "dialogue:jupiter.encounter.3",
], ],
victory: [ victory: [
"dialogue:galactic_admin.victory.1", "dialogue:jupiter.victory.1",
"dialogue:galactic_admin.victory.2", "dialogue:jupiter.victory.2",
"dialogue:galactic_admin.victory.3", "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]: [ [TrainerType.PLASMA_GRUNT]: [
{ {
encounter: [ 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: [ 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: [ encounter: [
"dialogue:plasma_sage.encounter.1", "dialogue:zinzolin.encounter.1",
"dialogue:plasma_sage.encounter.2", "dialogue:zinzolin.encounter.2",
"dialogue:plasma_sage.encounter.3", "dialogue:zinzolin.encounter.3",
], ],
victory: [ victory: [
"dialogue:plasma_sage.victory.1", "dialogue:zinzolin.victory.1",
"dialogue:plasma_sage.victory.2", "dialogue:zinzolin.victory.2",
"dialogue:plasma_sage.victory.3", "dialogue:zinzolin.victory.3",
] ]
} }
], ],
[TrainerType.FLARE_GRUNT]: [ [TrainerType.FLARE_GRUNT]: [
{ {
encounter: [ 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: [ 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: [ encounter: [
"dialogue:flare_admin.encounter.1", "dialogue:bryony.encounter.1",
"dialogue:flare_admin.encounter.2", "dialogue:bryony.encounter.2",
"dialogue:flare_admin.encounter.3", "dialogue:bryony.encounter.3",
], ],
victory: [ victory: [
"dialogue:flare_admin.victory.1", "dialogue:bryony.victory.1",
"dialogue:flare_admin.victory.2", "dialogue:bryony.victory.2",
"dialogue:flare_admin.victory.3", "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",
] ]
} }
], ],

View File

@ -140,30 +140,30 @@ export class Egg {
constructor(eggOptions?: IEggOptions) { 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.") //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 // 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 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 // 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._sourceType = eggOptions?.sourceType ?? undefined;
this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves();
this._timestamp = eggOptions.timestamp ?? new Date().getTime(); this._timestamp = eggOptions?.timestamp ?? new Date().getTime();
// First roll shiny and variant so we can filter if species with an variant exist // 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._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny());
this._variantTier = eggOptions.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant());
this._species = eggOptions.species ?? this.rollSpecies(eggOptions.scene); 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 // Override egg tier and hatchwaves if species was given
if (eggOptions.species) { if (eggOptions?.species) {
this._tier = this.getEggTierFromSpeciesStarterValue(); this._tier = this.getEggTierFromSpeciesStarterValue();
this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves();
} }
@ -174,10 +174,10 @@ export class Egg {
this._variantTier = VariantTier.COMMON; this._variantTier = VariantTier.COMMON;
} }
// Needs this._tier so it needs to be generated afer the tier override if bought from same species // Needs this._tier so it needs to be generated afer the tier override if bought from same species
this._eggMoveIndex = eggOptions.eggMoveIndex ?? this.rollEggMoveIndex(); this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex();
if (eggOptions.pulled) { if (eggOptions?.pulled) {
this.increasePullStatistic(eggOptions.scene); this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct?
this.addEggToGameData(eggOptions.scene); this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct?
} }
} }
@ -202,14 +202,18 @@ export class Egg {
// Legacy egg wants to hatch. Generate missing properties // Legacy egg wants to hatch. Generate missing properties
if (!this._species) { if (!this._species) {
this._isShiny = this.rollShiny(); 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 // Sets the hidden ability if a hidden ability exists and the override is set
// or if the same species egg hits the chance // or if the same species egg hits the chance
let abilityIndex = undefined; let abilityIndex: number | undefined = undefined;
if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility
|| (this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE)))) { || (this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE)))) {
abilityIndex = 2; abilityIndex = 2;
@ -273,6 +277,9 @@ export class Egg {
return i18next.t("egg:gachaTypeShiny"); return i18next.t("egg:gachaTypeShiny");
case EggSourceType.GACHA_MOVE: case EggSourceType.GACHA_MOVE:
return i18next.t("egg:gachaTypeMove"); 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; 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) { if (!scene) {
return undefined; return null;
} }
/** /**
* Manaphy eggs have a 1/8 chance of being Manaphy and 7/8 chance of being Phione * 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 * and being the same each time
*/ */
let totalWeight = 0; let totalWeight = 0;
const speciesWeights = []; const speciesWeights : number[] = [];
for (const speciesId of speciesPool) { for (const speciesId of speciesPool) {
let weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); let weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100);
const species = getPokemonSpecies(speciesId); const species = getPokemonSpecies(speciesId);
@ -416,6 +423,7 @@ export class Egg {
break; break;
} }
} }
species = species!; // tell TS compiled it's defined now!
if (!!scene.gameData.dexData[species].caughtAttr || scene.gameData.eggs.some(e => e.species === species)) { 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); scene.gameData.unlockPity[this.tier] = Math.min(scene.gameData.unlockPity[this.tier] + 1, 10);
@ -513,6 +521,8 @@ export class Egg {
if (speciesStartValue >= 8) { if (speciesStartValue >= 8) {
return EggTier.MASTER; return EggTier.MASTER;
} }
return EggTier.COMMON;
} }
//// ////
@ -537,6 +547,7 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
ret = Phaser.Math.RND.shuffle(legendarySpecies)[index]; ret = Phaser.Math.RND.shuffle(legendarySpecies)[index];
}, offset, EGG_SEED.toString()); }, offset, EGG_SEED.toString());
ret = ret!; // tell TS compiler it's
return ret; return ret;
} }

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,8 @@ export function getNatureName(nature: Nature, includeStatEffects: boolean = fals
} }
if (includeStatEffects) { if (includeStatEffects) {
const stats = Utils.getEnumValues(Stat).slice(1); const stats = Utils.getEnumValues(Stat).slice(1);
let increasedStat: Stat = null; let increasedStat: Stat | null = null;
let decreasedStat: Stat = null; let decreasedStat: Stat | null = null;
for (const stat of stats) { for (const stat of stats) {
const multiplier = getNatureStatMultiplier(nature, stat); const multiplier = getNatureStatMultiplier(nature, stat);
if (multiplier > 1) { if (multiplier > 1) {

View File

@ -59,26 +59,26 @@ export type EvolutionConditionEnforceFunc = (p: Pokemon) => void;
export class SpeciesFormEvolution { export class SpeciesFormEvolution {
public speciesId: Species; public speciesId: Species;
public preFormKey: string; public preFormKey: string | null;
public evoFormKey: string; public evoFormKey: string | null;
public level: integer; public level: integer;
public item: EvolutionItem; public item: EvolutionItem | null;
public condition: SpeciesEvolutionCondition; public condition: SpeciesEvolutionCondition | null;
public wildDelay: SpeciesWildEvolutionDelay; 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.speciesId = speciesId;
this.preFormKey = preFormKey; this.preFormKey = preFormKey;
this.evoFormKey = evoFormKey; this.evoFormKey = evoFormKey;
this.level = level; this.level = level;
this.item = item || EvolutionItem.NONE; this.item = item || EvolutionItem.NONE;
this.condition = condition; this.condition = condition;
this.wildDelay = wildDelay || SpeciesWildEvolutionDelay.NONE; this.wildDelay = wildDelay ?? SpeciesWildEvolutionDelay.NONE;
} }
} }
export class SpeciesEvolution extends SpeciesFormEvolution { 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); super(speciesId, null, null, level, item, condition, wildDelay);
} }
} }
@ -95,7 +95,7 @@ export class FusionSpeciesFormEvolution extends SpeciesFormEvolution {
export class SpeciesEvolutionCondition { export class SpeciesEvolutionCondition {
public predicate: EvolutionConditionPredicate; public predicate: EvolutionConditionPredicate;
public enforceFunc: EvolutionConditionEnforceFunc; public enforceFunc: EvolutionConditionEnforceFunc | undefined;
constructor(predicate: EvolutionConditionPredicate, enforceFunc?: EvolutionConditionEnforceFunc) { constructor(predicate: EvolutionConditionPredicate, enforceFunc?: EvolutionConditionEnforceFunc) {
this.predicate = predicate; this.predicate = predicate;
@ -400,8 +400,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.LINOONE, 20, null, null) new SpeciesEvolution(Species.LINOONE, 20, null, null)
], ],
[Species.WURMPLE]: [ [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.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), null) new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.SILCOON]: [ [Species.SILCOON]: [
new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null) new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null)
@ -945,7 +945,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.SHIINOTIC, 24, null, null) new SpeciesEvolution(Species.SHIINOTIC, 24, null, null)
], ],
[Species.SALANDIT]: [ [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]: [ [Species.STUFFUL]: [
new SpeciesEvolution(Species.BEWEAR, 27, null, null) new SpeciesEvolution(Species.BEWEAR, 27, null, null)
@ -969,8 +969,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.COSMOEM, 43, null, null) new SpeciesEvolution(Species.COSMOEM, 43, null, null)
], ],
[Species.COSMOEM]: [ [Species.COSMOEM]: [
new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), 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), null) new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
], ],
[Species.MELTAN]: [ [Species.MELTAN]: [
new SpeciesEvolution(Species.MELMETAL, 48, null, null) 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) new SpeciesEvolution(Species.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.TANGELA]: [ [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]: [ [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]: [ [Species.STARYU]: [
new SpeciesEvolution(Species.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.EEVEE]: [ [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, "", "", 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, "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, "", "", 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.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), 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) new SpeciesEvolution(Species.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.AIPOM]: [ [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]: [ [Species.SUNKERN]: [
new SpeciesEvolution(Species.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.YANMA]: [ [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]: [ [Species.MURKROW]: [
new SpeciesEvolution(Species.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) 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) new SpeciesEvolution(Species.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.GIRAFARIG]: [ [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]: [ [Species.DUNSPARCE]: [
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => { new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => {
let ret = false; 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); p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
} }
return ret; return ret;
}), SpeciesWildEvolutionDelay.LONG), }), 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]: [ [Species.GLIGAR]: [
new SpeciesEvolution(Species.GLISCOR, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.LONG) 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 new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna
], ],
[Species.PILOSWINE]: [ [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]: [ [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]: [ [Species.LOMBRE]: [
new SpeciesEvolution(Species.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) 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) new SpeciesEvolution(Species.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.BONSLY]: [ [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]: [ [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.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.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.PANSAGE]: [ [Species.PANSAGE]: [
new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) 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) new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.ROCKRUFF]: [ [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, "", "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), null), 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)), 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)))
], ],
[Species.STEENEE]: [ [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]: [ [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]: [ [Species.ALOLA_SANDSHREW]: [
new SpeciesEvolution(Species.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) 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) new SpeciesEvolution(Species.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.CLOBBOPUS]: [ [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]: [ [Species.SINISTEA]: [
new SpeciesFormEvolution(Species.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, SpeciesWildEvolutionDelay.LONG), 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) new SpeciesEvolution(Species.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.HISUI_QWILFISH]: [ [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]: [ [Species.HISUI_SNEASEL]: [
new SpeciesEvolution(Species.SNEASLER, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.LONG) 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) new SpeciesFormEvolution(Species.SINISTCHA, "artisan", "masterpiece", 1, EvolutionItem.MASTERPIECE_TEACUP, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.DIPPLIN]: [ [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]: [ [Species.KADABRA]: [
new SpeciesEvolution(Species.ALAKAZAM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ALAKAZAM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1501,7 +1501,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
], ],
[Species.ONIX]: [ [Species.ONIX]: [
new SpeciesEvolution(Species.STEELIX, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( 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) SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.RHYDON]: [ [Species.RHYDON]: [
@ -1512,7 +1512,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
], ],
[Species.SCYTHER]: [ [Species.SCYTHER]: [
new SpeciesEvolution(Species.SCIZOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( 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), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesEvolution(Species.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, 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) new SpeciesEvolution(Species.ALOLA_GOLEM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.PRIMEAPE]: [ [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]: [ [Species.GOLBAT]: [
new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(110), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(110), SpeciesWildEvolutionDelay.VERY_LONG)

View File

@ -181,7 +181,7 @@ export class SpeciesFormChange {
return true; return true;
} }
findTrigger(triggerType: Constructor<SpeciesFormChangeTrigger>): SpeciesFormChangeTrigger { findTrigger(triggerType: Constructor<SpeciesFormChangeTrigger>): SpeciesFormChangeTrigger | null {
if (!this.trigger.hasTriggerType(triggerType)) { if (!this.trigger.hasTriggerType(triggerType)) {
return null; return null;
} }
@ -189,7 +189,7 @@ export class SpeciesFormChange {
const trigger = this.trigger; const trigger = this.trigger;
if (trigger instanceof SpeciesFormChangeCompoundTrigger) { 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; return trigger;
@ -198,11 +198,11 @@ export class SpeciesFormChange {
export class SpeciesFormChangeCondition { export class SpeciesFormChangeCondition {
public predicate: SpeciesFormChangeConditionPredicate; public predicate: SpeciesFormChangeConditionPredicate;
public enforceFunc: SpeciesFormChangeConditionEnforceFunc; public enforceFunc: SpeciesFormChangeConditionEnforceFunc | null;
constructor(predicate: SpeciesFormChangeConditionPredicate, enforceFunc?: SpeciesFormChangeConditionEnforceFunc) { constructor(predicate: SpeciesFormChangeConditionPredicate, enforceFunc?: SpeciesFormChangeConditionEnforceFunc) {
this.predicate = predicate; 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 { 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 { export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger {
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
const command = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; 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]: [ [Species.EISCUE]: [
new SpeciesFormChange(Species.EISCUE, "", "no-ice", new SpeciesFormChangeManualTrigger(), true), new SpeciesFormChange(Species.EISCUE, "", "no-ice", new SpeciesFormChangeManualTrigger(), true),
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),
] ]
}; };

View File

@ -18666,7 +18666,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 48, Moves.PIKA_PAPOW ], [ 48, Moves.PIKA_PAPOW ],
], ],
3: [ 3: [
[ EVOLVE_MOVE, Moves.METEOR_MASH ], [ 1, Moves.METEOR_MASH ],
[ 1, Moves.TAIL_WHIP ], [ 1, Moves.TAIL_WHIP ],
[ 1, Moves.GROWL ], [ 1, Moves.GROWL ],
[ 1, Moves.THUNDER_SHOCK ], [ 1, Moves.THUNDER_SHOCK ],
@ -18690,7 +18690,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 48, Moves.PIKA_PAPOW ], [ 48, Moves.PIKA_PAPOW ],
], ],
4: [ 4: [
[ EVOLVE_MOVE, Moves.ICICLE_CRASH ], [ 1, Moves.ICICLE_CRASH ],
[ 1, Moves.TAIL_WHIP ], [ 1, Moves.TAIL_WHIP ],
[ 1, Moves.GROWL ], [ 1, Moves.GROWL ],
[ 1, Moves.THUNDER_SHOCK ], [ 1, Moves.THUNDER_SHOCK ],
@ -18714,7 +18714,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 48, Moves.PIKA_PAPOW ], [ 48, Moves.PIKA_PAPOW ],
], ],
5: [ 5: [
[ EVOLVE_MOVE, Moves.DRAINING_KISS ], [ 1, Moves.DRAINING_KISS ],
[ 1, Moves.TAIL_WHIP ], [ 1, Moves.TAIL_WHIP ],
[ 1, Moves.GROWL ], [ 1, Moves.GROWL ],
[ 1, Moves.THUNDER_SHOCK ], [ 1, Moves.THUNDER_SHOCK ],
@ -18738,7 +18738,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 48, Moves.PIKA_PAPOW ], [ 48, Moves.PIKA_PAPOW ],
], ],
6: [ 6: [
[ EVOLVE_MOVE, Moves.ELECTRIC_TERRAIN ], [ 1, Moves.ELECTRIC_TERRAIN ],
[ 1, Moves.TAIL_WHIP ], [ 1, Moves.TAIL_WHIP ],
[ 1, Moves.GROWL ], [ 1, Moves.GROWL ],
[ 1, Moves.THUNDER_SHOCK ], [ 1, Moves.THUNDER_SHOCK ],
@ -18762,7 +18762,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 48, Moves.PIKA_PAPOW ], [ 48, Moves.PIKA_PAPOW ],
], ],
7: [ 7: [
[ EVOLVE_MOVE, Moves.FLYING_PRESS ], [ 1, Moves.FLYING_PRESS ],
[ 1, Moves.TAIL_WHIP ], [ 1, Moves.TAIL_WHIP ],
[ 1, Moves.GROWL ], [ 1, Moves.GROWL ],
[ 1, Moves.THUNDER_SHOCK ], [ 1, Moves.THUNDER_SHOCK ],
@ -18886,7 +18886,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
}, },
[Species.ROTOM]: { [Species.ROTOM]: {
1: [ 1: [
[ EVOLVE_MOVE, Moves.OVERHEAT ], [ 1, Moves.OVERHEAT ],
[ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.DOUBLE_TEAM ],
[ 1, Moves.ASTONISH ], [ 1, Moves.ASTONISH ],
[ 5, Moves.THUNDER_SHOCK ], [ 5, Moves.THUNDER_SHOCK ],
@ -18902,7 +18902,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 55, Moves.UPROAR ], [ 55, Moves.UPROAR ],
], ],
2: [ 2: [
[ EVOLVE_MOVE, Moves.HYDRO_PUMP ], [ 1, Moves.HYDRO_PUMP ],
[ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.DOUBLE_TEAM ],
[ 1, Moves.ASTONISH ], [ 1, Moves.ASTONISH ],
[ 5, Moves.THUNDER_SHOCK ], [ 5, Moves.THUNDER_SHOCK ],
@ -18918,7 +18918,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 55, Moves.UPROAR ], [ 55, Moves.UPROAR ],
], ],
3: [ 3: [
[ EVOLVE_MOVE, Moves.BLIZZARD ], [ 1, Moves.BLIZZARD ],
[ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.DOUBLE_TEAM ],
[ 1, Moves.ASTONISH ], [ 1, Moves.ASTONISH ],
[ 5, Moves.THUNDER_SHOCK ], [ 5, Moves.THUNDER_SHOCK ],
@ -18934,7 +18934,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 55, Moves.UPROAR ], [ 55, Moves.UPROAR ],
], ],
4: [ 4: [
[ EVOLVE_MOVE, Moves.AIR_SLASH ], [ 1, Moves.AIR_SLASH ],
[ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.DOUBLE_TEAM ],
[ 1, Moves.ASTONISH ], [ 1, Moves.ASTONISH ],
[ 5, Moves.THUNDER_SHOCK ], [ 5, Moves.THUNDER_SHOCK ],
@ -18950,7 +18950,7 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 55, Moves.UPROAR ], [ 55, Moves.UPROAR ],
], ],
5: [ 5: [
[ EVOLVE_MOVE, Moves.LEAF_STORM ], [ 1, Moves.LEAF_STORM ],
[ 1, Moves.DOUBLE_TEAM ], [ 1, Moves.DOUBLE_TEAM ],
[ 1, Moves.ASTONISH ], [ 1, Moves.ASTONISH ],
[ 5, Moves.THUNDER_SHOCK ], [ 5, Moves.THUNDER_SHOCK ],

View File

@ -28,21 +28,21 @@ export enum Region {
PALDEA 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 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)) { if (Array.isArray(species)) {
// Pick a random species from the list // Pick a random species from the list
species = species[Math.floor(Math.random() * species.length)]; species = species[Math.floor(Math.random() * species.length)];
} }
if (species >= 2000) { 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]; return allSpecies[species - 1];
} }
export function getPokemonSpeciesForm(species: Species, formIndex: integer): PokemonSpeciesForm { export function getPokemonSpeciesForm(species: Species, formIndex: integer): PokemonSpeciesForm {
const retSpecies: PokemonSpecies = species >= 2000 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]; : allSpecies[species - 1];
if (formIndex < retSpecies.forms?.length) { if (formIndex < retSpecies.forms?.length) {
return retSpecies.forms[formIndex]; return retSpecies.forms[formIndex];
@ -97,7 +97,7 @@ export function getFusedSpeciesName(speciesAName: string, speciesBName: string):
fragB = fragB.slice(1); fragB = fragB.slice(1);
} else { } else {
const newCharMatch = new RegExp(`[^${lastCharA}]`).exec(fragB); const newCharMatch = new RegExp(`[^${lastCharA}]`).exec(fragB);
if (newCharMatch?.index > 0) { if (newCharMatch?.index !== undefined && newCharMatch.index > 0) {
fragB = fragB.slice(newCharMatch.index); fragB = fragB.slice(newCharMatch.index);
} }
} }
@ -125,7 +125,7 @@ export abstract class PokemonSpeciesForm {
public formIndex: integer; public formIndex: integer;
public generation: integer; public generation: integer;
public type1: Type; public type1: Type;
public type2: Type; public type2: Type | null;
public height: number; public height: number;
public weight: number; public weight: number;
public ability1: Abilities; public ability1: Abilities;
@ -139,7 +139,7 @@ export abstract class PokemonSpeciesForm {
public genderDiffs: boolean; public genderDiffs: boolean;
public isStarterSelectable: 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, baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer,
catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs: boolean, isStarterSelectable: boolean) { catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs: boolean, isStarterSelectable: boolean) {
this.type1 = type1; this.type1 = type1;
@ -267,7 +267,7 @@ export abstract class PokemonSpeciesForm {
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; 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) { if (formIndex === undefined || this instanceof PokemonForm) {
formIndex = this.formIndex; formIndex = this.formIndex;
} }
@ -281,7 +281,7 @@ export abstract class PokemonSpeciesForm {
`${back ? "back__" : ""}${baseSpriteKey}`.split("__").map(p => config ? config = config[p] : null); `${back ? "back__" : ""}${baseSpriteKey}`.split("__").map(p => config ? config = config[p] : null);
const variantSet = config as VariantSet; 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 { 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 * @returns species id if no additional forms, index with formkey if a pokemon with a form
*/ */
getVariantDataIndex(formIndex?: integer) { getVariantDataIndex(formIndex?: integer) {
let formkey = null; let formkey: string | null = null;
let variantDataIndex: integer|string = this.speciesId; let variantDataIndex: integer | string = this.speciesId;
const species = getPokemonSpecies(this.speciesId); const species = getPokemonSpecies(this.speciesId);
if (species.forms.length > 0) { if (species.forms.length > 0 && formIndex !== undefined) {
formkey = species.forms[formIndex]?.formSpriteKey; formkey = species.forms[formIndex]?.formSpriteKey;
if (formkey) { if (formkey) {
variantDataIndex = `${this.speciesId}-${formkey}`; variantDataIndex = `${this.speciesId}-${formkey}`;
@ -311,7 +311,7 @@ export abstract class PokemonSpeciesForm {
getIconAtlasKey(formIndex?: integer, shiny?: boolean, variant?: integer): string { getIconAtlasKey(formIndex?: integer, shiny?: boolean, variant?: integer): string {
const variantDataIndex = this.getVariantDataIndex(formIndex); 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" : ""}`; return `pokemon_icons_${this.generation}${isVariant ? "v" : ""}`;
} }
@ -324,7 +324,7 @@ export abstract class PokemonSpeciesForm {
let ret = this.speciesId.toString(); 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) { if (shiny && !isVariant) {
ret += "s"; ret += "s";
@ -382,7 +382,7 @@ export abstract class PokemonSpeciesForm {
let ret = speciesId.toString(); let ret = speciesId.toString();
const forms = getPokemonSpecies(speciesId).forms; const forms = getPokemonSpecies(speciesId).forms;
if (forms.length) { 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`); 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); formIndex = Math.min(formIndex, forms.length - 1);
} }
@ -478,7 +478,7 @@ export abstract class PokemonSpeciesForm {
let config = variantData; let config = variantData;
spritePath.split("/").map(p => config ? config = config[p] : null); spritePath.split("/").map(p => config ? config = config[p] : null);
const variantSet = config as VariantSet; const variantSet = config as VariantSet;
if (variantSet && variantSet[variant] === 1) { if (variantSet && (variant !== undefined && variantSet[variant] === 1)) {
const populateVariantColors = (key: string): Promise<void> => { const populateVariantColors = (key: string): Promise<void> => {
return new Promise(resolve => { return new Promise(resolve => {
if (variantColorCache.hasOwnProperty(key)) { if (variantColorCache.hasOwnProperty(key)) {
@ -507,7 +507,7 @@ export abstract class PokemonSpeciesForm {
cry(scene: BattleScene, soundConfig?: Phaser.Types.Sound.SoundConfig, ignorePlay?: boolean): AnySound { cry(scene: BattleScene, soundConfig?: Phaser.Types.Sound.SoundConfig, ignorePlay?: boolean): AnySound {
const cryKey = this.getCryKey(this.formIndex); 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) { if (cry?.pendingRemove) {
cry = null; cry = null;
} }
@ -532,10 +532,12 @@ export abstract class PokemonSpeciesForm {
const frame = sourceFrame; const frame = sourceFrame;
canvas.width = frame.width; canvas.width = frame.width;
canvas.height = frame.height; canvas.height = frame.height;
context.drawImage(sourceImage, frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, 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 imageData = context?.getImageData(frame.cutX, frame.cutY, frame.width, frame.height);
const pixelData = imageData.data; const pixelData = imageData?.data;
const pixelColors: number[] = [];
if (pixelData?.length !== undefined) {
for (let i = 0; i < pixelData.length; i += 4) { for (let i = 0; i < pixelData.length; i += 4) {
if (pixelData[i + 3]) { if (pixelData[i + 3]) {
const pixel = pixelData.slice(i, i + 4); const pixel = pixelData.slice(i, i + 4);
@ -546,7 +548,6 @@ export abstract class PokemonSpeciesForm {
} }
} }
const pixelColors = [];
for (let i = 0; i < pixelData.length; i += 4) { for (let i = 0; i < pixelData.length; i += 4) {
const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0); const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0);
if (!total) { if (!total) {
@ -554,8 +555,9 @@ export abstract class PokemonSpeciesForm {
} }
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<number, number>; let paletteColors: Map<number, number> = new Map();
const originalRandom = Math.random; const originalRandom = Math.random;
Math.random = () => Phaser.Math.RND.realInRange(0, 1); Math.random = () => Phaser.Math.RND.realInRange(0, 1);
@ -577,15 +579,15 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
public mythical: boolean; public mythical: boolean;
public species: string; public species: string;
public growthRate: GrowthRate; public growthRate: GrowthRate;
public malePercent: number; public malePercent: number | null;
public genderDiffs: boolean; public genderDiffs: boolean;
public canChangeForm: boolean; public canChangeForm: boolean;
public forms: PokemonForm[]; public forms: PokemonForm[];
constructor(id: Species, generation: integer, subLegendary: boolean, legendary: boolean, mythical: boolean, species: string, 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, 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[]) { genderDiffs: boolean, canChangeForm?: boolean, ...forms: PokemonForm[]) {
super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd, super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd,
catchRate, baseFriendship, baseExp, genderDiffs, false); catchRate, baseFriendship, baseExp, genderDiffs, false);
@ -614,7 +616,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
getName(formIndex?: integer): string { getName(formIndex?: integer): string {
if (formIndex !== undefined && this.forms.length) { if (formIndex !== undefined && this.forms.length) {
const form = this.forms[formIndex]; const form = this.forms[formIndex];
let key: string; let key: string | null;
switch (form.formKey) { switch (form.formKey) {
case SpeciesFormKey.MEGA: case SpeciesFormKey.MEGA:
case SpeciesFormKey.PRIMAL: case SpeciesFormKey.PRIMAL:
@ -626,6 +628,8 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
default: default:
if (form.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1) { if (form.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1) {
key = "gigantamax"; 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); evolutionChance = Math.min(minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance), 1);
} }
} else { } 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); let evolutionLevel = Math.max(ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2), 1);
if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) { 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) { if (prevolutionLevel > 1) {
evolutionLevel = prevolutionLevel; evolutionLevel = prevolutionLevel;
} }
@ -750,15 +754,15 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
for (const weight of evolutionPool.keys()) { for (const weight of evolutionPool.keys()) {
if (randValue < weight) { 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; return this.speciesId;
} }
getEvolutionLevels() { getEvolutionLevels(): [Species, integer][] {
const evolutionLevels = []; const evolutionLevels: [Species, integer][] = [];
//console.log(Species[this.speciesId], pokemonEvolutions[this.speciesId]) //console.log(Species[this.speciesId], pokemonEvolutions[this.speciesId])
@ -778,8 +782,8 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
return evolutionLevels; return evolutionLevels;
} }
getPrevolutionLevels() { getPrevolutionLevels(): [Species, integer][] {
const prevolutionLevels = []; const prevolutionLevels: [Species, integer][] = [];
const allEvolvingPokemon = Object.keys(pokemonEvolutions); const allEvolvingPokemon = Object.keys(pokemonEvolutions);
for (const p of allEvolvingPokemon) { 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 // 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][] { 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)) { if (pokemonPrevolutions.hasOwnProperty(this.speciesId)) {
const prevolutionLevels = this.getPrevolutionLevels().reverse(); const prevolutionLevels = this.getPrevolutionLevels().reverse();
const levelDiff = player ? 0 : forTrainer || isBoss ? forTrainer && isBoss ? 2.5 : 5 : 10; const levelDiff = player ? 0 : forTrainer || isBoss ? forTrainer && isBoss ? 2.5 : 5 : 10;
ret.push([ prevolutionLevels[0][0], 1 ]); ret.push([ prevolutionLevels[0][0], 1 ]);
for (let l = 1; l < prevolutionLevels.length; l++) { for (let l = 1; l < prevolutionLevels.length; l++) {
const evolution = pokemonEvolutions[prevolutionLevels[l - 1][0]].find(e => e.speciesId === prevolutionLevels[l][0]); 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 lastPrevolutionLevel = ret[prevolutionLevels.length - 1][1];
const evolution = pokemonEvolutions[prevolutionLevels[prevolutionLevels.length - 1][0]].find(e => e.speciesId === this.speciesId); 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 { } else {
ret.push([ this.speciesId, 1 ]); ret.push([ this.speciesId, 1 ]);
} }
@ -853,7 +857,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
} }
getFormSpriteKey(formIndex?: integer) { 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`); 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); 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 { export class PokemonForm extends PokemonSpeciesForm {
public formName: string; public formName: string;
public formKey: 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 // 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"]; 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, 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, super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd,
catchRate, baseFriendship, baseExp, !!genderDiffs, (!!isStarterSelectable || !formKey)); catchRate, baseFriendship, baseExp, !!genderDiffs, (!!isStarterSelectable || !formKey));
this.formName = formName; this.formName = formName;
@ -2725,8 +2729,8 @@ export const speciesStarters = {
[Species.VOLTORB]: 2, [Species.VOLTORB]: 2,
[Species.EXEGGCUTE]: 3, [Species.EXEGGCUTE]: 3,
[Species.CUBONE]: 3, [Species.CUBONE]: 3,
[Species.HITMONLEE]: 4, [Species.HITMONLEE]: 5,
[Species.HITMONCHAN]: 4, [Species.HITMONCHAN]: 5,
[Species.LICKITUNG]: 3, [Species.LICKITUNG]: 3,
[Species.KOFFING]: 2, [Species.KOFFING]: 2,
[Species.RHYHORN]: 3, [Species.RHYHORN]: 3,
@ -2738,7 +2742,7 @@ export const speciesStarters = {
[Species.STARYU]: 3, [Species.STARYU]: 3,
[Species.MR_MIME]: 3, [Species.MR_MIME]: 3,
[Species.SCYTHER]: 5, [Species.SCYTHER]: 5,
[Species.JYNX]: 3, [Species.JYNX]: 4,
[Species.ELECTABUZZ]: 4, [Species.ELECTABUZZ]: 4,
[Species.MAGMAR]: 4, [Species.MAGMAR]: 4,
[Species.PINSIR]: 4, [Species.PINSIR]: 4,
@ -2765,7 +2769,7 @@ export const speciesStarters = {
[Species.SENTRET]: 1, [Species.SENTRET]: 1,
[Species.HOOTHOOT]: 2, [Species.HOOTHOOT]: 2,
[Species.LEDYBA]: 1, [Species.LEDYBA]: 1,
[Species.SPINARAK]: 2, [Species.SPINARAK]: 1,
[Species.CHINCHOU]: 2, [Species.CHINCHOU]: 2,
[Species.PICHU]: 2, [Species.PICHU]: 2,
[Species.CLEFFA]: 2, [Species.CLEFFA]: 2,
@ -2805,8 +2809,8 @@ export const speciesStarters = {
[Species.PHANPY]: 3, [Species.PHANPY]: 3,
[Species.STANTLER]: 3, [Species.STANTLER]: 3,
[Species.SMEARGLE]: 1, [Species.SMEARGLE]: 1,
[Species.TYROGUE]: 2, [Species.TYROGUE]: 3,
[Species.SMOOCHUM]: 2, [Species.SMOOCHUM]: 3,
[Species.ELEKID]: 3, [Species.ELEKID]: 3,
[Species.MAGBY]: 3, [Species.MAGBY]: 3,
[Species.MILTANK]: 4, [Species.MILTANK]: 4,
@ -2819,7 +2823,7 @@ export const speciesStarters = {
[Species.CELEBI]: 6, [Species.CELEBI]: 6,
[Species.TREECKO]: 3, [Species.TREECKO]: 3,
[Species.TORCHIC]: 3, [Species.TORCHIC]: 4,
[Species.MUDKIP]: 3, [Species.MUDKIP]: 3,
[Species.POOCHYENA]: 2, [Species.POOCHYENA]: 2,
[Species.ZIGZAGOON]: 2, [Species.ZIGZAGOON]: 2,
@ -2898,7 +2902,7 @@ export const speciesStarters = {
[Species.CHIMCHAR]: 3, [Species.CHIMCHAR]: 3,
[Species.PIPLUP]: 3, [Species.PIPLUP]: 3,
[Species.STARLY]: 3, [Species.STARLY]: 3,
[Species.BIDOOF]: 3, [Species.BIDOOF]: 2,
[Species.KRICKETOT]: 1, [Species.KRICKETOT]: 1,
[Species.SHINX]: 2, [Species.SHINX]: 2,
[Species.BUDEW]: 3, [Species.BUDEW]: 3,
@ -2986,7 +2990,7 @@ export const speciesStarters = {
[Species.ZORUA]: 3, [Species.ZORUA]: 3,
[Species.MINCCINO]: 3, [Species.MINCCINO]: 3,
[Species.GOTHITA]: 3, [Species.GOTHITA]: 3,
[Species.SOLOSIS]: 4, [Species.SOLOSIS]: 3,
[Species.DUCKLETT]: 2, [Species.DUCKLETT]: 2,
[Species.VANILLITE]: 3, [Species.VANILLITE]: 3,
[Species.DEERLING]: 2, [Species.DEERLING]: 2,
@ -3205,7 +3209,7 @@ export const speciesStarters = {
[Species.LECHONK]: 2, [Species.LECHONK]: 2,
[Species.TAROUNTULA]: 1, [Species.TAROUNTULA]: 1,
[Species.NYMBLE]: 3, [Species.NYMBLE]: 3,
[Species.PAWMI]: 4, [Species.PAWMI]: 3,
[Species.TANDEMAUS]: 4, [Species.TANDEMAUS]: 4,
[Species.FIDOUGH]: 2, [Species.FIDOUGH]: 2,
[Species.SMOLIV]: 3, [Species.SMOLIV]: 3,
@ -3319,14 +3323,14 @@ export const starterPassiveAbilities = {
[Species.SQUIRTLE]: Abilities.STURDY, [Species.SQUIRTLE]: Abilities.STURDY,
[Species.CATERPIE]: Abilities.MAGICIAN, [Species.CATERPIE]: Abilities.MAGICIAN,
[Species.WEEDLE]: Abilities.TINTED_LENS, [Species.WEEDLE]: Abilities.TINTED_LENS,
[Species.PIDGEY]: Abilities.GALE_WINGS, [Species.PIDGEY]: Abilities.FLARE_BOOST,
[Species.RATTATA]: Abilities.STRONG_JAW, [Species.RATTATA]: Abilities.STRONG_JAW,
[Species.SPEAROW]: Abilities.MOXIE, [Species.SPEAROW]: Abilities.MOXIE,
[Species.EKANS]: Abilities.REGENERATOR, [Species.EKANS]: Abilities.REGENERATOR,
[Species.SANDSHREW]: Abilities.TOUGH_CLAWS, [Species.SANDSHREW]: Abilities.TOUGH_CLAWS,
[Species.NIDORAN_F]: Abilities.FLARE_BOOST, [Species.NIDORAN_F]: Abilities.FLARE_BOOST,
[Species.NIDORAN_M]: Abilities.GUTS, [Species.NIDORAN_M]: Abilities.GUTS,
[Species.VULPIX]: Abilities.SOLAR_POWER, [Species.VULPIX]: Abilities.FUR_COAT,
[Species.ZUBAT]: Abilities.INTIMIDATE, [Species.ZUBAT]: Abilities.INTIMIDATE,
[Species.ODDISH]: Abilities.TRIAGE, [Species.ODDISH]: Abilities.TRIAGE,
[Species.PARAS]: Abilities.TRIAGE, [Species.PARAS]: Abilities.TRIAGE,
@ -3345,16 +3349,16 @@ export const starterPassiveAbilities = {
[Species.PONYTA]: Abilities.MAGIC_GUARD, [Species.PONYTA]: Abilities.MAGIC_GUARD,
[Species.SLOWPOKE]: Abilities.UNAWARE, [Species.SLOWPOKE]: Abilities.UNAWARE,
[Species.MAGNEMITE]: Abilities.LEVITATE, [Species.MAGNEMITE]: Abilities.LEVITATE,
[Species.FARFETCHD]: Abilities.HUGE_POWER, [Species.FARFETCHD]: Abilities.SNIPER,
[Species.DODUO]: Abilities.PARENTAL_BOND, [Species.DODUO]: Abilities.PARENTAL_BOND,
[Species.SEEL]: Abilities.WATER_BUBBLE, [Species.SEEL]: Abilities.WATER_BUBBLE,
[Species.GRIMER]: Abilities.WATER_ABSORB, [Species.GRIMER]: Abilities.WATER_ABSORB,
[Species.SHELLDER]: Abilities.ICE_SCALES, [Species.SHELLDER]: Abilities.ICE_SCALES,
[Species.GASTLY]: Abilities.SHADOW_SHIELD, [Species.GASTLY]: Abilities.SHADOW_SHIELD,
[Species.ONIX]: Abilities.ROCKY_PAYLOAD, [Species.ONIX]: Abilities.ROCKY_PAYLOAD,
[Species.DROWZEE]: Abilities.BAD_DREAMS, [Species.DROWZEE]: Abilities.MAGICIAN,
[Species.KRABBY]: Abilities.UNBURDEN, [Species.KRABBY]: Abilities.UNBURDEN,
[Species.VOLTORB]: Abilities.ELECTRIC_SURGE, [Species.VOLTORB]: Abilities.TRANSISTOR,
[Species.EXEGGCUTE]: Abilities.RIPEN, [Species.EXEGGCUTE]: Abilities.RIPEN,
[Species.CUBONE]: Abilities.PARENTAL_BOND, [Species.CUBONE]: Abilities.PARENTAL_BOND,
[Species.LICKITUNG]: Abilities.THICK_FAT, [Species.LICKITUNG]: Abilities.THICK_FAT,
@ -3374,7 +3378,7 @@ export const starterPassiveAbilities = {
[Species.EEVEE]: Abilities.SIMPLE, [Species.EEVEE]: Abilities.SIMPLE,
[Species.PORYGON]: Abilities.PROTEAN, [Species.PORYGON]: Abilities.PROTEAN,
[Species.OMANYTE]: Abilities.STURDY, [Species.OMANYTE]: Abilities.STURDY,
[Species.KABUTO]: Abilities.SHARPNESS, [Species.KABUTO]: Abilities.TOUGH_CLAWS,
[Species.AERODACTYL]: Abilities.ORICHALCUM_PULSE, [Species.AERODACTYL]: Abilities.ORICHALCUM_PULSE,
[Species.ARTICUNO]: Abilities.SNOW_WARNING, [Species.ARTICUNO]: Abilities.SNOW_WARNING,
[Species.ZAPDOS]: Abilities.DRIZZLE, [Species.ZAPDOS]: Abilities.DRIZZLE,
@ -3476,7 +3480,7 @@ export const starterPassiveAbilities = {
[Species.CACNEA]: Abilities.SAND_RUSH, [Species.CACNEA]: Abilities.SAND_RUSH,
[Species.SWABLU]: Abilities.ADAPTABILITY, [Species.SWABLU]: Abilities.ADAPTABILITY,
[Species.ZANGOOSE]: Abilities.POISON_HEAL, [Species.ZANGOOSE]: Abilities.POISON_HEAL,
[Species.SEVIPER]: Abilities.INTIMIDATE, [Species.SEVIPER]: Abilities.MULTISCALE,
[Species.LUNATONE]: Abilities.SHADOW_SHIELD, [Species.LUNATONE]: Abilities.SHADOW_SHIELD,
[Species.SOLROCK]: Abilities.DROUGHT, [Species.SOLROCK]: Abilities.DROUGHT,
[Species.BARBOACH]: Abilities.SIMPLE, [Species.BARBOACH]: Abilities.SIMPLE,
@ -3495,16 +3499,16 @@ export const starterPassiveAbilities = {
[Species.SNORUNT]: Abilities.SNOW_WARNING, [Species.SNORUNT]: Abilities.SNOW_WARNING,
[Species.SPHEAL]: Abilities.UNAWARE, [Species.SPHEAL]: Abilities.UNAWARE,
[Species.CLAMPERL]: Abilities.DRIZZLE, [Species.CLAMPERL]: Abilities.DRIZZLE,
[Species.RELICANTH]: Abilities.SOLID_ROCK, [Species.RELICANTH]: Abilities.PRIMORDIAL_SEA,
[Species.LUVDISC]: Abilities.MULTISCALE, [Species.LUVDISC]: Abilities.MULTISCALE,
[Species.BAGON]: Abilities.ADAPTABILITY, [Species.BAGON]: Abilities.DRAGONS_MAW,
[Species.BELDUM]: Abilities.LEVITATE, [Species.BELDUM]: Abilities.LEVITATE,
[Species.REGIROCK]: Abilities.SAND_STREAM, [Species.REGIROCK]: Abilities.SAND_STREAM,
[Species.REGICE]: Abilities.SNOW_WARNING, [Species.REGICE]: Abilities.SNOW_WARNING,
[Species.REGISTEEL]: Abilities.FILTER, [Species.REGISTEEL]: Abilities.FILTER,
[Species.LATIAS]: Abilities.SOUL_HEART, [Species.LATIAS]: Abilities.PRISM_ARMOR,
[Species.LATIOS]: Abilities.TINTED_LENS, [Species.LATIOS]: Abilities.TINTED_LENS,
[Species.KYOGRE]: Abilities.RAIN_DISH, [Species.KYOGRE]: Abilities.MOLD_BREAKER,
[Species.GROUDON]: Abilities.TURBOBLAZE, [Species.GROUDON]: Abilities.TURBOBLAZE,
[Species.RAYQUAZA]: Abilities.UNNERVE, [Species.RAYQUAZA]: Abilities.UNNERVE,
[Species.JIRACHI]: Abilities.COMATOSE, [Species.JIRACHI]: Abilities.COMATOSE,
@ -3523,7 +3527,7 @@ export const starterPassiveAbilities = {
[Species.COMBEE]: Abilities.INTIMIDATE, [Species.COMBEE]: Abilities.INTIMIDATE,
[Species.PACHIRISU]: Abilities.HONEY_GATHER, [Species.PACHIRISU]: Abilities.HONEY_GATHER,
[Species.BUIZEL]: Abilities.MOXIE, [Species.BUIZEL]: Abilities.MOXIE,
[Species.CHERUBI]: Abilities.DROUGHT, [Species.CHERUBI]: Abilities.ORICHALCUM_PULSE,
[Species.SHELLOS]: Abilities.REGENERATOR, [Species.SHELLOS]: Abilities.REGENERATOR,
[Species.DRIFLOON]: Abilities.MAGIC_GUARD, [Species.DRIFLOON]: Abilities.MAGIC_GUARD,
[Species.BUNEARY]: Abilities.ADAPTABILITY, [Species.BUNEARY]: Abilities.ADAPTABILITY,
@ -3537,13 +3541,13 @@ export const starterPassiveAbilities = {
[Species.CHATOT]: Abilities.PUNK_ROCK, [Species.CHATOT]: Abilities.PUNK_ROCK,
[Species.SPIRITOMB]: Abilities.VESSEL_OF_RUIN, [Species.SPIRITOMB]: Abilities.VESSEL_OF_RUIN,
[Species.GIBLE]: Abilities.SAND_STREAM, [Species.GIBLE]: Abilities.SAND_STREAM,
[Species.MUNCHLAX]: Abilities.RIPEN, [Species.MUNCHLAX]: Abilities.HARVEST,
[Species.RIOLU]: Abilities.MINDS_EYE, [Species.RIOLU]: Abilities.MINDS_EYE,
[Species.HIPPOPOTAS]: Abilities.UNAWARE, [Species.HIPPOPOTAS]: Abilities.UNAWARE,
[Species.SKORUPI]: Abilities.SUPER_LUCK, [Species.SKORUPI]: Abilities.SUPER_LUCK,
[Species.CROAGUNK]: Abilities.MOXIE, [Species.CROAGUNK]: Abilities.MOXIE,
[Species.CARNIVINE]: Abilities.ARENA_TRAP, [Species.CARNIVINE]: Abilities.ARENA_TRAP,
[Species.FINNEON]: Abilities.DRIZZLE, [Species.FINNEON]: Abilities.WATER_BUBBLE,
[Species.MANTYKE]: Abilities.UNAWARE, [Species.MANTYKE]: Abilities.UNAWARE,
[Species.SNOVER]: Abilities.THICK_FAT, [Species.SNOVER]: Abilities.THICK_FAT,
[Species.ROTOM]: Abilities.HADRON_ENGINE, [Species.ROTOM]: Abilities.HADRON_ENGINE,
@ -3557,7 +3561,7 @@ export const starterPassiveAbilities = {
[Species.GIRATINA]: Abilities.SHADOW_SHIELD, [Species.GIRATINA]: Abilities.SHADOW_SHIELD,
[Species.CRESSELIA]: Abilities.MAGIC_BOUNCE, [Species.CRESSELIA]: Abilities.MAGIC_BOUNCE,
[Species.PHIONE]: Abilities.SIMPLE, [Species.PHIONE]: Abilities.SIMPLE,
[Species.MANAPHY]: Abilities.SIMPLE, [Species.MANAPHY]: Abilities.PRIMORDIAL_SEA,
[Species.DARKRAI]: Abilities.UNNERVE, [Species.DARKRAI]: Abilities.UNNERVE,
[Species.SHAYMIN]: Abilities.WIND_RIDER, [Species.SHAYMIN]: Abilities.WIND_RIDER,
[Species.ARCEUS]: Abilities.ADAPTABILITY, [Species.ARCEUS]: Abilities.ADAPTABILITY,
@ -3590,13 +3594,13 @@ export const starterPassiveAbilities = {
[Species.SANDILE]: Abilities.TOUGH_CLAWS, [Species.SANDILE]: Abilities.TOUGH_CLAWS,
[Species.DARUMAKA]: Abilities.GORILLA_TACTICS, [Species.DARUMAKA]: Abilities.GORILLA_TACTICS,
[Species.MARACTUS]: Abilities.WELL_BAKED_BODY, [Species.MARACTUS]: Abilities.WELL_BAKED_BODY,
[Species.DWEBBLE]: Abilities.ANGER_SHELL, [Species.DWEBBLE]: Abilities.ROCKY_PAYLOAD,
[Species.SCRAGGY]: Abilities.PROTEAN, [Species.SCRAGGY]: Abilities.PROTEAN,
[Species.SIGILYPH]: Abilities.MAGICIAN, [Species.SIGILYPH]: Abilities.FLARE_BOOST,
[Species.YAMASK]: Abilities.PURIFYING_SALT, [Species.YAMASK]: Abilities.PURIFYING_SALT,
[Species.TIRTOUGA]: Abilities.ANGER_SHELL, [Species.TIRTOUGA]: Abilities.WATER_ABSORB,
[Species.ARCHEN]: Abilities.MULTISCALE, [Species.ARCHEN]: Abilities.MULTISCALE,
[Species.TRUBBISH]: Abilities.TOXIC_DEBRIS, [Species.TRUBBISH]: Abilities.NEUTRALIZING_GAS,
[Species.ZORUA]: Abilities.DARK_AURA, [Species.ZORUA]: Abilities.DARK_AURA,
[Species.MINCCINO]: Abilities.FUR_COAT, [Species.MINCCINO]: Abilities.FUR_COAT,
[Species.GOTHITA]: Abilities.UNNERVE, [Species.GOTHITA]: Abilities.UNNERVE,
@ -3611,7 +3615,7 @@ export const starterPassiveAbilities = {
[Species.ALOMOMOLA]: Abilities.MULTISCALE, [Species.ALOMOMOLA]: Abilities.MULTISCALE,
[Species.JOLTIK]: Abilities.TRANSISTOR, [Species.JOLTIK]: Abilities.TRANSISTOR,
[Species.FERROSEED]: Abilities.ROUGH_SKIN, [Species.FERROSEED]: Abilities.ROUGH_SKIN,
[Species.KLINK]: Abilities.STEELWORKER, [Species.KLINK]: Abilities.STEELY_SPIRIT,
[Species.TYNAMO]: Abilities.POISON_HEAL, [Species.TYNAMO]: Abilities.POISON_HEAL,
[Species.ELGYEM]: Abilities.PRISM_ARMOR, [Species.ELGYEM]: Abilities.PRISM_ARMOR,
[Species.LITWICK]: Abilities.SOUL_HEART, [Species.LITWICK]: Abilities.SOUL_HEART,
@ -3625,7 +3629,7 @@ export const starterPassiveAbilities = {
[Species.GOLETT]: Abilities.SHADOW_SHIELD, [Species.GOLETT]: Abilities.SHADOW_SHIELD,
[Species.PAWNIARD]: Abilities.SWORD_OF_RUIN, [Species.PAWNIARD]: Abilities.SWORD_OF_RUIN,
[Species.BOUFFALANT]: Abilities.ROCK_HEAD, [Species.BOUFFALANT]: Abilities.ROCK_HEAD,
[Species.RUFFLET]: Abilities.GALE_WINGS, [Species.RUFFLET]: Abilities.SPEED_BOOST,
[Species.VULLABY]: Abilities.THICK_FAT, [Species.VULLABY]: Abilities.THICK_FAT,
[Species.HEATMOR]: Abilities.CONTRARY, [Species.HEATMOR]: Abilities.CONTRARY,
[Species.DURANT]: Abilities.COMPOUND_EYES, [Species.DURANT]: Abilities.COMPOUND_EYES,
@ -3651,12 +3655,12 @@ export const starterPassiveAbilities = {
[Species.SCATTERBUG]: Abilities.PRANKSTER, [Species.SCATTERBUG]: Abilities.PRANKSTER,
[Species.LITLEO]: Abilities.BEAST_BOOST, [Species.LITLEO]: Abilities.BEAST_BOOST,
[Species.FLABEBE]: Abilities.GRASSY_SURGE, [Species.FLABEBE]: Abilities.GRASSY_SURGE,
[Species.SKIDDO]: Abilities.GRASSY_SURGE, [Species.SKIDDO]: Abilities.SEED_SOWER,
[Species.PANCHAM]: Abilities.FUR_COAT, [Species.PANCHAM]: Abilities.FUR_COAT,
[Species.FURFROU]: Abilities.FLUFFY, [Species.FURFROU]: Abilities.FLUFFY,
[Species.ESPURR]: Abilities.FUR_COAT, [Species.ESPURR]: Abilities.FUR_COAT,
[Species.HONEDGE]: Abilities.SHARPNESS, [Species.HONEDGE]: Abilities.SHARPNESS,
[Species.SPRITZEE]: Abilities.MISTY_SURGE, [Species.SPRITZEE]: Abilities.FUR_COAT,
[Species.SWIRLIX]: Abilities.WELL_BAKED_BODY, [Species.SWIRLIX]: Abilities.WELL_BAKED_BODY,
[Species.INKAY]: Abilities.UNNERVE, [Species.INKAY]: Abilities.UNNERVE,
[Species.BINACLE]: Abilities.SAP_SIPPER, [Species.BINACLE]: Abilities.SAP_SIPPER,
@ -3670,17 +3674,17 @@ export const starterPassiveAbilities = {
[Species.CARBINK]: Abilities.SOLID_ROCK, [Species.CARBINK]: Abilities.SOLID_ROCK,
[Species.GOOMY]: Abilities.REGENERATOR, [Species.GOOMY]: Abilities.REGENERATOR,
[Species.KLEFKI]: Abilities.LEVITATE, [Species.KLEFKI]: Abilities.LEVITATE,
[Species.PHANTUMP]: Abilities.RIPEN, [Species.PHANTUMP]: Abilities.SHADOW_TAG,
[Species.PUMPKABOO]: Abilities.WELL_BAKED_BODY, [Species.PUMPKABOO]: Abilities.WELL_BAKED_BODY,
[Species.BERGMITE]: Abilities.ICE_SCALES, [Species.BERGMITE]: Abilities.ICE_SCALES,
[Species.NOIBAT]: Abilities.PUNK_ROCK, [Species.NOIBAT]: Abilities.PUNK_ROCK,
[Species.XERNEAS]: Abilities.MISTY_SURGE, [Species.XERNEAS]: Abilities.HARVEST,
[Species.YVELTAL]: Abilities.SOUL_HEART, [Species.YVELTAL]: Abilities.SOUL_HEART,
[Species.ZYGARDE]: Abilities.HUGE_POWER, [Species.ZYGARDE]: Abilities.HUGE_POWER,
[Species.DIANCIE]: Abilities.LEVITATE, [Species.DIANCIE]: Abilities.LEVITATE,
[Species.HOOPA]: Abilities.OPPORTUNIST, [Species.HOOPA]: Abilities.OPPORTUNIST,
[Species.VOLCANION]: Abilities.FILTER, [Species.VOLCANION]: Abilities.FILTER,
[Species.ROWLET]: Abilities.UNBURDEN, [Species.ROWLET]: Abilities.SNIPER,
[Species.LITTEN]: Abilities.FUR_COAT, [Species.LITTEN]: Abilities.FUR_COAT,
[Species.POPPLIO]: Abilities.PUNK_ROCK, [Species.POPPLIO]: Abilities.PUNK_ROCK,
[Species.PIKIPEK]: Abilities.TECHNICIAN, [Species.PIKIPEK]: Abilities.TECHNICIAN,
@ -3714,7 +3718,7 @@ export const starterPassiveAbilities = {
[Species.BRUXISH]: Abilities.MULTISCALE, [Species.BRUXISH]: Abilities.MULTISCALE,
[Species.DRAMPA]: Abilities.THICK_FAT, [Species.DRAMPA]: Abilities.THICK_FAT,
[Species.DHELMISE]: Abilities.WATER_BUBBLE, [Species.DHELMISE]: Abilities.WATER_BUBBLE,
[Species.JANGMO_O]: Abilities.PUNK_ROCK, [Species.JANGMO_O]: Abilities.DAUNTLESS_SHIELD,
[Species.TAPU_KOKO]: Abilities.TRANSISTOR, [Species.TAPU_KOKO]: Abilities.TRANSISTOR,
[Species.TAPU_LELE]: Abilities.SHEER_FORCE, [Species.TAPU_LELE]: Abilities.SHEER_FORCE,
[Species.TAPU_BULU]: Abilities.TRIAGE, [Species.TAPU_BULU]: Abilities.TRIAGE,
@ -3726,7 +3730,7 @@ export const starterPassiveAbilities = {
[Species.XURKITREE]: Abilities.TRANSISTOR, [Species.XURKITREE]: Abilities.TRANSISTOR,
[Species.CELESTEELA]: Abilities.HEATPROOF, [Species.CELESTEELA]: Abilities.HEATPROOF,
[Species.KARTANA]: Abilities.SHARPNESS, [Species.KARTANA]: Abilities.SHARPNESS,
[Species.GUZZLORD]: Abilities.INNARDS_OUT, [Species.GUZZLORD]: Abilities.POISON_HEAL,
[Species.NECROZMA]: Abilities.BEAST_BOOST, [Species.NECROZMA]: Abilities.BEAST_BOOST,
[Species.MAGEARNA]: Abilities.STEELY_SPIRIT, [Species.MAGEARNA]: Abilities.STEELY_SPIRIT,
[Species.MARSHADOW]: Abilities.IRON_FIST, [Species.MARSHADOW]: Abilities.IRON_FIST,
@ -3738,13 +3742,13 @@ export const starterPassiveAbilities = {
[Species.GROOKEY]: Abilities.GRASS_PELT, [Species.GROOKEY]: Abilities.GRASS_PELT,
[Species.SCORBUNNY]: Abilities.NO_GUARD, [Species.SCORBUNNY]: Abilities.NO_GUARD,
[Species.SOBBLE]: Abilities.SUPER_LUCK, [Species.SOBBLE]: Abilities.SUPER_LUCK,
[Species.SKWOVET]: Abilities.RIPEN, [Species.SKWOVET]: Abilities.HARVEST,
[Species.ROOKIDEE]: Abilities.IRON_BARBS, [Species.ROOKIDEE]: Abilities.IRON_BARBS,
[Species.BLIPBUG]: Abilities.PSYCHIC_SURGE, [Species.BLIPBUG]: Abilities.PSYCHIC_SURGE,
[Species.NICKIT]: Abilities.MAGICIAN, [Species.NICKIT]: Abilities.MAGICIAN,
[Species.GOSSIFLEUR]: Abilities.GRASSY_SURGE, [Species.GOSSIFLEUR]: Abilities.GRASSY_SURGE,
[Species.WOOLOO]: Abilities.SIMPLE, [Species.WOOLOO]: Abilities.SIMPLE,
[Species.CHEWTLE]: Abilities.ROCK_HEAD, [Species.CHEWTLE]: Abilities.ROCKY_PAYLOAD,
[Species.YAMPER]: Abilities.SHEER_FORCE, [Species.YAMPER]: Abilities.SHEER_FORCE,
[Species.ROLYCOLY]: Abilities.SOLID_ROCK, [Species.ROLYCOLY]: Abilities.SOLID_ROCK,
[Species.APPLIN]: Abilities.DRAGONS_MAW, [Species.APPLIN]: Abilities.DRAGONS_MAW,
@ -3757,7 +3761,7 @@ export const starterPassiveAbilities = {
[Species.SINISTEA]: Abilities.SHADOW_SHIELD, [Species.SINISTEA]: Abilities.SHADOW_SHIELD,
[Species.HATENNA]: Abilities.FAIRY_AURA, [Species.HATENNA]: Abilities.FAIRY_AURA,
[Species.IMPIDIMP]: Abilities.FUR_COAT, [Species.IMPIDIMP]: Abilities.FUR_COAT,
[Species.MILCERY]: Abilities.MISTY_SURGE, [Species.MILCERY]: Abilities.REGENERATOR,
[Species.FALINKS]: Abilities.PARENTAL_BOND, [Species.FALINKS]: Abilities.PARENTAL_BOND,
[Species.PINCURCHIN]: Abilities.ELECTROMORPHOSIS, [Species.PINCURCHIN]: Abilities.ELECTROMORPHOSIS,
[Species.SNOM]: Abilities.SNOW_WARNING, [Species.SNOM]: Abilities.SNOW_WARNING,
@ -3776,7 +3780,7 @@ export const starterPassiveAbilities = {
[Species.ZAMAZENTA]: Abilities.STAMINA, [Species.ZAMAZENTA]: Abilities.STAMINA,
[Species.ETERNATUS]: Abilities.SUPREME_OVERLORD, [Species.ETERNATUS]: Abilities.SUPREME_OVERLORD,
[Species.KUBFU]: Abilities.IRON_FIST, [Species.KUBFU]: Abilities.IRON_FIST,
[Species.ZARUDE]: Abilities.GRASSY_SURGE, [Species.ZARUDE]: Abilities.TOUGH_CLAWS,
[Species.REGIELEKI]: Abilities.ELECTRIC_SURGE, [Species.REGIELEKI]: Abilities.ELECTRIC_SURGE,
[Species.REGIDRAGO]: Abilities.MULTISCALE, [Species.REGIDRAGO]: Abilities.MULTISCALE,
[Species.GLASTRIER]: Abilities.FILTER, [Species.GLASTRIER]: Abilities.FILTER,
@ -3785,7 +3789,7 @@ export const starterPassiveAbilities = {
[Species.ENAMORUS]: Abilities.FAIRY_AURA, [Species.ENAMORUS]: Abilities.FAIRY_AURA,
[Species.SPRIGATITO]: Abilities.MAGICIAN, [Species.SPRIGATITO]: Abilities.MAGICIAN,
[Species.FUECOCO]: Abilities.PUNK_ROCK, [Species.FUECOCO]: Abilities.PUNK_ROCK,
[Species.QUAXLY]: Abilities.DEFIANT, [Species.QUAXLY]: Abilities.OPPORTUNIST,
[Species.LECHONK]: Abilities.SIMPLE, [Species.LECHONK]: Abilities.SIMPLE,
[Species.TAROUNTULA]: Abilities.HONEY_GATHER, [Species.TAROUNTULA]: Abilities.HONEY_GATHER,
[Species.NYMBLE]: Abilities.GUTS, [Species.NYMBLE]: Abilities.GUTS,
@ -3833,7 +3837,7 @@ export const starterPassiveAbilities = {
[Species.IRON_MOTH]: Abilities.LEVITATE, [Species.IRON_MOTH]: Abilities.LEVITATE,
[Species.IRON_THORNS]: Abilities.SAND_STREAM, [Species.IRON_THORNS]: Abilities.SAND_STREAM,
[Species.FRIGIBAX]: Abilities.SNOW_WARNING, [Species.FRIGIBAX]: Abilities.SNOW_WARNING,
[Species.GIMMIGHOUL]: Abilities.CONTRARY, [Species.GIMMIGHOUL]: Abilities.HONEY_GATHER,
[Species.WO_CHIEN]: Abilities.VESSEL_OF_RUIN, [Species.WO_CHIEN]: Abilities.VESSEL_OF_RUIN,
[Species.CHIEN_PAO]: Abilities.INTREPID_SWORD, [Species.CHIEN_PAO]: Abilities.INTREPID_SWORD,
[Species.TING_LU]: Abilities.STAMINA, [Species.TING_LU]: Abilities.STAMINA,
@ -3864,7 +3868,7 @@ export const starterPassiveAbilities = {
[Species.ALOLA_GRIMER]: Abilities.TOXIC_DEBRIS, [Species.ALOLA_GRIMER]: Abilities.TOXIC_DEBRIS,
[Species.ETERNAL_FLOETTE]: Abilities.MAGIC_GUARD, [Species.ETERNAL_FLOETTE]: Abilities.MAGIC_GUARD,
[Species.GALAR_MEOWTH]: Abilities.STEELWORKER, [Species.GALAR_MEOWTH]: Abilities.STEELWORKER,
[Species.GALAR_PONYTA]: Abilities.PIXILATE, [Species.GALAR_PONYTA]: Abilities.MOXIE,
[Species.GALAR_SLOWPOKE]: Abilities.UNAWARE, [Species.GALAR_SLOWPOKE]: Abilities.UNAWARE,
[Species.GALAR_FARFETCHD]: Abilities.INTREPID_SWORD, [Species.GALAR_FARFETCHD]: Abilities.INTREPID_SWORD,
[Species.GALAR_ARTICUNO]: Abilities.SERENE_GRACE, [Species.GALAR_ARTICUNO]: Abilities.SERENE_GRACE,
@ -3876,7 +3880,7 @@ export const starterPassiveAbilities = {
[Species.GALAR_YAMASK]: Abilities.TABLETS_OF_RUIN, [Species.GALAR_YAMASK]: Abilities.TABLETS_OF_RUIN,
[Species.GALAR_STUNFISK]: Abilities.ARENA_TRAP, [Species.GALAR_STUNFISK]: Abilities.ARENA_TRAP,
[Species.HISUI_GROWLITHE]: Abilities.RECKLESS, [Species.HISUI_GROWLITHE]: Abilities.RECKLESS,
[Species.HISUI_VOLTORB]: Abilities.ELECTRIC_SURGE, [Species.HISUI_VOLTORB]: Abilities.TRANSISTOR,
[Species.HISUI_QWILFISH]: Abilities.MERCILESS, [Species.HISUI_QWILFISH]: Abilities.MERCILESS,
[Species.HISUI_SNEASEL]: Abilities.SCRAPPY, [Species.HISUI_SNEASEL]: Abilities.SCRAPPY,
[Species.HISUI_ZORUA]: Abilities.ADAPTABILITY, [Species.HISUI_ZORUA]: Abilities.ADAPTABILITY,

View File

@ -7,12 +7,12 @@ export { StatusEffect };
export class Status { export class Status {
public effect: StatusEffect; public effect: StatusEffect;
public turnCount: integer; public turnCount: integer;
public cureTurn: integer; public cureTurn: integer | null;
constructor(effect: StatusEffect, turnCount: integer = 0, cureTurn?: integer) { constructor(effect: StatusEffect, turnCount: integer = 0, cureTurn?: integer) {
this.effect = effect; this.effect = effect;
this.turnCount = turnCount === undefined ? 0 : turnCount; this.turnCount = turnCount === undefined ? 0 : turnCount;
this.cureTurn = cureTurn; this.cureTurn = cureTurn!; // TODO: is this bang correct?
} }
incrementTurn(): void { incrementTurn(): void {
@ -24,7 +24,7 @@ export class Status {
} }
} }
function getStatusEffectMessageKey(statusEffect: StatusEffect): string { function getStatusEffectMessageKey(statusEffect: StatusEffect | undefined): string {
switch (statusEffect) { switch (statusEffect) {
case StatusEffect.POISON: case StatusEffect.POISON:
return "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) { if (!sourceText) {
const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain`as ParseKeys; const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain`as ParseKeys;
return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix }); return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix });

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ import * as Utils from "../utils";
class TrainerNameConfig { class TrainerNameConfig {
public urls: string[]; public urls: string[];
public femaleUrls: string[]; public femaleUrls: string[] | null;
constructor(type: TrainerType, ...urls: string[]) { constructor(type: TrainerType, ...urls: string[]) {
this.urls = urls.length ? urls : [ Utils.toReadableString(TrainerType[type]).replace(/ /g, "_") ]; this.urls = urls.length ? urls : [ Utils.toReadableString(TrainerType[type]).replace(/ /g, "_") ];
@ -136,8 +136,11 @@ function fetchAndPopulateTrainerNames(url: string, parser: DOMParser, trainerNam
.then(html => { .then(html => {
console.log(url); console.log(url);
const htmlDoc = parser.parseFromString(html, "text/html"); const htmlDoc = parser.parseFromString(html, "text/html");
const trainerListHeader = htmlDoc.querySelector("#Trainer_list").parentElement; const trainerListHeader = htmlDoc.querySelector("#Trainer_list")?.parentElement;
const elements = [...trainerListHeader.parentElement.childNodes]; if (!trainerListHeader) {
return [];
}
const elements = [...(trainerListHeader?.parentElement?.childNodes ?? [])];
const startChildIndex = elements.indexOf(trainerListHeader); const startChildIndex = elements.indexOf(trainerListHeader);
const endChildIndex = elements.findIndex(h => h.nodeName === "H2" && elements.indexOf(h) > startChildIndex); const endChildIndex = elements.findIndex(h => h.nodeName === "H2" && elements.indexOf(h) > startChildIndex);
const tables = elements.filter(t => { 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); const trainerRows = [...table.querySelectorAll("tr:not(:first-child)")].filter(r => r.children.length === 9);
for (const row of trainerRows) { for (const row of trainerRows) {
const nameCell = row.firstElementChild; const nameCell = row.firstElementChild;
if (!nameCell) {
continue;
}
const content = nameCell.innerHTML; const content = nameCell.innerHTML;
if (content.indexOf(" <a ") > -1) { if (content.indexOf(" <a ") > -1) {
const female = /♀/.test(content); const female = /♀/.test(content);

View File

@ -499,6 +499,8 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
case Type.STELLAR: case Type.STELLAR:
return 1; return 1;
} }
return 0;
} }
/** /**

View File

@ -103,7 +103,7 @@ export class Weather {
const field = scene.getField(true); const field = scene.getField(true);
for (const pokemon of field) { for (const pokemon of field) {
let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0]; let suppressWeatherEffectAbAttr: SuppressWeatherEffectAbAttr | null = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0];
if (!suppressWeatherEffectAbAttr) { if (!suppressWeatherEffectAbAttr) {
suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr)[0] : null; 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) { switch (weatherType) {
case WeatherType.SUNNY: case WeatherType.SUNNY:
return i18next.t("weather:sunnyStartMessage"); return i18next.t("weather:sunnyStartMessage");
@ -141,7 +141,7 @@ export function getWeatherStartMessage(weatherType: WeatherType): string {
return null; return null;
} }
export function getWeatherLapseMessage(weatherType: WeatherType): string { export function getWeatherLapseMessage(weatherType: WeatherType): string | null {
switch (weatherType) { switch (weatherType) {
case WeatherType.SUNNY: case WeatherType.SUNNY:
return i18next.t("weather:sunnyLapseMessage"); return i18next.t("weather:sunnyLapseMessage");
@ -166,7 +166,7 @@ export function getWeatherLapseMessage(weatherType: WeatherType): string {
return null; return null;
} }
export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon): string { export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon): string | null {
switch (weatherType) { switch (weatherType) {
case WeatherType.SANDSTORM: case WeatherType.SANDSTORM:
return i18next.t("weather:sandstormDamageMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)}); return i18next.t("weather:sandstormDamageMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)});
@ -177,7 +177,7 @@ export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokem
return null; return null;
} }
export function getWeatherClearMessage(weatherType: WeatherType): string { export function getWeatherClearMessage(weatherType: WeatherType): string | null {
switch (weatherType) { switch (weatherType) {
case WeatherType.SUNNY: case WeatherType.SUNNY:
return i18next.t("weather:sunnyClearMessage"); return i18next.t("weather:sunnyClearMessage");
@ -202,7 +202,7 @@ export function getWeatherClearMessage(weatherType: WeatherType): string {
return null; return null;
} }
export function getTerrainStartMessage(terrainType: TerrainType): string { export function getTerrainStartMessage(terrainType: TerrainType): string | null {
switch (terrainType) { switch (terrainType) {
case TerrainType.MISTY: case TerrainType.MISTY:
return i18next.t("terrain:mistyStartMessage"); return i18next.t("terrain:mistyStartMessage");
@ -212,10 +212,13 @@ export function getTerrainStartMessage(terrainType: TerrainType): string {
return i18next.t("terrain:grassyStartMessage"); return i18next.t("terrain:grassyStartMessage");
case TerrainType.PSYCHIC: case TerrainType.PSYCHIC:
return i18next.t("terrain:psychicStartMessage"); 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) { switch (terrainType) {
case TerrainType.MISTY: case TerrainType.MISTY:
return i18next.t("terrain:mistyClearMessage"); return i18next.t("terrain:mistyClearMessage");
@ -225,6 +228,9 @@ export function getTerrainClearMessage(terrainType: TerrainType): string {
return i18next.t("terrain:grassyClearMessage"); return i18next.t("terrain:grassyClearMessage");
case TerrainType.PSYCHIC: case TerrainType.PSYCHIC:
return i18next.t("terrain:psychicClearMessage"); return i18next.t("terrain:psychicClearMessage");
default:
console.warn("getTerrainClearMessage not defined. Using default null");
return null;
} }
} }

View File

@ -84,7 +84,7 @@ export class EggHatchPhase extends Phase {
this.scene.gameData.eggs.splice(eggIndex, 1); 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; this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler;
@ -234,8 +234,8 @@ export class EggHatchPhase extends Phase {
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
duration: 250, duration: 250,
onComplete: () => { onComplete: () => {
count++; count!++;
if (count < repeatCount) { if (count! < repeatCount!) { // we know they are defined
return this.doEggShake(intensity, repeatCount, count).then(() => resolve()); return this.doEggShake(intensity, repeatCount, count).then(() => resolve());
} }
this.scene.tweens.add({ 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.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => { this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => { this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => {
this.scene.ui.showText(null, 0); this.scene.ui.showText("", 0);
this.end(); this.end();
}); });
}); });
@ -447,6 +447,6 @@ export class EggHatchPhase extends Phase {
}, this.egg.id, EGG_SEED.toString()); }, this.egg.id, EGG_SEED.toString());
return ret; return ret!;
} }
} }

View File

@ -21,5 +21,6 @@ export enum ArenaTagType {
MAT_BLOCK = "MAT_BLOCK", MAT_BLOCK = "MAT_BLOCK",
CRAFTY_SHIELD = "CRAFTY_SHIELD", CRAFTY_SHIELD = "CRAFTY_SHIELD",
TAILWIND = "TAILWIND", TAILWIND = "TAILWIND",
HAPPY_HOUR = "HAPPY_HOUR" HAPPY_HOUR = "HAPPY_HOUR",
NO_CRIT = "NO_CRIT"
} }

View File

@ -48,7 +48,6 @@ export enum BattlerTagType {
FIRE_BOOST = "FIRE_BOOST", FIRE_BOOST = "FIRE_BOOST",
CRIT_BOOST = "CRIT_BOOST", CRIT_BOOST = "CRIT_BOOST",
ALWAYS_CRIT = "ALWAYS_CRIT", ALWAYS_CRIT = "ALWAYS_CRIT",
NO_CRIT = "NO_CRIT",
IGNORE_ACCURACY = "IGNORE_ACCURACY", IGNORE_ACCURACY = "IGNORE_ACCURACY",
BYPASS_SLEEP = "BYPASS_SLEEP", BYPASS_SLEEP = "BYPASS_SLEEP",
IGNORE_FLYING = "IGNORE_FLYING", IGNORE_FLYING = "IGNORE_FLYING",
@ -63,5 +62,9 @@ export enum BattlerTagType {
ICE_FACE = "ICE_FACE", ICE_FACE = "ICE_FACE",
STOCKPILING = "STOCKPILING", STOCKPILING = "STOCKPILING",
RECEIVE_DOUBLE_DAMAGE = "RECEIVE_DOUBLE_DAMAGE", 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"
} }

View File

@ -1,4 +1,3 @@
export enum TrainerType { export enum TrainerType {
UNKNOWN, UNKNOWN,
@ -53,17 +52,26 @@ export enum TrainerType {
WORKER, WORKER,
YOUNGSTER, YOUNGSTER,
ROCKET_GRUNT, ROCKET_GRUNT,
ROCKET_ADMIN, ARCHER,
ARIANA,
PROTON,
PETREL,
MAGMA_GRUNT, MAGMA_GRUNT,
MAGMA_ADMIN, TABITHA,
COURTNEY,
AQUA_GRUNT, AQUA_GRUNT,
AQUA_ADMIN, MATT,
SHELLY,
GALACTIC_GRUNT, GALACTIC_GRUNT,
GALACTIC_ADMIN, JUPITER,
MARS,
SATURN,
PLASMA_GRUNT, PLASMA_GRUNT,
PLASMA_SAGE, ZINZOLIN,
ROOD,
FLARE_GRUNT, FLARE_GRUNT,
FLARE_ADMIN, BRYONY,
XEROSIC,
ROCKET_BOSS_GIOVANNI_1, ROCKET_BOSS_GIOVANNI_1,
ROCKET_BOSS_GIOVANNI_2, ROCKET_BOSS_GIOVANNI_2,
MAXIE, MAXIE,

View File

@ -81,8 +81,8 @@ export class TagAddedEvent extends ArenaEvent {
this.arenaTagType = arenaTagType; this.arenaTagType = arenaTagType;
this.arenaTagSide = arenaTagSide; this.arenaTagSide = arenaTagSide;
this.arenaTagLayers = arenaTagLayers; this.arenaTagLayers = arenaTagLayers!; // TODO: is this bang correct?
this.arenaTagMaxLayers = arenaTagMaxLayers; this.arenaTagMaxLayers = arenaTagMaxLayers!; // TODO: is this bang correct?
} }
} }
/** /**

View File

@ -16,7 +16,7 @@ export class EvolutionPhase extends Phase {
protected pokemon: PlayerPokemon; protected pokemon: PlayerPokemon;
protected lastLevel: integer; protected lastLevel: integer;
private evolution: SpeciesFormEvolution; private evolution: SpeciesFormEvolution | null;
protected evolutionContainer: Phaser.GameObjects.Container; protected evolutionContainer: Phaser.GameObjects.Container;
protected evolutionBaseBg: Phaser.GameObjects.Image; protected evolutionBaseBg: Phaser.GameObjects.Image;
@ -28,7 +28,7 @@ export class EvolutionPhase extends Phase {
protected pokemonEvoSprite: Phaser.GameObjects.Sprite; protected pokemonEvoSprite: Phaser.GameObjects.Sprite;
protected pokemonEvoTintSprite: 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); super(scene);
this.pokemon = pokemon; this.pokemon = pokemon;
@ -53,7 +53,7 @@ export class EvolutionPhase extends Phase {
return this.end(); return this.end();
} }
this.scene.fadeOutBgm(null, false); this.scene.fadeOutBgm(undefined, false);
const evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler; 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:stoppedEvolving", { pokemonName: preName }), null, () => {
this.scene.ui.showText(i18next.t("menu:pauseEvolutionsQuestion", { pokemonName: preName }), null, () => { this.scene.ui.showText(i18next.t("menu:pauseEvolutionsQuestion", { pokemonName: preName }), null, () => {
const end = () => { const end = () => {
this.scene.ui.showText(null, 0); this.scene.ui.showText("", 0);
this.scene.playBgm(); this.scene.playBgm();
evolvedPokemon.destroy(); evolvedPokemon.destroy();
this.end(); this.end();

View File

@ -25,8 +25,8 @@ import { TrainerType } from "#enums/trainer-type";
export class Arena { export class Arena {
public scene: BattleScene; public scene: BattleScene;
public biomeType: Biome; public biomeType: Biome;
public weather: Weather; public weather: Weather | null;
public terrain: Terrain; public terrain: Terrain | null;
public tags: ArenaTag[]; public tags: ArenaTag[];
public bgm: string; public bgm: string;
public ignoreAbilities: boolean; public ignoreAbilities: boolean;
@ -121,7 +121,7 @@ export class Arena {
} }
} }
ret = getPokemonSpecies(species); ret = getPokemonSpecies(species!);
if (ret.subLegendary || ret.legendary || ret.mythical) { if (ret.subLegendary || ret.legendary || ret.mythical) {
switch (true) { switch (true) {
@ -292,7 +292,7 @@ export class Arena {
trySetWeatherOverride(weather: WeatherType): boolean { trySetWeatherOverride(weather: WeatherType): boolean {
this.weather = new Weather(weather, 0); this.weather = new Weather(weather, 0);
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); 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; return true;
} }
@ -314,13 +314,13 @@ export class Arena {
const oldWeatherType = this.weather?.weatherType || WeatherType.NONE; const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null; 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) { if (this.weather) {
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); 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 { } 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 => { this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => {
@ -339,15 +339,15 @@ export class Arena {
const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE; const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE;
this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null; 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 (this.terrain) {
if (!ignoreAnim) { if (!ignoreAnim) {
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1))); 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 { } 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 => { this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => {
@ -554,7 +554,7 @@ export class Arena {
this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args); 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); const existingTag = this.getTagOnSide(tagType, side);
if (existingTag) { if (existingTag) {
existingTag.onOverlap(this); existingTag.onOverlap(this);
@ -568,21 +568,23 @@ export class Arena {
} }
const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side); const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side);
if (newTag) {
this.tags.push(newTag); this.tags.push(newTag);
newTag.onAdd(this, quiet); 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; return true;
} }
getTag(tagType: ArenaTagType | Constructor<ArenaTag>): ArenaTag { getTag(tagType: ArenaTagType | Constructor<ArenaTag>): ArenaTag | undefined {
return this.getTagOnSide(tagType, ArenaTagSide.BOTH); return this.getTagOnSide(tagType, ArenaTagSide.BOTH);
} }
getTagOnSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide): ArenaTag { getTagOnSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide): ArenaTag | undefined {
return typeof(tagType) === "string" 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.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)); : 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; return 0.000;
case Biome.SNOWY_FOREST: case Biome.SNOWY_FOREST:
return 3.047; 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.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.base.setOrigin(0, 0);
this.props = !player ? this.props = !player ?
new Array(3).fill(null).map(() => { 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.setOrigin(0, 0);
ret.setVisible(false); ret.setVisible(false);
return ret; return ret;

View File

@ -3,6 +3,8 @@ import Pokemon, { DamageResult, HitResult } from "./pokemon";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { BattlerIndex } from "../battle"; import { BattlerIndex } from "../battle";
type TextAndShadowArr = [ string | null, string | null ];
export default class DamageNumberHandler { export default class DamageNumberHandler {
private damageNumbers: Map<BattlerIndex, Phaser.GameObjects.Text[]>; private damageNumbers: Map<BattlerIndex, Phaser.GameObjects.Text[]>;
@ -24,7 +26,7 @@ export default class DamageNumberHandler {
damageNumber.setOrigin(0.5, 1); damageNumber.setOrigin(0.5, 1);
damageNumber.setScale(baseScale); damageNumber.setScale(baseScale);
let [ textColor, shadowColor ] = [ null, null ]; let [ textColor, shadowColor ] : TextAndShadowArr = [ null, null ];
switch (result) { switch (result) {
case HitResult.SUPER_EFFECTIVE: case HitResult.SUPER_EFFECTIVE:
@ -62,12 +64,12 @@ export default class DamageNumberHandler {
this.damageNumbers.set(battlerIndex, []); this.damageNumbers.set(battlerIndex, []);
} }
const yOffset = this.damageNumbers.get(battlerIndex).length * -10; const yOffset = this.damageNumbers.get(battlerIndex)!.length * -10;
if (yOffset) { if (yOffset) {
damageNumber.y += yOffset; damageNumber.y += yOffset;
} }
this.damageNumbers.get(battlerIndex).push(damageNumber); this.damageNumbers.get(battlerIndex)!.push(damageNumber);
if (scene.damageNumbersMode === 1) { if (scene.damageNumbersMode === 1) {
scene.tweens.add({ scene.tweens.add({
@ -83,7 +85,7 @@ export default class DamageNumberHandler {
alpha: 0, alpha: 0,
ease: "Sine.easeIn", ease: "Sine.easeIn",
onComplete: () => { 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); damageNumber.destroy(true);
} }
}); });
@ -167,7 +169,7 @@ export default class DamageNumberHandler {
delay: Utils.fixedInt(500), delay: Utils.fixedInt(500),
alpha: 0, alpha: 0,
onComplete: () => { 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); damageNumber.destroy(true);
} }
} }

View File

@ -35,7 +35,7 @@ export default class PokemonSpriteSparkleHandler {
const ratioX = s.width / width; const ratioX = s.width / width;
const ratioY = s.height / height; const ratioY = s.height / height;
const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE"); 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 [ 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"); 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"]; sparkle.pipelineData["ignoreTimeTint"] = s.pipelineData["ignoreTimeTint"];

File diff suppressed because it is too large Load Diff

View File

@ -121,7 +121,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
// Determine the title to include based on the configuration and includeTitle flag. // Determine the title to include based on the configuration and includeTitle flag.
let title = includeTitle && this.config.title ? this.config.title : null; 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))) { 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 // 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, "_")}`); 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, "_")}`); 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 the formatted name, including the title if it is set.
return title ? `${title} ${name}` : name; return title ? `${title} ${name}` : name;
} }
@ -206,7 +208,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
} }
getPartyLevels(waveIndex: integer): integer[] { getPartyLevels(waveIndex: integer): integer[] {
const ret = []; const ret: number[] = [];
const partyTemplate = this.getPartyTemplate(); const partyTemplate = this.getPartyTemplate();
const difficultyWaveIndex = this.scene.gameMode.getWaveForDifficulty(waveIndex); const difficultyWaveIndex = this.scene.gameMode.getWaveForDifficulty(waveIndex);
@ -255,7 +257,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
genPartyMember(index: integer): EnemyPokemon { genPartyMember(index: integer): EnemyPokemon {
const battle = this.scene.currentBattle; const battle = this.scene.currentBattle;
const level = battle.enemyLevels[index]; const level = battle.enemyLevels?.[index]!; // TODO: is this bang correct?
let ret: EnemyPokemon; 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) // 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; 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) // 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 !species.some(s => AlreadyUsedSpecies.includes(s));
} }
return !AlreadyUsedSpecies.includes(species); return !AlreadyUsedSpecies.includes(species);
}); }).flat();
// Filter out the species that are already in the enemy party from the partner trainer species pool // Filter out the species that are already in the enemy party from the partner trainer species pool
const speciesPoolPartnerFiltered = speciesPoolPartner.filter(species => { 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 !species.some(s => AlreadyUsedSpecies.includes(s));
} }
return !AlreadyUsedSpecies.includes(species); 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) // 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); 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)); }, 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) { if (maxScorePartyMemberIndexes.length > 1) {
let rand: integer; let rand: integer;
this.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length), this.scene.currentBattle.turn << 2); this.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length), this.scene.currentBattle.turn << 2);
return maxScorePartyMemberIndexes[rand]; return maxScorePartyMemberIndexes[rand!];
} }
return maxScorePartyMemberIndexes[0]; return maxScorePartyMemberIndexes[0];
@ -497,6 +499,9 @@ export default class Trainer extends Phaser.GameObjects.Container {
return 0.45; return 0.45;
case PartyMemberStrength.STRONGER: case PartyMemberStrength.STRONGER:
return 0.375; return 0.375;
default:
console.warn("getPartyMemberModifierChanceMultiplier not defined. Using default 0");
return 0;
} }
} }

View File

@ -167,7 +167,7 @@ export class GameMode implements GameModeConfig {
} }
} }
getOverrideSpecies(waveIndex: integer): PokemonSpecies { getOverrideSpecies(waveIndex: integer): PokemonSpecies | null {
if (this.isDaily && this.isWaveFinal(waveIndex)) { if (this.isDaily && this.isWaveFinal(waveIndex)) {
const allFinalBossSpecies = allSpecies.filter(s => (s.subLegendary || s.legendary || s.mythical) const allFinalBossSpecies = allSpecies.filter(s => (s.subLegendary || s.legendary || s.mythical)
&& s.baseTotal >= 600 && s.speciesId !== Species.ETERNATUS && s.speciesId !== Species.ARCEUS); && 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 * @returns true if waveIndex is a multiple of 50 in Endless
*/ */
isEndlessBoss(waveIndex: integer): boolean { isEndlessBoss(waveIndex: integer): boolean {
return waveIndex % 50 && return !!(waveIndex % 50) &&
(this.modeId === GameModes.ENDLESS || this.modeId === GameModes.SPLICED_ENDLESS); (this.modeId === GameModes.ENDLESS || this.modeId === GameModes.SPLICED_ENDLESS);
} }
@ -267,6 +267,8 @@ export class GameMode implements GameModeConfig {
return 5000; return 5000;
case GameModes.DAILY: case GameModes.DAILY:
return 2500; return 2500;
default:
return 0;
} }
} }

Some files were not shown because too many files have changed in this diff Show More