Compare commits

...

18 Commits

Author SHA1 Message Date
Adrian Torrano
a561e41634
return boolean from changing locale (#848) 2024-05-19 13:25:24 -05:00
hayuna
840dba79cc
Implement Trump Card move (#816)
* Implement Trump Card move

* Add docs, rearrange order of conditions

* Add Pressure consequences

* Remove additional reducing PP if pressure

* Update comments to adjust ways of working
2024-05-19 13:17:58 -05:00
Matthew Olker
77e9d81e5f Fix Eternatus Eternamax Variant Animations 2024-05-19 14:02:05 -04:00
Jaime
df7cb209f7
Fix Curse targeting for non ghost types (#782) 2024-05-19 12:40:56 -05:00
Alvin Zou
661e37f54f
Add capability to view Pokemon Status screen when learning moves (#756) 2024-05-19 12:19:38 -05:00
td76099
1bc6fcf4de
Bugfix - Ghost types are no longer trapped by abilities like Shadow Tag or moves like Fire Spin (#731)
* Checks if Pokemon is Ghost type when determining if it is trapped

* Adding doc comments based on new standard

* Update ability.ts

---------

Co-authored-by: Benjamin Odom <bennybroseph@gmail.com>
2024-05-19 12:05:46 -05:00
Frederico Santos
3031cc1245
Implement Ball Fetch (#1129)
* Implemented Ball Fetch

* Added once per battle condition

* Ability only functions on player side

* Update ability.ts

---------

Co-authored-by: SamuelHudson <samuel.hudson2017@gmail.com>
Co-authored-by: Benjamin Odom <bennybroseph@gmail.com>
2024-05-19 11:13:33 -05:00
ElMoustacho
932e6e39f9
Make status damage skip endure (#597)
* Make status damage skip endure

* Add a comment to justify arguments to damage method
2024-05-19 10:10:57 -05:00
Alvin Zou
608a968db4
Implement Beat Up (#593) 2024-05-19 09:22:37 -05:00
NxKarim
1ae5847e49
Implements Merciless (#440)
* Implemented Merciless

Attribute CritIfTargetIsPoisonedAbAttr
Fix BlockCritAbAttr

* Changed CritIfTargetPoisonedAbAttr to ConditionalCritAbAttr

Now any condition related to user, target or the move used can be applied to guarantee a critical hit.
2024-05-19 09:06:58 -05:00
Matt Ross
e8c1098b91
Mostly implements upper hand (#433) 2024-05-19 09:04:19 -05:00
lucfd
78c8e8164d
Implemented Terrain Pulse & Misty Explosion (#235)
* implemented Terrain Pulse

* type only changes when grounded

* Implement Misty Explosion

* fixed grounded check

* added tsdoc

* Update move.ts

---------

Co-authored-by: Benjamin Odom <bennybroseph@gmail.com>
2024-05-19 08:31:13 -05:00
Lugiad
48313e0c4d
Adjustments to French modifier-type.ts (#1117)
* Adjustments to French modifier-type.ts

* Update modifier-type.ts

* Update modifier-type.ts

* Update modifier-type.ts

* Update modifier-type.ts
2024-05-19 08:08:19 -05:00
JO
5e9ded2b8e
Fix Close Combat TM for Granbull, Pignite, Emboar, and Scraggy (#1113) 2024-05-19 07:32:41 -05:00
Lugiad
d017198460
Font resize for French in starterInfoTextSize (#1110) 2024-05-19 07:28:18 -05:00
Benjamin Odom
0914fd14e2
Revert "Generated .json for variant sprites. (#1116)" (#1124)
This reverts commit 8b559532b0.
2024-05-19 07:00:23 -05:00
Justin Gourlay
8b559532b0
Generated .json for variant sprites. (#1116) 2024-05-19 06:44:45 -05:00
Jannik Tappert
e3b25a9fab
So currently if you meet someone with a title it only shows there nam… (#1055)
* So currently if you meet someone with a title it only shows there name and only then the title (not like trainer classes). So i wanted to change that

* The title is not set again to its own value. It is just got differenlty for the nametag (includeTitle true)

* Added the ; as well as some comments that are hopefully helpful for others (i am terrible at writing comments)

* Forgot getTitle for comments
2024-05-19 06:23:24 -05:00
17 changed files with 1935 additions and 79 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -0,0 +1,755 @@
{
"textures": [
{
"image": "890-eternamax_2.png",
"format": "RGBA8888",
"size": {
"w": 579,
"h": 579
},
"scale": 1,
"frames": [
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 100,
"h": 98
},
"frame": {
"x": 0,
"y": 0,
"w": 100,
"h": 98
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 95,
"h": 100
},
"frame": {
"x": 100,
"y": 0,
"w": 95,
"h": 100
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 8,
"w": 91,
"h": 100
},
"frame": {
"x": 0,
"y": 98,
"w": 91,
"h": 100
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 96,
"h": 98
},
"frame": {
"x": 91,
"y": 100,
"w": 96,
"h": 98
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 95,
"h": 99
},
"frame": {
"x": 187,
"y": 100,
"w": 95,
"h": 99
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 10,
"w": 91,
"h": 98
},
"frame": {
"x": 0,
"y": 198,
"w": 91,
"h": 98
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 10,
"w": 88,
"h": 98
},
"frame": {
"x": 91,
"y": 198,
"w": 88,
"h": 98
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 10,
"w": 95,
"h": 97
},
"frame": {
"x": 195,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 179,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 274,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 290,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 94,
"h": 96
},
"frame": {
"x": 282,
"y": 97,
"w": 94,
"h": 96
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 90,
"h": 97
},
"frame": {
"x": 369,
"y": 193,
"w": 90,
"h": 97
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 13,
"w": 93,
"h": 95
},
"frame": {
"x": 385,
"y": 0,
"w": 93,
"h": 95
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 9,
"w": 91,
"h": 96
},
"frame": {
"x": 385,
"y": 95,
"w": 91,
"h": 96
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 97
},
"frame": {
"x": 369,
"y": 290,
"w": 87,
"h": 97
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 90,
"h": 96
},
"frame": {
"x": 456,
"y": 290,
"w": 90,
"h": 96
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 8,
"w": 90,
"h": 96
},
"frame": {
"x": 459,
"y": 191,
"w": 90,
"h": 96
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 8,
"w": 90,
"h": 95
},
"frame": {
"x": 476,
"y": 95,
"w": 90,
"h": 95
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 478,
"y": 0,
"w": 89,
"h": 95
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 96
},
"frame": {
"x": 456,
"y": 386,
"w": 89,
"h": 96
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 0,
"y": 296,
"w": 89,
"h": 95
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 14,
"w": 89,
"h": 94
},
"frame": {
"x": 89,
"y": 296,
"w": 89,
"h": 94
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 88,
"h": 95
},
"frame": {
"x": 178,
"y": 296,
"w": 88,
"h": 95
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 95
},
"frame": {
"x": 89,
"y": 390,
"w": 87,
"h": 95
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 94
},
"frame": {
"x": 0,
"y": 391,
"w": 89,
"h": 94
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 266,
"y": 387,
"w": 89,
"h": 93
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 13,
"w": 85,
"h": 91
},
"frame": {
"x": 266,
"y": 296,
"w": 85,
"h": 91
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 13,
"w": 88,
"h": 94
},
"frame": {
"x": 176,
"y": 391,
"w": 88,
"h": 94
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 13,
"w": 87,
"h": 94
},
"frame": {
"x": 355,
"y": 387,
"w": 87,
"h": 94
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 11,
"w": 87,
"h": 94
},
"frame": {
"x": 264,
"y": 480,
"w": 87,
"h": 94
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 351,
"y": 481,
"w": 89,
"h": 93
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 93
},
"frame": {
"x": 440,
"y": 482,
"w": 87,
"h": 93
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 10,
"w": 86,
"h": 94
},
"frame": {
"x": 0,
"y": 485,
"w": 86,
"h": 94
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 14,
"w": 85,
"h": 91
},
"frame": {
"x": 86,
"y": 485,
"w": 85,
"h": 91
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$"
}
}

View File

@ -0,0 +1,755 @@
{
"textures": [
{
"image": "890-eternamax_3.png",
"format": "RGBA8888",
"size": {
"w": 579,
"h": 579
},
"scale": 1,
"frames": [
{
"filename": "0035.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 100,
"h": 98
},
"frame": {
"x": 0,
"y": 0,
"w": 100,
"h": 98
}
},
{
"filename": "0031.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 8,
"w": 95,
"h": 100
},
"frame": {
"x": 100,
"y": 0,
"w": 95,
"h": 100
}
},
{
"filename": "0029.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 8,
"w": 91,
"h": 100
},
"frame": {
"x": 0,
"y": 98,
"w": 91,
"h": 100
}
},
{
"filename": "0001.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 96,
"h": 98
},
"frame": {
"x": 91,
"y": 100,
"w": 96,
"h": 98
}
},
{
"filename": "0032.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 9,
"w": 95,
"h": 99
},
"frame": {
"x": 187,
"y": 100,
"w": 95,
"h": 99
}
},
{
"filename": "0030.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 10,
"w": 91,
"h": 98
},
"frame": {
"x": 0,
"y": 198,
"w": 91,
"h": 98
}
},
{
"filename": "0007.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 10,
"w": 88,
"h": 98
},
"frame": {
"x": 91,
"y": 198,
"w": 88,
"h": 98
}
},
{
"filename": "0002.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 10,
"w": 95,
"h": 97
},
"frame": {
"x": 195,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0003.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 179,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0004.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 274,
"y": 199,
"w": 95,
"h": 97
}
},
{
"filename": "0033.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 95,
"h": 97
},
"frame": {
"x": 290,
"y": 0,
"w": 95,
"h": 97
}
},
{
"filename": "0034.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 11,
"w": 94,
"h": 96
},
"frame": {
"x": 282,
"y": 97,
"w": 94,
"h": 96
}
},
{
"filename": "0028.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 90,
"h": 97
},
"frame": {
"x": 369,
"y": 193,
"w": 90,
"h": 97
}
},
{
"filename": "0005.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 8,
"y": 13,
"w": 93,
"h": 95
},
"frame": {
"x": 385,
"y": 0,
"w": 93,
"h": 95
}
},
{
"filename": "0017.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 9,
"w": 91,
"h": 96
},
"frame": {
"x": 385,
"y": 95,
"w": 91,
"h": 96
}
},
{
"filename": "0008.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 97
},
"frame": {
"x": 369,
"y": 290,
"w": 87,
"h": 97
}
},
{
"filename": "0009.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 90,
"h": 96
},
"frame": {
"x": 456,
"y": 290,
"w": 90,
"h": 96
}
},
{
"filename": "0015.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 8,
"w": 90,
"h": 96
},
"frame": {
"x": 459,
"y": 191,
"w": 90,
"h": 96
}
},
{
"filename": "0016.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 8,
"w": 90,
"h": 95
},
"frame": {
"x": 476,
"y": 95,
"w": 90,
"h": 95
}
},
{
"filename": "0014.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 478,
"y": 0,
"w": 89,
"h": 95
}
},
{
"filename": "0027.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 96
},
"frame": {
"x": 456,
"y": 386,
"w": 89,
"h": 96
}
},
{
"filename": "0025.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 11,
"w": 89,
"h": 95
},
"frame": {
"x": 0,
"y": 296,
"w": 89,
"h": 95
}
},
{
"filename": "0006.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 9,
"y": 14,
"w": 89,
"h": 94
},
"frame": {
"x": 89,
"y": 296,
"w": 89,
"h": 94
}
},
{
"filename": "0011.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 88,
"h": 95
},
"frame": {
"x": 178,
"y": 296,
"w": 88,
"h": 95
}
},
{
"filename": "0023.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 95
},
"frame": {
"x": 89,
"y": 390,
"w": 87,
"h": 95
}
},
{
"filename": "0013.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 12,
"w": 89,
"h": 94
},
"frame": {
"x": 0,
"y": 391,
"w": 89,
"h": 94
}
},
{
"filename": "0012.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 266,
"y": 387,
"w": 89,
"h": 93
}
},
{
"filename": "0019.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 13,
"w": 85,
"h": 91
},
"frame": {
"x": 266,
"y": 296,
"w": 85,
"h": 91
}
},
{
"filename": "0024.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 13,
"w": 88,
"h": 94
},
"frame": {
"x": 176,
"y": 391,
"w": 88,
"h": 94
}
},
{
"filename": "0010.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 13,
"w": 87,
"h": 94
},
"frame": {
"x": 355,
"y": 387,
"w": 87,
"h": 94
}
},
{
"filename": "0018.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 16,
"y": 11,
"w": 87,
"h": 94
},
"frame": {
"x": 264,
"y": 480,
"w": 87,
"h": 94
}
},
{
"filename": "0026.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 11,
"y": 14,
"w": 89,
"h": 93
},
"frame": {
"x": 351,
"y": 481,
"w": 89,
"h": 93
}
},
{
"filename": "0022.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 12,
"y": 11,
"w": 87,
"h": 93
},
"frame": {
"x": 440,
"y": 482,
"w": 87,
"h": 93
}
},
{
"filename": "0021.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 10,
"w": 86,
"h": 94
},
"frame": {
"x": 0,
"y": 485,
"w": 86,
"h": 94
}
},
{
"filename": "0020.png",
"rotated": false,
"trimmed": true,
"sourceSize": {
"w": 112,
"h": 112
},
"spriteSourceSize": {
"x": 13,
"y": 14,
"w": 85,
"h": 91
},
"frame": {
"x": 86,
"y": 485,
"w": 85,
"h": 91
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:8fd9e1830200ec8e4aac8571cc2d27a6:c966e3efce03c7bae43d7bca6d6dfa62:cedd2711a12bbacba5623505fe88bd92$"
}
}

View File

@ -11,6 +11,7 @@ import { BattleSpec } from "./enums/battle-spec";
import { PlayerGender } from "./system/game-data"; import { PlayerGender } from "./system/game-data";
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier"; import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
import { MoneyAchv } from "./system/achv"; import { MoneyAchv } from "./system/achv";
import { PokeballType } from "./data/pokeball";
export enum BattleType { export enum BattleType {
WILD, WILD,
@ -61,6 +62,7 @@ export default class Battle {
public battleSeed: string; public battleSeed: string;
private battleSeedState: string; private battleSeedState: string;
public moneyScattered: number; public moneyScattered: number;
public lastUsedPokeball: PokeballType;
private rngCounter: integer = 0; private rngCounter: integer = 0;
@ -86,6 +88,7 @@ export default class Battle {
this.battleSeed = Utils.randomString(16, true); this.battleSeed = Utils.randomString(16, true);
this.battleSeedState = null; this.battleSeedState = null;
this.moneyScattered = 0; this.moneyScattered = 0;
this.lastUsedPokeball = null;
} }
private initBattleSpec(): void { private initBattleSpec(): void {

View File

@ -22,6 +22,7 @@ import i18next, { Localizable } from "#app/plugins/i18n.js";
import { Command } from "../ui/command-ui-handler"; import { Command } from "../ui/command-ui-handler";
import Battle from "#app/battle.js"; import Battle from "#app/battle.js";
import { ability } from "#app/locales/en/ability.js"; import { ability } from "#app/locales/en/ability.js";
import { PokeballType, getPokeballName } from "./pokeball";
export class Ability implements Localizable { export class Ability implements Localizable {
public id: Abilities; public id: Abilities;
@ -1822,6 +1823,36 @@ export class MultCritAbAttr extends AbAttr {
} }
} }
/**
* Guarantees a critical hit according to the given condition, except if target prevents critical hits. ie. Merciless
* @extends AbAttr
* @see {@linkcode apply}
*/
export class ConditionalCritAbAttr extends AbAttr {
private condition: PokemonAttackCondition;
constructor(condition: PokemonAttackCondition, checkUser?: Boolean) {
super();
this.condition = condition;
}
/**
* @param pokemon {@linkcode Pokemon} user.
* @param args [0] {@linkcode Utils.BooleanHolder} If true critical hit is guaranteed.
* [1] {@linkcode Pokemon} Target.
* [2] {@linkcode Move} used by ability user.
*/
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const target = (args[1] as Pokemon);
const move = (args[2] as Move);
if(!this.condition(pokemon,target,move))
return false;
(args[0] as Utils.BooleanHolder).value = true;
return true;
}
}
export class BlockNonDirectDamageAbAttr extends AbAttr { export class BlockNonDirectDamageAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
@ -2246,6 +2277,33 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr {
} }
} }
/**
* Grabs the last failed Pokeball used
* @extends PostTurnAbAttr
* @see {@linkcode applyPostTurn} */
export class FetchBallAbAttr extends PostTurnAbAttr {
constructor() {
super();
}
/**
* Adds the last used Pokeball back into the player's inventory
* @param pokemon {@linkcode Pokemon} with this ability
* @param passive N/A
* @param args N/A
* @returns true if player has used a pokeball and this pokemon is owned by the player
*/
applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
let lastUsed = pokemon.scene.currentBattle.lastUsedPokeball;
if(lastUsed != null && pokemon.isPlayer) {
pokemon.scene.pokeballCounts[lastUsed]++;
pokemon.scene.currentBattle.lastUsedPokeball = null;
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` found a\n${getPokeballName(lastUsed)}!`));
return true;
}
return false;
}
}
export class PostBiomeChangeAbAttr extends AbAttr { } export class PostBiomeChangeAbAttr extends AbAttr { }
export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr {
@ -2338,18 +2396,42 @@ export class RunSuccessAbAttr extends AbAttr {
} }
} }
/**
* Base class for checking if a Pokemon is trapped by arena trap
* @extends AbAttr
* @see {@linkcode applyCheckTrapped}
*/
export class CheckTrappedAbAttr extends AbAttr { export class CheckTrappedAbAttr extends AbAttr {
constructor() { constructor() {
super(false); super(false);
} }
applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> { applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean | Promise<boolean> {
return false; return false;
} }
} }
/**
* Determines whether a Pokemon is blocked from switching/running away
* because of a trapping ability or move.
* @extends CheckTrappedAbAttr
* @see {@linkcode applyCheckTrapped}
*/
export class ArenaTrapAbAttr extends CheckTrappedAbAttr { export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, args: any[]): boolean { /**
* Checks if enemy Pokemon is trapped by an Arena Trap-esque ability
* @param pokemon The {@link Pokemon} with this {@link AbAttr}
* @param passive N/A
* @param trapped {@link Utils.BooleanHolder} indicating whether the other Pokemon is trapped or not
* @param otherPokemon The {@link Pokemon} that is affected by an Arena Trap ability
* @param args N/A
* @returns if enemy Pokemon is trapped or not
*/
applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean {
if (otherPokemon.getTypes().includes(Type.GHOST)){
trapped.value = false;
return false;
}
trapped.value = true; trapped.value = true;
return true; return true;
} }
@ -2614,7 +2696,6 @@ export class SuppressFieldAbilitiesAbAttr extends AbAttr {
} }
} }
export class AlwaysHitAbAttr extends AbAttr { } export class AlwaysHitAbAttr extends AbAttr { }
export class UncopiableAbilityAbAttr extends AbAttr { export class UncopiableAbilityAbAttr extends AbAttr {
@ -2859,8 +2940,8 @@ export function applyPostTerrainChangeAbAttrs(attrType: { new(...args: any[]): P
} }
export function applyCheckTrappedAbAttrs(attrType: { new(...args: any[]): CheckTrappedAbAttr }, export function applyCheckTrappedAbAttrs(attrType: { new(...args: any[]): CheckTrappedAbAttr },
pokemon: Pokemon, trapped: Utils.BooleanHolder, ...args: any[]): Promise<void> { pokemon: Pokemon, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<CheckTrappedAbAttr>(attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, trapped, args), args, true); return applyAbAttrsInternal<CheckTrappedAbAttr>(attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, trapped, otherPokemon, args), args, true);
} }
export function applyPostBattleAbAttrs(attrType: { new(...args: any[]): PostBattleAbAttr }, export function applyPostBattleAbAttrs(attrType: { new(...args: any[]): PostBattleAbAttr },
@ -3442,7 +3523,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)
.unimplemented(), .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, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) .attr(PostBattleInitFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 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))
@ -3597,7 +3678,8 @@ export function initAbilities() {
new Ability(Abilities.LIBERO, 8) new Ability(Abilities.LIBERO, 8)
.unimplemented(), .unimplemented(),
new Ability(Abilities.BALL_FETCH, 8) new Ability(Abilities.BALL_FETCH, 8)
.unimplemented(), .attr(FetchBallAbAttr)
.condition(getOncePerBattleCondition(Abilities.BALL_FETCH)),
new Ability(Abilities.COTTON_DOWN, 8) new Ability(Abilities.COTTON_DOWN, 8)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.SPD, -1, false, true) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.SPD, -1, false, true)
.bypassFaint(), .bypassFaint(),

View File

@ -58,7 +58,8 @@ export enum MoveTarget {
/** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Entry_hazard-creating_moves Entry hazard-creating moves} */ /** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Entry_hazard-creating_moves Entry hazard-creating moves} */
ENEMY_SIDE, ENEMY_SIDE,
BOTH_SIDES, BOTH_SIDES,
PARTY PARTY,
CURSE
} }
export enum MoveFlags { export enum MoveFlags {
@ -916,6 +917,7 @@ export enum MultiHitType {
_3, _3,
_3_INCR, _3_INCR,
_1_TO_10, _1_TO_10,
BEAT_UP,
} }
/** /**
@ -1233,6 +1235,11 @@ export class MultiHitAttr extends MoveAttr {
hitTimes = 10; hitTimes = 10;
} }
break; break;
case MultiHitType.BEAT_UP:
// No status means the ally pokemon can contribute to Beat Up
hitTimes = user.scene.getParty().reduce((total, pokemon) => {
return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1)
}, 0);
} }
(args[0] as Utils.IntegerHolder).value = hitTimes; (args[0] as Utils.IntegerHolder).value = hitTimes;
return true; return true;
@ -2034,6 +2041,46 @@ export class VariablePowerAttr extends MoveAttr {
} }
} }
export class LessPPMorePowerAttr extends VariablePowerAttr {
/**
* Power up moves when less PP user has
* @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} being used
* @param args [0] {@linkcode Utils.NumberHolder} of power
* @returns true if the function succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const ppMax = move.pp;
let ppUsed = user.moveset.find((m) => m.moveId === move.id).ppUsed;
let ppRemains = ppMax - ppUsed;
/** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */
if(ppRemains < 0) ppRemains = 0;
const power = args[0] as Utils.NumberHolder;
switch (ppRemains) {
case 0:
power.value = 200;
break;
case 1:
power.value = 80;
break;
case 2:
power.value = 60;
break;
case 3:
power.value = 50;
break;
default:
power.value = 40;
break;
}
return true;
}
}
export class MovePowerMultiplierAttr extends VariablePowerAttr { export class MovePowerMultiplierAttr extends VariablePowerAttr {
private powerMultiplierFunc: (user: Pokemon, target: Pokemon, move: Move) => number; private powerMultiplierFunc: (user: Pokemon, target: Pokemon, move: Move) => number;
@ -2051,6 +2098,45 @@ export class MovePowerMultiplierAttr extends VariablePowerAttr {
} }
} }
/**
* Helper function to calculate the the base power of an ally's hit when using Beat Up.
* @param user The Pokemon that used Beat Up.
* @param allyIndex The party position of the ally contributing to Beat Up.
* @returns The base power of the Beat Up hit.
*/
const beatUpFunc = (user: Pokemon, allyIndex: number): number => {
const party = user.scene.getParty();
for (let i = allyIndex; i < party.length; i++) {
const pokemon = party[i];
// The user contributes to Beat Up regardless of status condition.
// Allies can contribute only if they do not have a non-volatile status condition.
if (pokemon.id !== user.id && pokemon?.status && pokemon.status.effect !== StatusEffect.NONE) {
continue;
}
return (pokemon.species.getBaseStat(Stat.ATK) / 10) + 5;
}
}
export class BeatUpAttr extends VariablePowerAttr {
/**
* Gets the next party member to contribute to a Beat Up hit, and calculates the base power for it.
* @param user Pokemon that used the move
* @param _target N/A
* @param _move Move with this attribute
* @param args N/A
* @returns true if the function succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const power = args[0] as Utils.NumberHolder;
const allyIndex = user.turnData.hitCount - user.turnData.hitsLeft;
power.value = beatUpFunc(user, allyIndex);
return true;
}
}
const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => {
let message: string = null; let message: string = null;
user.scene.executeWithSeedOffset(() => { user.scene.executeWithSeedOffset(() => {
@ -2778,6 +2864,47 @@ export class WeatherBallTypeAttr extends VariableMoveTypeAttr {
} }
} }
/**
* Changes the move's type to match the current terrain.
* Has no effect if the user is not grounded.
* @extends VariableMoveTypeAttr
* @see {@linkcode apply}
*/
export class TerrainPulseTypeAttr extends VariableMoveTypeAttr {
/**
* @param user {@linkcode Pokemon} using this move
* @param target N/A
* @param move N/A
* @param args [0] {@linkcode Utils.IntegerHolder} The move's type to be modified
* @returns true if the function succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if(!user.isGrounded)
return false;
const currentTerrain = user.scene.arena.getTerrainType();
const type = (args[0] as Utils.IntegerHolder);
switch (currentTerrain) {
case TerrainType.MISTY:
type.value = Type.FAIRY;
break;
case TerrainType.ELECTRIC:
type.value = Type.ELECTRIC;
break;
case TerrainType.GRASSY:
type.value = Type.GRASS;
break;
case TerrainType.PSYCHIC:
type.value = Type.PSYCHIC;
break;
default:
return false;
}
return true;
}
}
export class HiddenPowerTypeAttr extends VariableMoveTypeAttr { export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const type = (args[0] as Utils.IntegerHolder); const type = (args[0] as Utils.IntegerHolder);
@ -4411,6 +4538,9 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet {
set = [ user, user.getAlly() ].concat(opponents); set = [ user, user.getAlly() ].concat(opponents);
multiple = true; multiple = true;
break; break;
case MoveTarget.CURSE:
set = user.getTypes(true).includes(Type.GHOST) ? (opponents.concat([ user.getAlly() ])) : [ user ];
break;
} }
return { targets: set.filter(p => p?.isActive(true)).map(p => p.getBattlerIndex()).filter(t => t !== undefined), multiple }; return { targets: set.filter(p => p?.isActive(true)).map(p => p.getBattlerIndex()).filter(t => t !== undefined), multiple };
@ -4897,7 +5027,8 @@ export function initMoves() {
.soundBased(), .soundBased(),
new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2) new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2)
.attr(CurseAttr) .attr(CurseAttr)
.ignoresProtect(true), .ignoresProtect(true)
.target(MoveTarget.CURSE),
new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2)
.attr(LowHpPowerAttr), .attr(LowHpPowerAttr),
new StatusMove(Moves.CONVERSION_2, Type.NORMAL, -1, 30, -1, 0, 2) new StatusMove(Moves.CONVERSION_2, Type.NORMAL, -1, 30, -1, 0, 2)
@ -5115,8 +5246,9 @@ export function initMoves() {
.attr(TrapAttr, BattlerTagType.WHIRLPOOL) .attr(TrapAttr, BattlerTagType.WHIRLPOOL)
.attr(HitsTagAttr, BattlerTagType.UNDERWATER, true), .attr(HitsTagAttr, BattlerTagType.UNDERWATER, true),
new AttackMove(Moves.BEAT_UP, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 2) new AttackMove(Moves.BEAT_UP, Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 2)
.makesContact(false) .attr(MultiHitAttr, MultiHitType.BEAT_UP)
.unimplemented(), .attr(BeatUpAttr)
.makesContact(false),
new AttackMove(Moves.FAKE_OUT, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 10, 100, 3, 3) new AttackMove(Moves.FAKE_OUT, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 10, 100, 3, 3)
.attr(FlinchAttr) .attr(FlinchAttr)
.condition(new FirstMoveCondition()), .condition(new FirstMoveCondition()),
@ -5460,7 +5592,7 @@ export function initMoves() {
), ),
new AttackMove(Moves.TRUMP_CARD, Type.NORMAL, MoveCategory.SPECIAL, -1, -1, 5, -1, 0, 4) new AttackMove(Moves.TRUMP_CARD, Type.NORMAL, MoveCategory.SPECIAL, -1, -1, 5, -1, 0, 4)
.makesContact() .makesContact()
.unimplemented(), .attr(LessPPMorePowerAttr),
new StatusMove(Moves.HEAL_BLOCK, Type.PSYCHIC, 100, 15, -1, 0, 4) new StatusMove(Moves.HEAL_BLOCK, Type.PSYCHIC, 100, 15, -1, 0, 4)
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)
.unimplemented(), .unimplemented(),
@ -6691,14 +6823,16 @@ export function initMoves() {
.attr(SacrificialAttr) .attr(SacrificialAttr)
.target(MoveTarget.ALL_NEAR_OTHERS) .target(MoveTarget.ALL_NEAR_OTHERS)
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.MISTY && user.isGrounded() ? 1.5 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.MISTY && user.isGrounded() ? 1.5 : 1)
.condition(failIfDampCondition), .condition(failIfDampCondition)
.makesContact(false),
new AttackMove(Moves.GRASSY_GLIDE, Type.GRASS, MoveCategory.PHYSICAL, 55, 100, 20, -1, 0, 8) new AttackMove(Moves.GRASSY_GLIDE, Type.GRASS, MoveCategory.PHYSICAL, 55, 100, 20, -1, 0, 8)
.partial(), .partial(),
new AttackMove(Moves.RISING_VOLTAGE, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 8) new AttackMove(Moves.RISING_VOLTAGE, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 8)
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && target.isGrounded() ? 2 : 1), .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && target.isGrounded() ? 2 : 1),
new AttackMove(Moves.TERRAIN_PULSE, Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 8) new AttackMove(Moves.TERRAIN_PULSE, Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 8)
.pulseMove() .attr(TerrainPulseTypeAttr)
.partial(), .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() !== TerrainType.NONE && user.isGrounded() ? 2 : 1)
.pulseMove(),
new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) new AttackMove(Moves.SKITTER_SMACK, Type.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8)
.attr(StatChangeAttr, BattleStat.SPATK, -1), .attr(StatChangeAttr, BattleStat.SPATK, -1),
new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8) new AttackMove(Moves.BURNING_JEALOUSY, Type.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8)
@ -7158,7 +7292,10 @@ export function initMoves() {
new AttackMove(Moves.PSYCHIC_NOISE, Type.PSYCHIC, MoveCategory.SPECIAL, 75, 100, 10, -1, 0, 9) new AttackMove(Moves.PSYCHIC_NOISE, Type.PSYCHIC, MoveCategory.SPECIAL, 75, 100, 10, -1, 0, 9)
.soundBased() .soundBased()
.partial(), .partial(),
new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, -1, 3, 9) new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9)
.attr(FlinchAttr)
.condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].priority > 0 )
//TODO: Should also apply when target move priority increased by ability ex. gale wings
.partial(), .partial(),
new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9)
.attr(StatusEffectAttr, StatusEffect.TOXIC) .attr(StatusEffectAttr, StatusEffect.TOXIC)

View File

@ -16,6 +16,7 @@ import { GameMode } from '../game-mode';
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities"; import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities";
import { VariantSet } from './variant'; import { VariantSet } from './variant';
import i18next, { Localizable } from '../plugins/i18n'; import i18next, { Localizable } from '../plugins/i18n';
import { Stat } from "./pokemon-stat";
export enum Region { export enum Region {
NORMAL, NORMAL,
@ -192,6 +193,15 @@ export abstract class PokemonSpeciesForm {
return false; return false;
} }
/**
* Gets the species' base stat amount for the given stat.
* @param stat The desired stat.
* @returns The species' base stat amount.
*/
getBaseStat(stat: Stat): integer {
return this.baseStats[stat]
}
getBaseExp(): integer { getBaseExp(): integer {
let ret = this.baseExp; let ret = this.baseExp;
switch (this.getFormSpriteKey()) { switch (this.getFormSpriteKey()) {

View File

@ -43762,6 +43762,7 @@ export const tmSpecies: TmSpecies = {
Species.TAUROS, Species.TAUROS,
Species.MEW, Species.MEW,
Species.SNUBBULL, Species.SNUBBULL,
Species.GRANBULL,
Species.SCIZOR, Species.SCIZOR,
Species.HERACROSS, Species.HERACROSS,
Species.TEDDIURSA, Species.TEDDIURSA,
@ -43785,11 +43786,14 @@ export const tmSpecies: TmSpecies = {
Species.LUCARIO, Species.LUCARIO,
Species.TOXICROAK, Species.TOXICROAK,
Species.GALLADE, Species.GALLADE,
Species.PIGNITE,
Species.EMBOAR,
Species.TIMBURR, Species.TIMBURR,
Species.GURDURR, Species.GURDURR,
Species.CONKELDURR, Species.CONKELDURR,
Species.SAWK, Species.SAWK,
Species.KROOKODILE, Species.KROOKODILE,
Species.SCRAGGY,
Species.SCRAFTY, Species.SCRAFTY,
Species.ESCAVALIER, Species.ESCAVALIER,
Species.EELEKTROSS, Species.EELEKTROSS,

View File

@ -235,11 +235,10 @@ export class TrainerConfig {
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
} }
// This is only the male name, because the female name is handled in a different function (setHasGenders)
if (name === 'Finn') { if (name === 'Finn') {
name = i18next.t('trainerNames:rival'); name = i18next.t('trainerNames:rival');
} }
} }
this.name = name; this.name = name;
return this; return this;
@ -254,6 +253,7 @@ export class TrainerConfig {
// Make the title lowercase and replace spaces with underscores // Make the title lowercase and replace spaces with underscores
title = title.toLowerCase().replace(/\s/g, '_'); title = title.toLowerCase().replace(/\s/g, '_');
// Get the title from the i18n file
this.title = i18next.t(`titles:${title}`); this.title = i18next.t(`titles:${title}`);
@ -281,21 +281,40 @@ export class TrainerConfig {
return trainerType; return trainerType;
} }
/**
* Sets the configuration for trainers with genders, including the female name and encounter background music (BGM).
* @param {string} [nameFemale] - The name of the female trainer. If 'Ivy', a localized name will be assigned.
* @param {TrainerType | string} [femaleEncounterBgm] - The encounter BGM for the female trainer, which can be a TrainerType or a string.
* @returns {TrainerConfig} - The updated TrainerConfig instance.
**/
setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig { setHasGenders(nameFemale?: string, femaleEncounterBgm?: TrainerType | string): TrainerConfig {
// If the female name is 'Ivy' (the rival), assign a localized name.
if (nameFemale === 'Ivy') { if (nameFemale === 'Ivy') {
// Give the rival a localized name // Check if the internationalization (i18n) system is initialized.
// First check if i18n is initialized
if (!getIsInitialized()) { if (!getIsInitialized()) {
// Initialize the i18n system if it is not already initialized.
initI18n(); initI18n();
} }
// Set the localized name for the female rival.
this.nameFemale = i18next.t('trainerNames:rival_female'); this.nameFemale = i18next.t('trainerNames:rival_female');
} else { } else {
// Otherwise, assign the provided female name.
this.nameFemale = nameFemale; this.nameFemale = nameFemale;
} }
// Indicate that this trainer configuration includes genders.
this.hasGenders = true; this.hasGenders = true;
if (femaleEncounterBgm)
this.femaleEncounterBgm = typeof femaleEncounterBgm === 'number' ? TrainerType[femaleEncounterBgm].toString().replace(/\_/g, ' ').toLowerCase() : femaleEncounterBgm; // If a female encounter BGM is provided.
if (femaleEncounterBgm) {
// If the BGM is a TrainerType (number), convert it to a string, replace underscores with spaces, and convert to lowercase.
// Otherwise, assign the provided string as the BGM.
this.femaleEncounterBgm = typeof femaleEncounterBgm === 'number'
? TrainerType[femaleEncounterBgm].toString().replace(/_/g, ' ').toLowerCase()
: femaleEncounterBgm;
}
// Return the updated TrainerConfig instance.
return this; return this;
} }
@ -398,29 +417,44 @@ export class TrainerConfig {
return this; return this;
} }
/**
* Initializes the trainer configuration for a Gym Leader.
* @param {Species | Species[]} signatureSpecies - The signature species for the Gym Leader.
* @param {Type[]} specialtyTypes - The specialty types for the Gym Leader.
* @returns {TrainerConfig} - The updated TrainerConfig instance.
* **/
initForGymLeader(signatureSpecies: (Species | Species[])[], ...specialtyTypes: Type[]): TrainerConfig { initForGymLeader(signatureSpecies: (Species | Species[])[], ...specialtyTypes: Type[]): TrainerConfig {
// Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
} }
// Set the function to generate the Gym Leader's party template.
this.setPartyTemplateFunc(getGymLeaderPartyTemplate); this.setPartyTemplateFunc(getGymLeaderPartyTemplate);
// Set up party members with their corresponding species.
signatureSpecies.forEach((speciesPool, s) => { signatureSpecies.forEach((speciesPool, s) => {
// Ensure speciesPool is an array.
if (!Array.isArray(speciesPool)) if (!Array.isArray(speciesPool))
speciesPool = [speciesPool]; speciesPool = [speciesPool];
// Set a function to get a random party member from the species pool.
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
// If specialty types are provided, set species filter and specialty types.
if (specialtyTypes.length) { if (specialtyTypes.length) {
this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) !== undefined); this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) !== undefined);
this.setSpecialtyTypes(...specialtyTypes); this.setSpecialtyTypes(...specialtyTypes);
} }
// Handle name by checking this.name - making it lowercase and replacing spaces with underscores and then calling i18next.t with the name // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores.
const nameForCall = this.name.toLowerCase().replace(/\s/g, '_'); const nameForCall = this.name.toLowerCase().replace(/\s/g, '_');
this.name = i18next.t(`trainerNames:${nameForCall}`); this.name = i18next.t(`trainerNames:${nameForCall}`);
// Set the title to "gym_leader". (this is the key in the i18n file)
this.setTitle('gym_leader');
// Configure various properties for the Gym Leader.
this.setTitle("gym_leader");
this.setMoneyMultiplier(2.5); this.setMoneyMultiplier(2.5);
this.setBoss(); this.setBoss();
this.setStaticParty(); this.setStaticParty();
@ -430,75 +464,125 @@ export class TrainerConfig {
const waveIndex = party[0].scene.currentBattle.waveIndex; const waveIndex = party[0].scene.currentBattle.waveIndex;
return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : null); return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : null);
}); });
return this; return this;
} }
/**
* Initializes the trainer configuration for an Elite Four member.
* @param {Species | Species[]} signatureSpecies - The signature species for the Elite Four member.
* @param {Type[]} specialtyTypes - The specialty types for the Elite Four member.
* @returns {TrainerConfig} - The updated TrainerConfig instance.
**/
initForEliteFour(signatureSpecies: (Species | Species[])[], ...specialtyTypes: Type[]): TrainerConfig { initForEliteFour(signatureSpecies: (Species | Species[])[], ...specialtyTypes: Type[]): TrainerConfig {
// Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
} }
// Set the party templates for the Elite Four.
this.setPartyTemplates(trainerPartyTemplates.ELITE_FOUR); this.setPartyTemplates(trainerPartyTemplates.ELITE_FOUR);
// Set up party members with their corresponding species.
signatureSpecies.forEach((speciesPool, s) => { signatureSpecies.forEach((speciesPool, s) => {
// Ensure speciesPool is an array.
if (!Array.isArray(speciesPool)) if (!Array.isArray(speciesPool))
speciesPool = [speciesPool]; speciesPool = [speciesPool];
// Set a function to get a random party member from the species pool.
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
// Set species filter and specialty types if provided, otherwise filter by base total.
if (specialtyTypes.length) { if (specialtyTypes.length) {
this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) && p.baseTotal >= 450); this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) && p.baseTotal >= 450);
this.setSpecialtyTypes(...specialtyTypes); this.setSpecialtyTypes(...specialtyTypes);
} else } else {
this.setSpeciesFilter(p => p.baseTotal >= 450); this.setSpeciesFilter(p => p.baseTotal >= 450);
// Handle name by checking this.name - making it lowercase and replacing spaces with underscores and then calling i18next.t with the name }
// Localize the trainer's name by converting it to lowercase and replacing spaces with underscores.
const nameForCall = this.name.toLowerCase().replace(/\s/g, '_'); const nameForCall = this.name.toLowerCase().replace(/\s/g, '_');
this.name = i18next.t(`trainerNames:${nameForCall}`); this.name = i18next.t(`trainerNames:${nameForCall}`);
this.setTitle("elite_four"); // Set the title to "elite_four". (this is the key in the i18n file)
this.setTitle('elite_four');
// Configure various properties for the Elite Four member.
this.setMoneyMultiplier(3.25); this.setMoneyMultiplier(3.25);
this.setBoss(); this.setBoss();
this.setStaticParty(); this.setStaticParty();
this.setBattleBgm('battle_elite'); this.setBattleBgm('battle_elite');
this.setVictoryBgm('victory_gym'); this.setVictoryBgm('victory_gym');
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : null)); this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : null));
return this; return this;
} }
/**
* Initializes the trainer configuration for a Champion.
* @param {Species | Species[]} signatureSpecies - The signature species for the Champion.
* @returns {TrainerConfig} - The updated TrainerConfig instance.
**/
initForChampion(signatureSpecies: (Species | Species[])[]): TrainerConfig { initForChampion(signatureSpecies: (Species | Species[])[]): TrainerConfig {
// Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
} }
// Set the party templates for the Champion.
this.setPartyTemplates(trainerPartyTemplates.CHAMPION); this.setPartyTemplates(trainerPartyTemplates.CHAMPION);
// Set up party members with their corresponding species.
signatureSpecies.forEach((speciesPool, s) => { signatureSpecies.forEach((speciesPool, s) => {
// Ensure speciesPool is an array.
if (!Array.isArray(speciesPool)) if (!Array.isArray(speciesPool))
speciesPool = [speciesPool]; speciesPool = [speciesPool];
// Set a function to get a random party member from the species pool.
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
// Set species filter to only include species with a base total of 470 or higher.
this.setSpeciesFilter(p => p.baseTotal >= 470); this.setSpeciesFilter(p => p.baseTotal >= 470);
// Handle name by checking this.name - making it lowercase and replacing spaces with underscores and then calling i18next.t with the name
// Localize the trainer's name by converting it to lowercase and replacing spaces with underscores.
const nameForCall = this.name.toLowerCase().replace(/\s/g, '_'); const nameForCall = this.name.toLowerCase().replace(/\s/g, '_');
this.name = i18next.t(`trainerNames:${nameForCall}`); this.name = i18next.t(`trainerNames:${nameForCall}`);
this.setTitle("champion");
// Set the title to "champion". (this is the key in the i18n file)
this.setTitle('champion');
// Configure various properties for the Champion.
this.setMoneyMultiplier(10); this.setMoneyMultiplier(10);
this.setBoss(); this.setBoss();
this.setStaticParty(); this.setStaticParty();
this.setBattleBgm('battle_champion_alder'); this.setBattleBgm('battle_champion_alder');
this.setVictoryBgm('victory_champion'); this.setVictoryBgm('victory_champion');
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 3)); this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 3));
return this; return this;
} }
/**
* Retrieves the title for the trainer based on the provided trainer slot and variant.
* @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE.
* @param {TrainerVariant} variant - The variant of the trainer to determine the specific title.
* @returns {string} - The title of the trainer.
**/
getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string { getTitle(trainerSlot: TrainerSlot = TrainerSlot.NONE, variant: TrainerVariant): string {
let ret = this.name; let ret = this.name;
// Check if the variant is double and the name for double exists
if (!trainerSlot && variant === TrainerVariant.DOUBLE && this.nameDouble) if (!trainerSlot && variant === TrainerVariant.DOUBLE && this.nameDouble)
return this.nameDouble; return this.nameDouble;
// Female variant
if (this.hasGenders) { if (this.hasGenders) {
// If the name is already set
if (this.nameFemale) { if (this.nameFemale) {
// Check if the variant is either female or this is for the partner in a double battle
if (variant === TrainerVariant.FEMALE || (variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER)) if (variant === TrainerVariant.FEMALE || (variant === TrainerVariant.DOUBLE && trainerSlot === TrainerSlot.TRAINER_PARTNER))
return this.nameFemale; return this.nameFemale;
} else } else
// Check if !variant is true, if so return the name, else return the name with _female appended // Check if !variant is true, if so return the name, else return the name with _female appended
if (variant) { if (variant) {
if (!getIsInitialized()) { if (!getIsInitialized()) {

View File

@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat';
import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag';
import { ArenaTagType } from "../data/enums/arena-tag-type"; import { ArenaTagType } from "../data/enums/arena-tag-type";
import { Biome } from "../data/enums/biome"; import { Biome } from "../data/enums/biome";
import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr } from '../data/ability'; import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr } from '../data/ability';
import { Abilities } from "#app/data/enums/abilities"; import { Abilities } from "#app/data/enums/abilities";
import PokemonData from '../system/pokemon-data'; import PokemonData from '../system/pokemon-data';
import Battle, { BattlerIndex } from '../battle'; import Battle, { BattlerIndex } from '../battle';
@ -1523,6 +1523,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const critOnly = new Utils.BooleanHolder(false); const critOnly = new Utils.BooleanHolder(false);
const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT); const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT);
applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly); applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly);
applyAbAttrs(ConditionalCritAbAttr, source, null, critOnly, this, move);
if (critOnly.value || critAlways) if (critOnly.value || critAlways)
isCritical = true; isCritical = true;
else { else {
@ -1538,13 +1539,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
critLevel.value += 2; critLevel.value += 2;
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critLevel.value, 3))]; const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critLevel.value, 3))];
isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.randBattleSeedInt(critChance)); isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.randBattleSeedInt(critChance));
}
if (isCritical) { if (isCritical) {
const blockCrit = new Utils.BooleanHolder(false); const blockCrit = new Utils.BooleanHolder(false);
applyAbAttrs(BlockCritAbAttr, this, null, blockCrit); applyAbAttrs(BlockCritAbAttr, this, null, blockCrit);
if (blockCrit.value) if (blockCrit.value)
isCritical = false; isCritical = false;
} }
}
const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, null, isCritical)); const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, null, isCritical));
const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical));
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);

View File

@ -96,30 +96,54 @@ export default class Trainer extends Phaser.GameObjects.Container {
return this.config.getSpriteKey(this.variant === TrainerVariant.FEMALE || forceFemale); return this.config.getSpriteKey(this.variant === TrainerVariant.FEMALE || forceFemale);
} }
/**
* Returns the name of the trainer based on the provided trainer slot and the option to include a title.
* @param {TrainerSlot} trainerSlot - The slot to determine which name to use. Defaults to TrainerSlot.NONE.
* @param {boolean} includeTitle - Whether to include the title in the returned name. Defaults to false.
* @returns {string} - The formatted name of the trainer.
**/
getName(trainerSlot: TrainerSlot = TrainerSlot.NONE, includeTitle: boolean = false): string { getName(trainerSlot: TrainerSlot = TrainerSlot.NONE, includeTitle: boolean = false): string {
// Get the base title based on the trainer slot and variant.
let name = this.config.getTitle(trainerSlot, this.variant); let name = this.config.getTitle(trainerSlot, this.variant);
// 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;
// If the trainer has a name (not null or undefined).
if (this.name) { if (this.name) {
if (includeTitle) // If the title should be included.
if (includeTitle) {
// Check if i18n is initialized // Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n() // Initialize the i18n system if it is not already initialized.
initI18n();
} }
// Get the localized trainer class name from the i18n file and set it as the title.
// This is used for trainer class names, not titles like "Elite Four, Champion, etc."
title = i18next.t(`trainerClasses:${name.toLowerCase().replace(/\s/g, '_')}`); title = i18next.t(`trainerClasses:${name.toLowerCase().replace(/\s/g, '_')}`);
}
// If no specific trainer slot is set.
if (!trainerSlot) { if (!trainerSlot) {
// Use the trainer's name.
name = this.name; name = this.name;
if (this.partnerName) // If there is a partner name, concatenate it with the trainer's name using "&".
if (this.partnerName) {
name = `${name} & ${this.partnerName}`; name = `${name} & ${this.partnerName}`;
} else }
} else {
// Assign the name based on the trainer slot:
// Use 'this.name' if 'trainerSlot' is TRAINER.
// Otherwise, use 'this.partnerName' if it exists, or 'this.name' if it doesn't.
name = trainerSlot === TrainerSlot.TRAINER ? this.name : this.partnerName || this.name; name = trainerSlot === TrainerSlot.TRAINER ? this.name : this.partnerName || this.name;
} }
}
// Return the formatted name, including the title if it is set.
return title ? `${title} ${name}` : name; return title ? `${title} ${name}` : name;
} }
isDouble(): boolean { isDouble(): boolean {
return this.config.doubleOnly || this.variant === TrainerVariant.DOUBLE; return this.config.doubleOnly || this.variant === TrainerVariant.DOUBLE;
} }

View File

@ -52,16 +52,16 @@ export const modifierType: ModifierTypeTranslationEntries = {
description: "Double les chances de tomber sur un combat double pendant {{battleCount}} combats", description: "Double les chances de tomber sur un combat double pendant {{battleCount}} combats",
}, },
"TempBattleStatBoosterModifierType": { "TempBattleStatBoosterModifierType": {
description: "Augmente d1 cran {{tempBattleStatName}} pour toute léquipe pendant 5 combats", description: "Augmente dun cran {{tempBattleStatName}} pour toute léquipe pendant 5 combats",
}, },
"AttackTypeBoosterModifierType": { "AttackTypeBoosterModifierType": {
description: "Augmente de 20% la puissance des capacités de type {{moveType}} dun Pokémon", description: "Augmente de 20% la puissance des capacités de type {{moveType}} dun Pokémon",
}, },
"PokemonLevelIncrementModifierType": { "PokemonLevelIncrementModifierType": {
description: "Fait monter un Pokémon d1 niveau", description: "Fait monter un Pokémon dun niveau",
}, },
"AllPokemonLevelIncrementModifierType": { "AllPokemonLevelIncrementModifierType": {
description: "Fait monter toute léquipe d1 niveau", description: "Fait monter toute léquipe dun niveau",
}, },
"PokemonBaseStatBoosterModifierType": { "PokemonBaseStatBoosterModifierType": {
description: "Augmente de 10% {{statName}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter.", description: "Augmente de 10% {{statName}} de base de son porteur. Plus les IV sont hauts, plus il peut en porter.",
@ -214,13 +214,13 @@ export const modifierType: ModifierTypeTranslationEntries = {
"SHINY_CHARM": { name: "Charme Chroma", description: "Augmente énormément les chances de rencontrer un Pokémon sauvage chromatique" }, "SHINY_CHARM": { name: "Charme Chroma", description: "Augmente énormément les chances de rencontrer un Pokémon sauvage chromatique" },
"ABILITY_CHARM": { name: "Charme Talent", description: "Augmente énormément les chances de rencontrer un Pokémon sauvage avec un Talent Caché" }, "ABILITY_CHARM": { name: "Charme Talent", description: "Augmente énormément les chances de rencontrer un Pokémon sauvage avec un Talent Caché" },
"IV_SCANNER": { name: "Scanner dIV", description: "Scanne les IV dun Pokémon sauvage. 2 IV sont révélés par Scanner. Les meilleurs sont montrés en 1er." }, "IV_SCANNER": { name: "Scanner dIV", description: "Révèle la qualité de deux IV dun Pokémon sauvage par scanner possédé. Les meilleurs IV sont révélés en priorité." },
"DNA_SPLICERS": { name: "Pointeau ADN" }, "DNA_SPLICERS": { name: "Pointeau ADN" },
"MINI_BLACK_HOLE": { name: "Mini Trou Noir" }, "MINI_BLACK_HOLE": { name: "Mini Trou Noir" },
"GOLDEN_POKEBALL": { name: "Poké Ball Dorée", description: "Ajoute 1 choix dobjet à la fin de chaque combat" }, "GOLDEN_POKEBALL": { name: "Poké Ball Dorée", description: "Ajoute un choix dobjet à la fin de chaque combat" },
"ENEMY_DAMAGE_BOOSTER": { name: "Jeton Dégâts", description: "Augmente les dégâts de 5%" }, "ENEMY_DAMAGE_BOOSTER": { name: "Jeton Dégâts", description: "Augmente les dégâts de 5%" },
"ENEMY_DAMAGE_REDUCTION": { name: "Jeton Protection", description: "Diminue les dégâts reçus de 2,5%" }, "ENEMY_DAMAGE_REDUCTION": { name: "Jeton Protection", description: "Diminue les dégâts reçus de 2,5%" },

View File

@ -906,7 +906,7 @@ export class EncounterPhase extends BattlePhase {
const showDialogueAndSummon = () => { const showDialogueAndSummon = () => {
let message: string; let message: string;
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex);
this.scene.ui.showDialogue(message, trainer.getName(), null, () => { this.scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE,true), null, () => {
this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon()));
}); });
}; };
@ -1809,7 +1809,7 @@ export class CommandPhase extends FieldPhase {
const trapped = new Utils.BooleanHolder(false); const trapped = new Utils.BooleanHolder(false);
const batonPass = isSwitch && args[0] as boolean; const batonPass = isSwitch && args[0] as boolean;
if (!batonPass) if (!batonPass)
enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped)); enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon));
if (batonPass || (!trapTag && !trapped.value)) { if (batonPass || (!trapTag && !trapped.value)) {
this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch
? { command: Command.POKEMON, cursor: cursor, args: args } ? { command: Command.POKEMON, cursor: cursor, args: args }
@ -1914,7 +1914,7 @@ export class EnemyCommandPhase extends FieldPhase {
const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag;
const trapped = new Utils.BooleanHolder(false); const trapped = new Utils.BooleanHolder(false);
opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped)); opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon));
if (!trapTag && !trapped.value) { if (!trapTag && !trapped.value) {
const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true); const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true);
@ -3045,7 +3045,8 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
break; break;
} }
if (damage) { if (damage) {
this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage)); // Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage, false, true));
pokemon.updateInfo(); pokemon.updateInfo();
} }
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end()); new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end());
@ -4349,6 +4350,7 @@ export class AttemptCapturePhase extends PokemonPhase {
scale: 1 scale: 1
}); });
this.scene.currentBattle.lastUsedPokeball = this.pokeballType;
this.removePb(); this.removePb();
this.end(); this.end();
} }

View File

@ -178,11 +178,17 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer)
scene.ui.revertMode(); scene.ui.revertMode();
(scene.ui.getHandler() as SettingsUiHandler).setOptionCursor(Object.values(Setting).indexOf(Setting.Language), 0, true); (scene.ui.getHandler() as SettingsUiHandler).setOptionCursor(Object.values(Setting).indexOf(Setting.Language), 0, true);
}; };
const changeLocaleHandler = (locale: string) => { const changeLocaleHandler = (locale: string): boolean => {
try {
i18next.changeLanguage(locale); i18next.changeLanguage(locale);
localStorage.setItem('prLang', locale); localStorage.setItem('prLang', locale);
cancelHandler(); cancelHandler();
scene.reset(true, false, true); scene.reset(true, false, true);
return true;
} catch (error) {
console.error('Error changing locale:', error);
return false;
}
}; };
scene.ui.setOverlayMode(Mode.OPTION_SELECT, { scene.ui.setOverlayMode(Mode.OPTION_SELECT, {
options: [ options: [

View File

@ -271,6 +271,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
// The font size should be set per language // The font size should be set per language
// currentLanguage is already defined // currentLanguage is already defined
switch (currentLanguage) { switch (currentLanguage) {
case 'fr':
starterInfoTextSize = '54px';
break;
case 'pt_BR': case 'pt_BR':
starterInfoTextSize = '47px'; starterInfoTextSize = '47px';
break; break;

View File

@ -494,20 +494,16 @@ export default class SummaryUiHandler extends UiHandler {
} }
break; break;
case Button.LEFT: case Button.LEFT:
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE)
break;
if (this.cursor) if (this.cursor)
success = this.setCursor(this.cursor - 1); success = this.setCursor(this.cursor - 1);
break; break;
case Button.RIGHT: case Button.RIGHT:
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { if (this.cursor < pages.length - 1) {
this.setCursor(Page.MOVES);
this.moveSelect = true;
success = true;
break;
}
if (this.cursor < pages.length - 1)
success = this.setCursor(this.cursor + 1); success = this.setCursor(this.cursor + 1);
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.cursor === Page.MOVES) {
this.moveSelect = true;
}
}
break; break;
} }
} }
@ -614,13 +610,7 @@ export default class SummaryUiHandler extends UiHandler {
onComplete: () => { onComplete: () => {
if (forward){ if (forward){
this.populatePageContainer(this.summaryPageContainer); this.populatePageContainer(this.summaryPageContainer);
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { if (this.cursor===Page.MOVES) {
this.moveCursorObj = null;
this.extraMoveRowContainer.setVisible(true);
this.setCursor(0, true);
this.showMoveEffect();
}
else if (this.cursor===Page.MOVES) {
this.moveCursorObj = null; this.moveCursorObj = null;
this.showMoveSelect(); this.showMoveSelect();
this.showMoveEffect(); this.showMoveEffect();