Compare commits

..

31 Commits

Author SHA1 Message Date
Flashfyre
62224aa3c9 Merge branch 'main' into nuzlocke-mode 2024-05-05 19:20:07 -04:00
Temps Ray
77b6ad47d0 Make curse target 2024-05-05 18:40:21 -04:00
Greenlamp2
82b0606311
Fix - stop repeating key when focus is lost (#514)
* disable all key pressed when trying to open a modal

* in fact, checking the if the focus is lost is a better solution
2024-05-05 18:40:05 -04:00
Flashfyre
005caeb1ab Change cookie expiration to 90 days 2024-05-05 18:13:19 -04:00
Benjamin Odom
e6fd331931 Fix HalfSacrificialAttr
Mind Blown targets all users and was triggering recoil every time it hit. Changed so that it only applies once after all targets take damage by adding a new MoveEffectTrigger, POST_TARGET. This also fixes an issue where dying to recoil stopped the rest of the damage from hitting the remaining targets.

This change also applies fixes for Steel Beam since they are have the same effect but is single target.

Also added missing messages for Damp and Recoil. All messaging is in line with Pokémon Showdown.
2024-05-05 17:46:15 -04:00
Tempoanon
9b72dfbd71
Implement curse (#510)
* Implement curse

* self targeting is always true if not ghost type
2024-05-05 17:45:20 -04:00
Flashfyre
6a3ca62490 Add separate sub-legendary stats 2024-05-05 17:11:29 -04:00
Madi Simpson
f7f235cdec move: handle some aspects of custom fail text a bit better 2024-05-05 14:28:06 -04:00
lucfd
4aad37a758
Implement Take Heart Sleep Talk edge case (#508)
* removed take heart condition, added sleep talk edge case

* removed freeze heal

* removed testing stuff
2024-05-05 13:04:19 -05:00
LaukkaE
697f2a3a42 fix-takeHeart 2024-05-05 13:36:52 -04:00
Madi Simpson
b6603e2c6c move: fix issue with self switch moves causing softlocks 2024-05-05 12:22:03 -04:00
EggMuncherSupreme
fa2d257144
Changed Bouncy Bubble to heal 100% of damage dealt and implemented Take Heart (#326)
* Changed Bouncy Bubble to heal 100% of damage dealt, as according to Gen 8

* Fully implemented Take Heart
2024-05-05 11:01:39 -05:00
Madmadness65
d5cd243967 Re-export Furfrou trim sprites
Apparently all the non-shiny sprites had artifacts on them. This has been corrected.
2024-05-05 10:51:41 -05:00
Madi Simpson
8e606139fa
moves: (mostly) fix parting shot stack bug (#495) 2024-05-05 11:50:46 -04:00
Greenlamp
408b189966 1st attempt with a fix for black screen 2024-05-05 11:43:38 -04:00
Madi Simpson
fa01f32475 ability: sturdy ability tag no longer pops up with 1 max hp 2024-05-05 11:40:54 -04:00
Armin
5c6e161eda Issue-483: Fix contact effects after death 2024-05-05 11:06:26 -04:00
Flashfyre
437cb87df9 Add separate achievements for sub-legendaries and rename pseudoLegendary field 2024-05-05 11:05:22 -04:00
Madi Simpson
80ee440109
Implement Guard Dog, attribute for abilities to give Intimidate immunity (#448)
* abilities: implement guard dog, abilities that give intimidate immunity

* abilities: implement rattled's second effect, remove refs to mold breaker

* abilities: fix rattled not giving the attack drop still

* abilities: make ability bars pop in to some success

* abilities: implement suction cups since it has the same effect

* moves: add custom fail text, fix animation issues with Guard Dog/Roar

* abilities: manually show intimidate ability bar to prevent weirdness
2024-05-05 10:52:27 -04:00
Lugiad
f5bf35f8de
Removing forms names in Italian pokemon.ts (#486)
* Removing forms names in Italian pokemon.ts

Forms names aren't supposed to be specified in the name of the Pokémon

* Removing forms names in Italian pokemon.ts
2024-05-05 10:47:16 -04:00
Alessandro Bruzzese
fea73081e5
Trasleted move.ts in italian (#490)
* Added Italian translations for ability.ts and other element

* traslated move.ts
2024-05-05 10:46:56 -04:00
Benjamin Odom
faf1c0bce6 Small Typo when Learning a TM
Fixes a small typo when learning a tm. The incorrect escape character was being used and no escape character was being used for Spanish.
2024-05-05 10:45:47 -04:00
Greenlamp
3ee9c39975 disable the gamepad support when the option is on OFF 2024-05-05 10:45:18 -04:00
Madi Simpson
e4b8bffdec stat change phase: only display comma before and if there are 3 or more stats 2024-05-05 10:45:00 -04:00
Akuma-Reiki
b076dec346
Add Candy Progress UI to Starter Selecgt (#479)
Co-authored-by: Samuel H <flashfireex@gmail.com>
2024-05-05 10:35:14 -04:00
Greenlamp2
d98f7733d4
Rework - Inputs management to include all gamepad mapping V2 (#429)
* rework of the input handling, including different gamepad and keyboard

* rework of the input handling, including different gamepad and keyboard

* first version of a too complex inputHandler based on phaser3-merged-input

* removed useless control management and kept it simple for our use case, investigating to put out button_XX()

* renamed inputHandler to inputController

* aggregate directions and some action into a same method + fix menu return value

* added back repeated input feature on keeping down a key

* cleanup + return type

* fix submit/action doing two things simultaneously, still same behaviour as before

* extracted UI inputs out of battle-scene

* tab -> spaces

* tab -> spaces what about now github ?

* tab -> spaces final (maybe)

* tried to fix the plugin loading issue on prod

* remove Plugins things as it's too uncertain how it works on prod

* seems old code source is indented with tab

* cleanup

* cleanup

* cleanup

* putting in an enum file the enum buttons

* fix repeating stats button + change message in event when the key is repeating

* added return type for ui-inputs

* added return type for inputs-controller

* adapted the code to integrate changes of bennybroseph
2024-05-05 10:30:00 -04:00
Benjamin Odom
3ed7649ce5
Remove Debug Code (#480)
Remove code intended only for debugging.
2024-05-05 18:45:54 +10:00
Ivan Perez
3a218eb92b
Fixed a bug where a yawned target in a semi-vulnerable state would be stuck in that state (#396) 2024-05-05 00:35:47 -05:00
Reldnahc
7cdf07c050
have arena tags check their sides. (#447)
* have arena traps check their sides.

* make sure we check the right tags layers
2024-05-05 00:27:46 -05:00
Benjamin Odom
dd76cbc7a2
Add Setting to Swap A and B on Gamepad (#474)
* Add Setting to Swap A and B on Gamepad

Added a new setting to swap the A and B button on controller. Defaults to off which retains the current behavior on live.

* Add comments
2024-05-05 01:25:57 -04:00
Madmadness65
5440c0b40c Add B2W2 Legendary Titan Theme
This is a small part of a larger addition of music, added before the rest due to it being a B2W2 track.
2024-05-04 22:48:23 -05:00
94 changed files with 4033 additions and 3512 deletions

Binary file not shown.

View File

@ -4,30 +4,30 @@
"image": "676-dandy.png",
"format": "RGBA8888",
"size": {
"w": 71,
"h": 71
"w": 65,
"h": 65
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 56,
"h": 71
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 56,
"h": 71
"x": 22,
"y": 15,
"w": 50,
"h": 65
},
"frame": {
"x": 0,
"y": 0,
"w": 56,
"h": 71
"w": 50,
"h": 65
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:29165e5c573c8a4235076355ce5d29d5:17a1912fd81d23ce8a7209e27ad3e9ed:b5a287256e260744fe6660dc2ceefd11$"
"smartupdate": "$TexturePacker:SmartUpdate:c09947cc1f66b7a7bf844a7caaa3bd76:20ba7e14cce8a9ee80955d94d19ae46f:b5a287256e260744fe6660dc2ceefd11$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 765 B

View File

@ -4,30 +4,30 @@
"image": "676-debutante.png",
"format": "RGBA8888",
"size": {
"w": 67,
"h": 67
"w": 61,
"h": 61
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 56,
"h": 67
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 56,
"h": 67
"x": 20,
"y": 19,
"w": 50,
"h": 61
},
"frame": {
"x": 0,
"y": 0,
"w": 56,
"h": 67
"w": 50,
"h": 61
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:689efba1d73755e99f598016fd321c3c:07d06d321fff31976e1b993edb972e4f:bf2f862a1c8881133f9f3ffbf44602fa$"
"smartupdate": "$TexturePacker:SmartUpdate:107780a0e7447bf89ae4998e24021f8b:7623c7ab8d7b6bc5f755e8a1f81ea257:bf2f862a1c8881133f9f3ffbf44602fa$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 802 B

View File

@ -4,30 +4,30 @@
"image": "676-diamond.png",
"format": "RGBA8888",
"size": {
"w": 74,
"h": 74
"w": 68,
"h": 68
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 53,
"h": 74
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 53,
"h": 74
"x": 24,
"y": 12,
"w": 47,
"h": 68
},
"frame": {
"x": 0,
"y": 0,
"w": 53,
"h": 74
"w": 47,
"h": 68
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:b33f3de937219e7763316531398d7bbd:c7e104da9393ec52b9e6d37f92fc20d9:88c467fc09249b0d2f579d44eacd528d$"
"smartupdate": "$TexturePacker:SmartUpdate:90477dc67c3429f2f0430f0a868ed85c:bd69660307b7d945313b73a256c24d57:88c467fc09249b0d2f579d44eacd528d$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 807 B

View File

@ -4,30 +4,30 @@
"image": "676-heart.png",
"format": "RGBA8888",
"size": {
"w": 72,
"h": 72
"w": 66,
"h": 66
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 72
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 72
"x": 24,
"y": 14,
"w": 48,
"h": 66
},
"frame": {
"x": 0,
"y": 0,
"w": 54,
"h": 72
"w": 48,
"h": 66
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:15e433a2269dd4043b1614811a1b8fbd:421a40542b16cdb2cc154c85e963b40d:e11d4dd16bff2ef69bc5ca683c44ca7c$"
"smartupdate": "$TexturePacker:SmartUpdate:a9b1c7a73abb563b1ecadad8b517794e:2273c7342204f52860d8e96670ddf46a:e11d4dd16bff2ef69bc5ca683c44ca7c$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 813 B

View File

@ -4,30 +4,30 @@
"image": "676-kabuki.png",
"format": "RGBA8888",
"size": {
"w": 70,
"h": 70
"w": 64,
"h": 64
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 58,
"h": 70
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 58,
"h": 70
"x": 22,
"y": 16,
"w": 52,
"h": 64
},
"frame": {
"x": 0,
"y": 0,
"w": 58,
"h": 70
"w": 52,
"h": 64
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:7f4056b1c055475f905d048c8d8d2969:08eecd31f557298e1570ec3490acffe2:c3cad56aa65d2ee971ceebc435a14ee6$"
"smartupdate": "$TexturePacker:SmartUpdate:d36abbc31bcd28664e28eb50f8e4719d:43c9d8bb344b7a1373b93f493badb47e:c3cad56aa65d2ee971ceebc435a14ee6$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 842 B

View File

@ -4,30 +4,30 @@
"image": "676-la-reine.png",
"format": "RGBA8888",
"size": {
"w": 69,
"h": 69
"w": 63,
"h": 63
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 55,
"h": 69
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 55,
"h": 69
"x": 23,
"y": 17,
"w": 49,
"h": 63
},
"frame": {
"x": 0,
"y": 0,
"w": 55,
"h": 69
"w": 49,
"h": 63
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:d209428aca72f49f1e9ee3e97f1c18ab:c56ae957d7acb10c7373b842af3a8786:b99821e87dffda3297dd25a224a9029f$"
"smartupdate": "$TexturePacker:SmartUpdate:81b2b5d853a3d2527630ca0b2fea6569:98097941483e509b7962c50b179d132f:b99821e87dffda3297dd25a224a9029f$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 726 B

View File

@ -4,30 +4,30 @@
"image": "676-matron.png",
"format": "RGBA8888",
"size": {
"w": 68,
"h": 68
"w": 62,
"h": 62
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 52,
"h": 68
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 52,
"h": 68
"x": 23,
"y": 18,
"w": 46,
"h": 62
},
"frame": {
"x": 0,
"y": 0,
"w": 52,
"h": 68
"w": 46,
"h": 62
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:698fab0e5221743b912637e774bfaecd:72a9977db421d061829c639e3b50a617:020798583d07c479020bb0af34cf8bb6$"
"smartupdate": "$TexturePacker:SmartUpdate:50d9857668a013306b96adc3777a22b2:7d3708f1b5b6eeb8419815fc6734bf06:020798583d07c479020bb0af34cf8bb6$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 777 B

View File

@ -4,30 +4,30 @@
"image": "676-pharaoh.png",
"format": "RGBA8888",
"size": {
"w": 69,
"h": 69
"w": 63,
"h": 63
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 52,
"h": 69
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 52,
"h": 69
"x": 25,
"y": 17,
"w": 46,
"h": 63
},
"frame": {
"x": 0,
"y": 0,
"w": 52,
"h": 69
"w": 46,
"h": 63
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:5c1df95bcf0d53d4f1b9f1bf77690598:623b4bc6698ef1a984b6bad42e68115b:904b7ab6f57249c96beea4b5f72deb53$"
"smartupdate": "$TexturePacker:SmartUpdate:69f1be06f9a7175f294267b08e7ba275:42e7e4068e30b78248d3629cffa5d2ec:904b7ab6f57249c96beea4b5f72deb53$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 770 B

View File

@ -4,30 +4,30 @@
"image": "676-star.png",
"format": "RGBA8888",
"size": {
"w": 72,
"h": 72
"w": 66,
"h": 66
},
"scale": 1,
"scale": 0.5,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 56,
"h": 72
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 56,
"h": 72
"x": 24,
"y": 14,
"w": 50,
"h": 66
},
"frame": {
"x": 0,
"y": 0,
"w": 56,
"h": 72
"w": 50,
"h": 66
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:31c1ce60aa953fb647ff40c50ac54cf1:d7eefc05e299310d05d389e05997e449:dd7fedf66b493b0bbdef5d25e0041dd8$"
"smartupdate": "$TexturePacker:SmartUpdate:fde8824a7749541f5af1cc14e6281670:204c6eb602358a5c7c1389b42317ffce:dd7fedf66b493b0bbdef5d25e0041dd8$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 838 B

View File

@ -4,30 +4,30 @@
"image": "676-dandy.png",
"format": "RGBA8888",
"size": {
"w": 72,
"h": 72
"w": 66,
"h": 66
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 62,
"h": 72
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 62,
"h": 72
"x": 20,
"y": 14,
"w": 56,
"h": 66
},
"frame": {
"x": 0,
"y": 0,
"w": 62,
"h": 72
"w": 56,
"h": 66
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:92d02eca8d05166d15bfa8b79ac478b9:f01538e7a19ca3d0d2fea48cd27d8a4c:b5a287256e260744fe6660dc2ceefd11$"
"smartupdate": "$TexturePacker:SmartUpdate:24899e1e616c6a0b9143d36903f9249e:8132d78c7f1f4ab0f673c82868b6a090:b5a287256e260744fe6660dc2ceefd11$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 715 B

View File

@ -4,30 +4,30 @@
"image": "676-debutante.png",
"format": "RGBA8888",
"size": {
"w": 68,
"h": 68
"w": 62,
"h": 62
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 57,
"h": 68
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 57,
"h": 68
"x": 25,
"y": 18,
"w": 51,
"h": 62
},
"frame": {
"x": 0,
"y": 0,
"w": 57,
"h": 68
"w": 51,
"h": 62
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:76ef75ea5c9561531c220cdb7f78934d:6f39eb82deb32d251df708f5d4edc9f1:bf2f862a1c8881133f9f3ffbf44602fa$"
"smartupdate": "$TexturePacker:SmartUpdate:ba172ad89c93e6e6f36f621c38e54754:7c9d0d13f309221141cd334833e412a5:bf2f862a1c8881133f9f3ffbf44602fa$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 782 B

View File

@ -4,30 +4,30 @@
"image": "676-diamond.png",
"format": "RGBA8888",
"size": {
"w": 74,
"h": 74
"w": 68,
"h": 68
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 51,
"h": 74
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 51,
"h": 74
"x": 27,
"y": 12,
"w": 45,
"h": 68
},
"frame": {
"x": 0,
"y": 0,
"w": 51,
"h": 74
"w": 45,
"h": 68
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:d2b094f0534f6acd2e5bbe67b8e94fa8:47037a1746645a551e31781cb8262e1f:88c467fc09249b0d2f579d44eacd528d$"
"smartupdate": "$TexturePacker:SmartUpdate:0a6f3993923b87e1c16c84b1aa8c3705:6d59d20d1b0cd07c3e7a16eadf0de4cf:88c467fc09249b0d2f579d44eacd528d$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 696 B

View File

@ -4,30 +4,30 @@
"image": "676-heart.png",
"format": "RGBA8888",
"size": {
"w": 71,
"h": 71
"w": 65,
"h": 65
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 52,
"h": 71
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 52,
"h": 71
"x": 26,
"y": 15,
"w": 46,
"h": 65
},
"frame": {
"x": 0,
"y": 0,
"w": 52,
"h": 71
"w": 46,
"h": 65
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:537e6149183f630be038f053dd62d27d:dce25f0e6474cad17709f18c96ffb3e5:e11d4dd16bff2ef69bc5ca683c44ca7c$"
"smartupdate": "$TexturePacker:SmartUpdate:f99ca7d216355ffd449999125499d32e:cf2dd50594846ac631d9da32cd66dceb:e11d4dd16bff2ef69bc5ca683c44ca7c$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 730 B

View File

@ -4,30 +4,30 @@
"image": "676-kabuki.png",
"format": "RGBA8888",
"size": {
"w": 71,
"h": 71
"w": 65,
"h": 65
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 55,
"h": 71
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 55,
"h": 71
"x": 23,
"y": 15,
"w": 49,
"h": 65
},
"frame": {
"x": 0,
"y": 0,
"w": 55,
"h": 71
"w": 49,
"h": 65
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:dfc02a08763810a151fc4e22c23a3093:2f6c7f2792f64728b478333fc2c65138:c3cad56aa65d2ee971ceebc435a14ee6$"
"smartupdate": "$TexturePacker:SmartUpdate:8de2d15ea2537eb2104a72a133418e94:903840ea77662ae31175e482a712402a:c3cad56aa65d2ee971ceebc435a14ee6$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 729 B

View File

@ -4,30 +4,30 @@
"image": "676-la-reine.png",
"format": "RGBA8888",
"size": {
"w": 69,
"h": 69
"w": 63,
"h": 63
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 69
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 69
"x": 24,
"y": 17,
"w": 48,
"h": 63
},
"frame": {
"x": 0,
"y": 0,
"w": 54,
"h": 69
"w": 48,
"h": 63
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:014beffd470b78d94c3234500d9dee5b:3261fa54b4c9574937d670de782ca3e8:b99821e87dffda3297dd25a224a9029f$"
"smartupdate": "$TexturePacker:SmartUpdate:b72b1f5723bb276f4a555ccdbb3ed4c0:0ab778e99ee37c883a0b1a0f918f1bee:b99821e87dffda3297dd25a224a9029f$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 655 B

View File

@ -4,30 +4,30 @@
"image": "676-matron.png",
"format": "RGBA8888",
"size": {
"w": 68,
"h": 68
"w": 62,
"h": 62
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 53,
"h": 68
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 53,
"h": 68
"x": 26,
"y": 18,
"w": 47,
"h": 62
},
"frame": {
"x": 0,
"y": 0,
"w": 53,
"h": 68
"w": 47,
"h": 62
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:a9199442be3400b8a70f8ad212790732:18b55adc9c377d1b24d6b236e1f39177:020798583d07c479020bb0af34cf8bb6$"
"smartupdate": "$TexturePacker:SmartUpdate:d447de0737ae7f9ca8690e7f039a884d:1ffc2bfd67fda79ede1d0e489bf36c94:020798583d07c479020bb0af34cf8bb6$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 740 B

View File

@ -4,30 +4,30 @@
"image": "676-pharaoh.png",
"format": "RGBA8888",
"size": {
"w": 70,
"h": 70
"w": 64,
"h": 64
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 53,
"h": 70
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 53,
"h": 70
"x": 25,
"y": 16,
"w": 47,
"h": 64
},
"frame": {
"x": 0,
"y": 0,
"w": 53,
"h": 70
"w": 47,
"h": 64
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:be799b3acab0a591e7b2d4a2ad903c94:6260eeb9aa48a1047c8ec12842ac28c6:904b7ab6f57249c96beea4b5f72deb53$"
"smartupdate": "$TexturePacker:SmartUpdate:729eefb95778889aa11840e684f35af8:9c72a2d345e9be40096dffd6b99a7ba0:904b7ab6f57249c96beea4b5f72deb53$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 725 B

View File

@ -4,30 +4,30 @@
"image": "676-star.png",
"format": "RGBA8888",
"size": {
"w": 72,
"h": 72
"w": 66,
"h": 66
},
"scale": 1,
"scale": 0.333,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 54,
"h": 72
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 54,
"h": 72
"x": 24,
"y": 14,
"w": 48,
"h": 66
},
"frame": {
"x": 0,
"y": 0,
"w": 54,
"h": 72
"w": 48,
"h": 66
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:8a18f8f110bc4f6f6e86ca95b9a566b8:c9e3d3db02b6ea0720a20b9bbd159d79:dd7fedf66b493b0bbdef5d25e0041dd8$"
"smartupdate": "$TexturePacker:SmartUpdate:37e89522e7b92408df9d52c3d98137ea:241a531f0918e0a4b616569e7d4bc186:dd7fedf66b493b0bbdef5d25e0041dd8$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 741 B

View File

@ -1,4 +1,4 @@
import Phaser, { Time } from 'phaser';
import Phaser from 'phaser';
import UI, { Mode } from './ui/ui';
import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from './phases';
import Pokemon, { PlayerPokemon, EnemyPokemon } from './field/pokemon';
@ -53,13 +53,14 @@ import CharSprite from './ui/char-sprite';
import DamageNumberHandler from './field/damage-number-handler';
import PokemonInfoContainer from './ui/pokemon-info-container';
import { biomeDepths, getBiomeName } from './data/biomes';
import { initTouchControls } from './touch-controls';
import { UiTheme } from './enums/ui-theme';
import { SceneBase } from './scene-base';
import CandyBar from './ui/candy-bar';
import { Variant, variantData } from './data/variant';
import { Localizable } from './plugins/i18n';
import { STARTING_WAVE_OVERRIDE, OPP_SPECIES_OVERRIDE, SEED_OVERRIDE, STARTING_BIOME_OVERRIDE } from './overrides';
import {InputsController} from "./inputs-controller";
import {UiInputs} from "./ui-inputs";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -68,33 +69,12 @@ const DEBUG_RNG = false;
export const startingWave = STARTING_WAVE_OVERRIDE || 1;
const expSpriteKeys: string[] = [];
const repeatInputDelayMillis = 250;
export let starterColors: StarterColors;
interface StarterColors {
[key: string]: [string, string]
}
export enum Button {
UP,
DOWN,
LEFT,
RIGHT,
SUBMIT,
ACTION,
CANCEL,
MENU,
STATS,
CYCLE_SHINY,
CYCLE_FORM,
CYCLE_GENDER,
CYCLE_ABILITY,
CYCLE_NATURE,
CYCLE_VARIANT,
SPEED_UP,
SLOW_DOWN
}
export interface PokeballCounts {
[pb: string]: integer;
}
@ -103,6 +83,8 @@ export type AnySound = Phaser.Sound.WebAudioSound | Phaser.Sound.HTML5AudioSound
export default class BattleScene extends SceneBase {
public rexUI: UIPlugin;
public inputController: InputsController;
public uiInputs: UiInputs;
public sessionPlayTime: integer = null;
public masterVolume: number = 0.5;
@ -123,6 +105,7 @@ export default class BattleScene extends SceneBase {
public gamepadSupport: boolean = true;
public enableTouchControls: boolean = false;
public enableVibration: boolean = false;
public abSwapped: boolean = false;
public disableMenu: boolean = false;
@ -192,34 +175,6 @@ export default class BattleScene extends SceneBase {
private bgmCache: Set<string> = new Set();
private playTimeTimer: Phaser.Time.TimerEvent;
private buttonKeys: Phaser.Input.Keyboard.Key[][];
private lastProcessedButtonPressTimes: Map<Button, number> = new Map();
// movementButtonLock ensures only a single movement key is firing repeated inputs
// (i.e. by holding down a button) at a time
private movementButtonLock: Button;
// using a dualshock controller as a map
private gamepadKeyConfig = {
[Button.UP]: 12, // up
[Button.DOWN]: 13, // down
[Button.LEFT]: 14, // left
[Button.RIGHT]: 15, // right
[Button.SUBMIT]: 17, // touchpad
[Button.ACTION]: 0, // X
[Button.CANCEL]: 1, // O
[Button.MENU]: 9, // options
[Button.STATS]: 8, // share
[Button.CYCLE_SHINY]: 5, // RB
[Button.CYCLE_FORM]: 4, // LB
[Button.CYCLE_GENDER]: 6, // LT
[Button.CYCLE_ABILITY]: 7, // RT
[Button.CYCLE_NATURE]: 2, // square
[Button.CYCLE_VARIANT]: 3, // triangle
[Button.SPEED_UP]: 10, // L3
[Button.SLOW_DOWN]: 11 // R3
};
public gamepadButtonStates: boolean[] = new Array(17).fill(false);
public rngCounter: integer = 0;
public rngSeedOverride: string = '';
public rngOffset: integer = 0;
@ -271,13 +226,13 @@ export default class BattleScene extends SceneBase {
create() {
initGameSpeed.apply(this);
this.inputController = new InputsController(this);
this.uiInputs = new UiInputs(this, this.inputController);
this.gameData = new GameData(this);
addUiThemeOverrides(this);
this.setupControls();
this.load.setBaseURL();
this.spritePipeline = new SpritePipeline(this.game);
@ -290,7 +245,7 @@ export default class BattleScene extends SceneBase {
}
update() {
this.checkInput();
this.inputController.update();
this.ui?.update();
}
@ -609,42 +564,6 @@ export default class BattleScene extends SceneBase {
return true;
}
setupControls() {
const keyCodes = Phaser.Input.Keyboard.KeyCodes;
const keyConfig = {
[Button.UP]: [keyCodes.UP, keyCodes.W],
[Button.DOWN]: [keyCodes.DOWN, keyCodes.S],
[Button.LEFT]: [keyCodes.LEFT, keyCodes.A],
[Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D],
[Button.SUBMIT]: [keyCodes.ENTER],
[Button.ACTION]: [keyCodes.SPACE, keyCodes.ENTER, keyCodes.Z],
[Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X],
[Button.MENU]: [keyCodes.ESC, keyCodes.M],
[Button.STATS]: [keyCodes.SHIFT, keyCodes.C],
[Button.CYCLE_SHINY]: [keyCodes.R],
[Button.CYCLE_FORM]: [keyCodes.F],
[Button.CYCLE_GENDER]: [keyCodes.G],
[Button.CYCLE_ABILITY]: [keyCodes.E],
[Button.CYCLE_NATURE]: [keyCodes.N],
[Button.CYCLE_VARIANT]: [keyCodes.V],
[Button.SPEED_UP]: [keyCodes.PLUS],
[Button.SLOW_DOWN]: [keyCodes.MINUS]
};
const mobileKeyConfig = {};
this.buttonKeys = [];
for (let b of Utils.getEnumValues(Button)) {
const keys: Phaser.Input.Keyboard.Key[] = [];
if (keyConfig.hasOwnProperty(b)) {
for (let k of keyConfig[b])
keys.push(this.input.keyboard.addKey(k, false));
mobileKeyConfig[Button[b]] = keys[0];
}
this.buttonKeys[b] = keys;
}
initTouchControls(mobileKeyConfig);
}
getParty(): PlayerPokemon[] {
return this.party;
}
@ -1116,7 +1035,7 @@ export default class BattleScene extends SceneBase {
return 5;
let isBoss: boolean;
if (forceBoss || (species && (species.pseudoLegendary || species.legendary || species.mythical)))
if (forceBoss || (species && (species.subLegendary || species.legendary || species.mythical)))
isBoss = true;
else {
this.executeWithSeedOffset(() => {
@ -1348,177 +1267,6 @@ export default class BattleScene extends SceneBase {
return biomes[Utils.randSeedInt(biomes.length)];
}
checkInput(): boolean {
let inputSuccess = false;
let vibrationLength = 0;
if (this.buttonJustPressed(Button.UP) || this.repeatInputDurationJustPassed(Button.UP)) {
inputSuccess = this.ui.processInput(Button.UP);
vibrationLength = 5;
this.setLastProcessedMovementTime(Button.UP)
} else if (this.buttonJustPressed(Button.DOWN) || this.repeatInputDurationJustPassed(Button.DOWN)) {
inputSuccess = this.ui.processInput(Button.DOWN);
vibrationLength = 5;
this.setLastProcessedMovementTime(Button.DOWN)
} else if (this.buttonJustPressed(Button.LEFT) || this.repeatInputDurationJustPassed(Button.LEFT)) {
inputSuccess = this.ui.processInput(Button.LEFT);
vibrationLength = 5;
this.setLastProcessedMovementTime(Button.LEFT)
} else if (this.buttonJustPressed(Button.RIGHT) || this.repeatInputDurationJustPassed(Button.RIGHT)) {
inputSuccess = this.ui.processInput(Button.RIGHT);
vibrationLength = 5;
this.setLastProcessedMovementTime(Button.RIGHT)
} else if (this.buttonJustPressed(Button.SUBMIT) || this.repeatInputDurationJustPassed(Button.SUBMIT)) {
inputSuccess = this.ui.processInput(Button.SUBMIT) || this.ui.processInput(Button.ACTION);
this.setLastProcessedMovementTime(Button.SUBMIT);
} else if (this.buttonJustPressed(Button.ACTION) || this.repeatInputDurationJustPassed(Button.ACTION)) {
inputSuccess = this.ui.processInput(Button.ACTION);
this.setLastProcessedMovementTime(Button.ACTION);
} else if (this.buttonJustPressed(Button.CANCEL)|| this.repeatInputDurationJustPassed(Button.CANCEL)) {
inputSuccess = this.ui.processInput(Button.CANCEL);
this.setLastProcessedMovementTime(Button.CANCEL);
} else if (this.buttonJustPressed(Button.MENU)) {
if (this.disableMenu)
return;
switch (this.ui?.getMode()) {
case Mode.MESSAGE:
if (!(this.ui.getHandler() as MessageUiHandler).pendingPrompt)
return;
case Mode.TITLE:
case Mode.COMMAND:
case Mode.FIGHT:
case Mode.BALL:
case Mode.TARGET_SELECT:
case Mode.SAVE_SLOT:
case Mode.PARTY:
case Mode.SUMMARY:
case Mode.STARTER_SELECT:
case Mode.CONFIRM:
case Mode.OPTION_SELECT:
this.ui.setOverlayMode(Mode.MENU);
inputSuccess = true;
break;
case Mode.MENU:
case Mode.SETTINGS:
case Mode.ACHIEVEMENTS:
this.ui.revertMode();
this.playSound('select');
inputSuccess = true;
break;
default:
return;
}
} else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) {
if (this.buttonJustPressed(Button.CYCLE_SHINY)) {
inputSuccess = this.ui.processInput(Button.CYCLE_SHINY);
this.setLastProcessedMovementTime(Button.CYCLE_SHINY);
} else if (this.buttonJustPressed(Button.CYCLE_FORM)) {
inputSuccess = this.ui.processInput(Button.CYCLE_FORM);
this.setLastProcessedMovementTime(Button.CYCLE_FORM);
} else if (this.buttonJustPressed(Button.CYCLE_GENDER)) {
inputSuccess = this.ui.processInput(Button.CYCLE_GENDER);
this.setLastProcessedMovementTime(Button.CYCLE_GENDER);
} else if (this.buttonJustPressed(Button.CYCLE_ABILITY)) {
inputSuccess = this.ui.processInput(Button.CYCLE_ABILITY);
this.setLastProcessedMovementTime(Button.CYCLE_ABILITY);
} else if (this.buttonJustPressed(Button.CYCLE_NATURE)) {
inputSuccess = this.ui.processInput(Button.CYCLE_NATURE);
this.setLastProcessedMovementTime(Button.CYCLE_NATURE);
} else if (this.buttonJustPressed(Button.CYCLE_VARIANT)) {
inputSuccess = this.ui.processInput(Button.CYCLE_VARIANT);
this.setLastProcessedMovementTime(Button.CYCLE_VARIANT);
} else
return;
} else if (this.buttonJustPressed(Button.SPEED_UP)) {
if (this.gameSpeed < 5) {
this.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) + 1);
if (this.ui?.getMode() === Mode.SETTINGS)
(this.ui.getHandler() as SettingsUiHandler).show([]);
}
} else if (this.buttonJustPressed(Button.SLOW_DOWN)) {
if (this.gameSpeed > 1) {
this.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) - 1, 0));
if (this.ui?.getMode() === Mode.SETTINGS)
(this.ui.getHandler() as SettingsUiHandler).show([]);
}
} else {
let pressed = false;
if (this.ui && (this.buttonJustReleased(Button.STATS) || (pressed = this.buttonJustPressed(Button.STATS)))) {
for (let p of this.getField().filter(p => p?.isActive(true)))
p.toggleStats(pressed);
if (pressed)
this.setLastProcessedMovementTime(Button.STATS);
} else
return;
}
if (inputSuccess && this.enableVibration && typeof navigator.vibrate !== 'undefined')
navigator.vibrate(vibrationLength || 10);
}
/**
* gamepadButtonJustDown returns true if @param button has just been pressed down
* or not. It will only return true once, until the key is released and pressed down
* again.
*/
gamepadButtonJustDown(button: Phaser.Input.Gamepad.Button): boolean {
if (!button || !this.gamepadSupport)
return false;
let ret = false;
if (button.pressed) {
if (!this.gamepadButtonStates[button.index])
ret = true;
this.gamepadButtonStates[button.index] = true;
} else
this.gamepadButtonStates[button.index] = false;
return ret;
}
buttonJustPressed(button: Button): boolean {
const gamepad = this.input.gamepad?.gamepads[0];
return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustDown(k)) || this.gamepadButtonJustDown(gamepad?.buttons[this.gamepadKeyConfig[button]]);
}
/**
* gamepadButtonJustUp returns true if @param button has just been released
* or not. It will only return true once, until the key is released and pressed down
* again.
*/
gamepadButtonJustUp(button: Phaser.Input.Gamepad.Button): boolean {
if (!button || !this.gamepadSupport)
return false;
return !this.gamepadButtonStates[button.index];
}
buttonJustReleased(button: Button): boolean {
const gamepad = this.input.gamepad?.gamepads[0];
return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustUp(k)) || this.gamepadButtonJustUp(gamepad?.buttons[this.gamepadKeyConfig[button]]);
}
/**
* repeatInputDurationJustPassed returns true if @param button has been held down long
* enough to fire a repeated input. A button must claim the movementButtonLock before
* firing a repeated input - this is to prevent multiple buttons from firing repeatedly.
*/
repeatInputDurationJustPassed(button: Button): boolean {
if (this.movementButtonLock !== null && this.movementButtonLock !== button) {
return false;
}
if (this.buttonKeys[button].every(k => k.isUp) && this.gamepadButtonStates.every(b => b == false)) {
this.movementButtonLock = null;
return false;
}
if (this.time.now - this.lastProcessedButtonPressTimes.get(button) >= repeatInputDelayMillis) {
return true;
}
}
setLastProcessedMovementTime(button: Button) {
this.lastProcessedButtonPressTimes.set(button, this.time.now);
this.movementButtonLock = button;
}
isBgmPlaying(): boolean {
return this.bgm && this.bgm.isPlaying;
}
@ -1676,11 +1424,13 @@ export default class BattleScene extends SceneBase {
return 13.122;
case 'battle_unova_gym':
return 19.145;
case 'battle_legendary':
case 'battle_legendary_regis': //B2W2 Legendary Titan Battle
return 49.500;
case 'battle_legendary_unova': //BW Unova Legendary Battle
return 13.855;
case 'battle_legendary_k':
case 'battle_legendary_kyurem': //BW Kyurem Battle
return 18.314;
case 'battle_legendary_rz':
case 'battle_legendary_res_zek': //BW Reshiram & Zekrom Battle
return 18.329;
case 'battle_rival':
return 13.689;

View File

@ -194,12 +194,18 @@ export default class Battle {
return 'battle_final';
return 'battle_final_encounter';
}
if (pokemon.species.legendary || pokemon.species.pseudoLegendary || pokemon.species.mythical) {
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO)
return 'battle_legendary_regis';
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT)
return 'battle_legendary_unova';
if (pokemon.species.speciesId === Species.RESHIRAM || pokemon.species.speciesId === Species.ZEKROM)
return 'battle_legendary_res_zek';
if (pokemon.species.speciesId === Species.KYUREM)
return 'battle_legendary_k';
return 'battle_legendary_kyurem';
if (pokemon.species.legendary)
return 'battle_legendary_rz';
return 'battle_legendary';
return 'battle_legendary_res_zek';
return 'battle_legendary_unova';
}
}

View File

@ -0,0 +1,29 @@
/**
* Dualshock mapping
*/
const pad_dualshock = {
padID: 'Dualshock',
padType: 'Sony',
gamepadMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9, // Options
SELECT: 8, // Share
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
MENU: 16,
TOUCH: 17
},
};
export default pad_dualshock;

View File

@ -0,0 +1,27 @@
/**
* Generic pad mapping
*/
const pad_generic = {
padID: 'Generic',
padType: 'generic',
gamepadMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15
},
};
export default pad_generic;

View File

@ -0,0 +1,23 @@
/**
* 081f-e401 - UnlicensedSNES
*/
const pad_unlicensedSNES = {
padID: '081f-e401',
padType: 'snes',
gamepadMapping : {
RC_S: 2,
RC_E: 1,
RC_W: 3,
RC_N: 0,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15
}
};
export default pad_unlicensedSNES;

View File

@ -0,0 +1,28 @@
/**
* Generic pad mapping
*/
const pad_xbox360 = {
padID: 'Xbox 360 controller (XInput STANDARD GAMEPAD)',
padType: 'xbox',
gamepadMapping: {
RC_S: 0,
RC_E: 1,
RC_W: 2,
RC_N: 3,
START: 9,
SELECT: 8,
LB: 4,
RB: 5,
LT: 6,
RT: 7,
LS: 10,
RS: 11,
LC_N: 12,
LC_S: 13,
LC_W: 14,
LC_E: 15,
MENU: 16
},
};
export default pad_xbox360;

View File

@ -20,6 +20,8 @@ import { SpeciesFormChangeManualTrigger } from "./pokemon-forms";
import { Abilities } from "./enums/abilities";
import i18next, { Localizable } from "#app/plugins/i18n.js";
import { Command } from "../ui/command-ui-handler";
import Battle from "#app/battle.js";
import { ability } from "#app/locales/en/ability.js";
export class Ability implements Localizable {
public id: Abilities;
@ -247,7 +249,7 @@ export class PreDefendFormChangeAbAttr extends PreDefendAbAttr {
}
export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (pokemon.getHpRatio() < 1 || (args[0] as Utils.NumberHolder).value < pokemon.hp)
if (pokemon.getMaxHp() <= 1 && (pokemon.getHpRatio() < 1 || (args[0] as Utils.NumberHolder).value < pokemon.hp))
return false;
return pokemon.addTag(BattlerTagType.STURDY, 1);
@ -1271,6 +1273,40 @@ export class IgnoreOpponentStatChangesAbAttr extends AbAttr {
}
}
export class IntimidateImmunityAbAttr extends AbAttr {
constructor() {
super(false);
}
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
cancelled.value = true;
return true;
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
return getPokemonMessage(pokemon, `'s ${abilityName} prevented it from being Intimidated!`);
}
}
export class PostIntimidateStatChangeAbAttr extends AbAttr {
private stats: BattleStat[];
private levels: integer;
private overwrites: boolean;
constructor(stats: BattleStat[], levels: integer, overwrites?: boolean) {
super(true)
this.stats = stats
this.levels = levels
this.overwrites = !!overwrites
}
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
pokemon.scene.pushPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, this.levels));
cancelled.value = this.overwrites;
return true;
}
}
export class PostSummonAbAttr extends AbAttr {
applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
return false;
@ -1313,34 +1349,36 @@ export class PostSummonStatChangeAbAttr extends PostSummonAbAttr {
private stats: BattleStat[];
private levels: integer;
private selfTarget: boolean;
private intimidate: boolean;
constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean) {
super();
constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, intimidate?: boolean) {
super(false);
this.stats = typeof(stats) === 'number'
? [ stats as BattleStat ]
: stats as BattleStat[];
this.levels = levels;
this.selfTarget = !!selfTarget;
this.intimidate = !!intimidate;
}
applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
const statChangePhases: StatChangePhase[] = [];
if (this.selfTarget)
statChangePhases.push(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels));
else {
for (let opponent of pokemon.getOpponents())
statChangePhases.push(new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels));
queueShowAbility(pokemon, passive); // TODO: Better solution than manually showing the ability here
if (this.selfTarget) {
pokemon.scene.pushPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels));
return true;
}
for (let statChangePhase of statChangePhases) {
if (!this.selfTarget && !statChangePhase.getPokemon().summonData)
pokemon.scene.pushPhase(statChangePhase); // TODO: This causes the ability bar to be shown at the wrong time
else
for (let opponent of pokemon.getOpponents()) {
const cancelled = new Utils.BooleanHolder(false)
if (this.intimidate) {
applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled);
applyAbAttrs(PostIntimidateStatChangeAbAttr, opponent, cancelled);
}
if (!cancelled.value) {
const statChangePhase = new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels);
pokemon.scene.unshiftPhase(statChangePhase);
}
}
return true;
}
}
@ -2316,6 +2354,13 @@ export class FlinchStatChangeAbAttr extends FlinchEffectAbAttr {
export class IncreasePpAbAttr extends AbAttr { }
export class ForceSwitchOutImmunityAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
cancelled.value = true;
return true;
}
}
export class ReduceBerryUseThresholdAbAttr extends AbAttr {
constructor() {
super();
@ -2647,7 +2692,8 @@ export function initAbilities() {
.condition(getWeatherCondition(WeatherType.SANDSTORM))
.ignorable(),
new Ability(Abilities.STATIC, 3)
.attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.PARALYSIS),
.attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.PARALYSIS)
.bypassFaint(),
new Ability(Abilities.VOLT_ABSORB, 3)
.attr(TypeImmunityHealAbAttr, Type.ELECTRIC)
.ignorable(),
@ -2656,6 +2702,7 @@ export function initAbilities() {
.ignorable(),
new Ability(Abilities.OBLIVIOUS, 3)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.INFATUATED)
.attr(IntimidateImmunityAbAttr)
.ignorable(),
new Ability(Abilities.CLOUD_NINE, 3)
.attr(SuppressWeatherEffectAbAttr, true),
@ -2678,12 +2725,13 @@ export function initAbilities() {
.unimplemented(),
new Ability(Abilities.OWN_TEMPO, 3)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
.attr(IntimidateImmunityAbAttr)
.ignorable(),
new Ability(Abilities.SUCTION_CUPS, 3)
.ignorable()
.unimplemented(),
.attr(ForceSwitchOutImmunityAbAttr)
.ignorable(),
new Ability(Abilities.INTIMIDATE, 3)
.attr(PostSummonStatChangeAbAttr, BattleStat.ATK, -1),
.attr(PostSummonStatChangeAbAttr, BattleStat.ATK, -1, false, true),
new Ability(Abilities.SHADOW_TAG, 3)
.attr(ArenaTrapAbAttr),
new Ability(Abilities.ROUGH_SKIN, 3)
@ -2729,9 +2777,11 @@ export function initAbilities() {
new Ability(Abilities.HUGE_POWER, 3)
.attr(BattleStatMultiplierAbAttr, BattleStat.ATK, 2),
new Ability(Abilities.POISON_POINT, 3)
.attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.POISON),
.attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.POISON)
.bypassFaint(),
new Ability(Abilities.INNER_FOCUS, 3)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.FLINCHED)
.attr(IntimidateImmunityAbAttr)
.ignorable(),
new Ability(Abilities.MAGMA_ARMOR, 3)
.attr(StatusEffectImmunityAbAttr, StatusEffect.FREEZE)
@ -2761,7 +2811,8 @@ export function initAbilities() {
new Ability(Abilities.EARLY_BIRD, 3)
.attr(ReduceStatusEffectDurationAbAttr, StatusEffect.SLEEP),
new Ability(Abilities.FLAME_BODY, 3)
.attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.BURN),
.attr(PostDefendContactApplyStatusEffectAbAttr, 30, StatusEffect.BURN)
.bypassFaint(),
new Ability(Abilities.RUN_AWAY, 3)
.attr(RunSuccessAbAttr),
new Ability(Abilities.KEEN_EYE, 3)
@ -2929,7 +2980,8 @@ export function initAbilities() {
new Ability(Abilities.SLOW_START, 4)
.attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.SLOW_START, 5),
new Ability(Abilities.SCRAPPY, 4)
.unimplemented(),
.attr(IntimidateImmunityAbAttr)
.partial(),
new Ability(Abilities.STORM_DRAIN, 4)
.attr(RedirectTypeMoveAbAttr, Type.WATER)
.attr(TypeImmunityStatChangeAbAttr, Type.WATER, BattleStat.SPATK, 1)
@ -3048,7 +3100,7 @@ export function initAbilities() {
new Ability(Abilities.RATTLED, 5)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS && (move.type === Type.DARK || move.type === Type.BUG ||
move.type === Type.GHOST), BattleStat.SPD, 1)
.partial(),
.attr(PostIntimidateStatChangeAbAttr, [BattleStat.SPD], 1),
new Ability(Abilities.MAGIC_BOUNCE, 5)
.ignorable()
.unimplemented(),
@ -3433,8 +3485,9 @@ export function initAbilities() {
.ignorable()
.partial(),
new Ability(Abilities.GUARD_DOG, 9)
.ignorable()
.unimplemented(),
.attr(PostIntimidateStatChangeAbAttr, [BattleStat.ATK], 1, true)
.attr(ForceSwitchOutImmunityAbAttr)
.ignorable(),
new Ability(Abilities.ROCKY_PAYLOAD, 9)
.attr(MoveTypePowerBoostAbAttr, Type.ROCK),
new Ability(Abilities.WIND_POWER, 9)

View File

@ -340,7 +340,7 @@ export async function printPokemon() {
const speciesKey = (pokemonSpecies as any).key as string;
enumStr += ` ${speciesKey}${pokemonSpecies.speciesId >= 2000 ? ` = ${pokemonSpecies.speciesId}` : ''},\n`;
pokemonSpeciesStr += ` new PokemonSpecies(Species.${speciesKey}, "${pokemonSpecies.name}", ${pokemonSpecies.generation}, ${pokemonSpecies.pseudoLegendary}, ${pokemonSpecies.legendary}, ${pokemonSpecies.mythical}, "${pokemonSpecies.species}", Type.${Type[pokemonSpecies.type1]}, ${pokemonSpecies.type2 ? `Type.${Type[pokemonSpecies.type2]}` : 'null'}, ${pokemonSpecies.height}, ${pokemonSpecies.weight}, Abilities.${Abilities[pokemonSpecies.ability1]}, Abilities.${Abilities[pokemonSpecies.ability2]}, Abilities.${Abilities[pokemonSpecies.abilityHidden]}, ${pokemonSpecies.baseTotal}, ${pokemonSpecies.baseStats[0]}, ${pokemonSpecies.baseStats[1]}, ${pokemonSpecies.baseStats[2]}, ${pokemonSpecies.baseStats[3]}, ${pokemonSpecies.baseStats[4]}, ${pokemonSpecies.baseStats[5]}, ${pokemonSpecies.catchRate}, ${pokemonSpecies.baseFriendship}, ${pokemonSpecies.baseExp}, GrowthRate.${GrowthRate[pokemonSpecies.growthRate]}, ${pokemonSpecies.malePercent}, ${pokemonSpecies.genderDiffs}`;
pokemonSpeciesStr += ` new PokemonSpecies(Species.${speciesKey}, "${pokemonSpecies.name}", ${pokemonSpecies.generation}, ${pokemonSpecies.subLegendary}, ${pokemonSpecies.legendary}, ${pokemonSpecies.mythical}, "${pokemonSpecies.species}", Type.${Type[pokemonSpecies.type1]}, ${pokemonSpecies.type2 ? `Type.${Type[pokemonSpecies.type2]}` : 'null'}, ${pokemonSpecies.height}, ${pokemonSpecies.weight}, Abilities.${Abilities[pokemonSpecies.ability1]}, Abilities.${Abilities[pokemonSpecies.ability2]}, Abilities.${Abilities[pokemonSpecies.abilityHidden]}, ${pokemonSpecies.baseTotal}, ${pokemonSpecies.baseStats[0]}, ${pokemonSpecies.baseStats[1]}, ${pokemonSpecies.baseStats[2]}, ${pokemonSpecies.baseStats[3]}, ${pokemonSpecies.baseStats[4]}, ${pokemonSpecies.baseStats[5]}, ${pokemonSpecies.catchRate}, ${pokemonSpecies.baseFriendship}, ${pokemonSpecies.baseExp}, GrowthRate.${GrowthRate[pokemonSpecies.growthRate]}, ${pokemonSpecies.malePercent}, ${pokemonSpecies.genderDiffs}`;
if (pokemonSpecies.forms.length > 1) {
pokemonSpeciesStr += `, ${pokemonSpecies.canChangeForm},`;
for (let form of pokemonSpecies.forms)

View File

@ -12,7 +12,7 @@ import * as Utils from "../utils";
import { WeatherType } from "./weather";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type";
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr } from "./ability";
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr } from "./ability";
import { Abilities } from "./enums/abilities";
import { allAbilities } from './ability';
import { PokemonHeldItemModifier } from "../modifier/modifier";
@ -326,6 +326,15 @@ export default class Move implements Localizable {
return true;
}
getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
for (let attr of this.attrs) {
let failedText = attr.getFailedText(user, target, move, cancelled);
if (failedText !== null)
return failedText;
}
return null;
}
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
let score = 0;
@ -422,6 +431,10 @@ export abstract class MoveAttr {
return null;
}
getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
return null;
}
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
return 0;
}
@ -434,7 +447,9 @@ export abstract class MoveAttr {
export enum MoveEffectTrigger {
PRE_APPLY,
POST_APPLY,
HIT
HIT,
/** Triggers one time after all target effects have applied */
POST_TARGET,
}
export class MoveEffectAttr extends MoveAttr {
@ -727,19 +742,33 @@ export class SacrificialAttr extends MoveEffectAttr {
}
}
/**
* Attribute used for moves which cut the user's Max HP in half.
* Triggers using POST_TARGET.
*/
export class HalfSacrificialAttr extends MoveEffectAttr {
constructor() {
super(true, MoveEffectTrigger.PRE_APPLY);
super(true, MoveEffectTrigger.POST_TARGET);
}
/**
* Cut's the user's Max HP in half and displays the appropriate recoil message
* @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 {
if (!super.apply(user, target, move, args))
return false;
const cancelled = new Utils.BooleanHolder(false);
// Check to see if the Pokemon has an ability that blocks non-direct damage
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
if (!cancelled.value){
user.damageAndUpdate(Math.ceil(user.getMaxHp()/2), HitResult.OTHER, false, true, true);
user.scene.queueMessage(getPokemonMessage(user, ' cut its own HP to power up its move!')); // Queue recoil message
}
return true;
}
@ -1452,8 +1481,8 @@ export class StatChangeAttr extends MoveEffectAttr {
private condition: MoveConditionFunc;
private showMessage: boolean;
constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) {
super(selfTarget, MoveEffectTrigger.HIT, firstHitOnly);
constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT) {
super(selfTarget, moveEffectTrigger, firstHitOnly);
this.stats = typeof(stats) === 'number'
? [ stats as BattleStat ]
: stats as BattleStat[];
@ -2692,26 +2721,19 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
export class CurseAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean {
// Determine the correct target based on the user's type
if (!user.getTypes(true).includes(Type.GHOST)) {
// For non-Ghost types, target the user itself
target = user;
}
if (user.getTypes(true).includes(Type.GHOST)) {
if (target.getTag(BattlerTagType.CURSED)) {
user.scene.queueMessage('But it failed!');
return false;
}
let curseRecoilDamage = Math.floor(user.getMaxHp() / 2);
let curseRecoilDamage = Math.max(1, Math.floor(user.getMaxHp() / 2));
user.damageAndUpdate(curseRecoilDamage, HitResult.OTHER, false, true, true);
user.scene.queueMessage(getPokemonMessage(user, ` cut its own HP\nand laid a curse on the ${target.name}!`));
target.addTag(BattlerTagType.CURSED, 0, move.id, user.id);
return true;
} else {
target = user;
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), this.selfTarget, [BattleStat.ATK, BattleStat.DEF], 1));
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), this.selfTarget, [BattleStat.SPD], -1));
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF], 1));
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), true, [BattleStat.SPD], -1));
return true;
}
}
@ -2876,7 +2898,7 @@ export class AddArenaTagAttr extends MoveEffectAttr {
public tagType: ArenaTagType;
public turnCount: integer;
private failOnOverlap: boolean;
private selfSideTarget: boolean;
public selfSideTarget: boolean;
constructor(tagType: ArenaTagType, turnCount?: integer, failOnOverlap: boolean = false, selfSideTarget: boolean = false) {
super(true, MoveEffectTrigger.POST_APPLY, true);
@ -2909,9 +2931,10 @@ export class AddArenaTagAttr extends MoveEffectAttr {
export class AddArenaTrapTagAttr extends AddArenaTagAttr {
getCondition(): MoveConditionFunc {
return (user, target, move) => {
if (move.category !== MoveCategory.STATUS || !user.scene.arena.getTag(this.tagType))
const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
if (move.category !== MoveCategory.STATUS || !user.scene.arena.getTagOnSide(this.tagType, side))
return true;
const tag = user.scene.arena.getTag(this.tagType) as ArenaTrapTag;
const tag = user.scene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag;
return tag.layers < tag.maxLayers;
};
}
@ -2963,11 +2986,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
return new Promise(resolve => {
if (!this.user && target.isMax())
return resolve(false);
// Check if the move category is not STATUS or if the switch out condition is not met
if (move.category !== MoveCategory.STATUS && !this.getSwitchOutCondition()(user, target, move)) {
if (!this.getSwitchOutCondition()(user, target, move)) {
//Apply effects before switch out i.e. poison point, flame body, etc
applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, new PokemonMove(move.id), null);
return resolve(false);
@ -3021,7 +3042,13 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
}
getCondition(): MoveConditionFunc {
return (user, target, move) => move.category !== MoveCategory.STATUS || this.getSwitchOutCondition()(user, target, move);
return (user, target, move) => (move.category !== MoveCategory.STATUS || this.getSwitchOutCondition()(user, target, move));
}
getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
const blockedByAbility = new Utils.BooleanHolder(false);
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility);
return blockedByAbility.value ? getPokemonMessage(target, ` can't be switched out!`) : null;
}
getSwitchOutCondition(): MoveConditionFunc {
@ -3029,6 +3056,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
const switchOutTarget = (this.user ? user : target);
const player = switchOutTarget instanceof PlayerPokemon;
if (!this.user && move.category == MoveCategory.STATUS && (target.hasAbilityWithAttr(ForceSwitchOutImmunityAbAttr) || target.isMax()))
return false;
if (!player && !user.scene.currentBattle.battleType) {
if (this.batonPass)
return false;
@ -3791,6 +3821,9 @@ const failOnMaxCondition: MoveConditionFunc = (user, target, move) => !target.is
const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
const cancelled = new Utils.BooleanHolder(false);
user.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled));
// Queue a message if an ability prevented usage of the move
if (cancelled.value)
user.scene.queueMessage(getPokemonMessage(user, ` cannot use ${move.name}!`));
return !cancelled.value;
}
@ -4384,10 +4417,8 @@ export function initMoves() {
.condition((user, target, move) => user.status?.effect === StatusEffect.SLEEP)
.soundBased(),
new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2)
.attr(StatChangeAttr, BattleStat.SPD, -1, true)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], 1, true)
.target(MoveTarget.USER)
.partial(),
.attr(CurseAttr)
.ignoresProtect(true),
new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2)
.attr(LowHpPowerAttr),
new StatusMove(Moves.CONVERSION_2, Type.NORMAL, -1, 30, -1, 0, 2)
@ -5479,7 +5510,7 @@ export function initMoves() {
.soundBased()
.target(MoveTarget.ALL_NEAR_ENEMIES),
new StatusMove(Moves.PARTING_SHOT, Type.DARK, 100, 20, 100, 0, 6)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY)
.attr(ForceSwitchOutAttr, true, false)
.soundBased(),
new StatusMove(Moves.TOPSY_TURVY, Type.DARK, -1, 20, -1, 0, 6)
@ -5888,7 +5919,7 @@ export function initMoves() {
new AttackMove(Moves.PIKA_PAPOW, Type.ELECTRIC, MoveCategory.SPECIAL, -1, -1, 20, -1, 0, 7)
.attr(FriendshipPowerAttr),
new AttackMove(Moves.BOUNCY_BUBBLE, Type.WATER, MoveCategory.SPECIAL, 60, 100, 20, -1, 0, 7)
.attr(HitHealAttr)
.attr(HitHealAttr, 1.0)
.triageMove(),
new AttackMove(Moves.BUZZY_BUZZ, Type.ELECTRIC, MoveCategory.SPECIAL, 60, 100, 20, 100, 0, 7)
.attr(StatusEffectAttr, StatusEffect.PARALYSIS),
@ -6231,7 +6262,7 @@ export function initMoves() {
.partial(),
new SelfStatusMove(Moves.TAKE_HEART, Type.PSYCHIC, -1, 10, -1, 0, 8)
.attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF ], 1, true)
.partial(),
.attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN, StatusEffect.SLEEP),
/* Unused
new AttackMove(Moves.G_MAX_WILDFIRE, Type.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8)
.target(MoveTarget.ALL_NEAR_ENEMIES)

View File

@ -476,7 +476,7 @@ export abstract class PokemonSpeciesForm {
export default class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
public name: string;
public pseudoLegendary: boolean;
public subLegendary: boolean;
public legendary: boolean;
public mythical: boolean;
public species: string;
@ -486,7 +486,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
public canChangeForm: boolean;
public forms: PokemonForm[];
constructor(id: Species, generation: integer, pseudoLegendary: 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,
baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer,
catchRate: integer, baseFriendship: integer, baseExp: integer, growthRate: GrowthRate, malePercent: number,
@ -496,7 +496,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
this.speciesId = id;
this.formIndex = 0;
this.generation = generation;
this.pseudoLegendary = pseudoLegendary;
this.subLegendary = subLegendary;
this.legendary = legendary;
this.mythical = mythical;
this.species = species;
@ -712,14 +712,14 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
getCompatibleFusionSpeciesFilter(): PokemonSpeciesFilter {
const hasEvolution = pokemonEvolutions.hasOwnProperty(this.speciesId);
const hasPrevolution = pokemonPrevolutions.hasOwnProperty(this.speciesId);
const pseudoLegendary = this.pseudoLegendary;
const pseudoLegendary = this.subLegendary;
const legendary = this.legendary;
const mythical = this.mythical;
return species => {
return (pseudoLegendary || legendary || mythical ||
(pokemonEvolutions.hasOwnProperty(species.speciesId) === hasEvolution
&& pokemonPrevolutions.hasOwnProperty(species.speciesId) === hasPrevolution))
&& species.pseudoLegendary === pseudoLegendary
&& species.subLegendary === pseudoLegendary
&& species.legendary === legendary
&& species.mythical === mythical
&& (this.isTrainerForbidden() || !species.isTrainerForbidden());

View File

@ -212,7 +212,7 @@ export class TrainerConfig {
this.battleBgm = 'battle_trainer';
this.victoryBgm = 'victory_trainer';
this.partyTemplates = [ trainerPartyTemplates.TWO_AVG ];
this.speciesFilter = species => (allowLegendaries || (!species.legendary && !species.pseudoLegendary && !species.mythical)) && !species.isTrainerForbidden();
this.speciesFilter = species => (allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical)) && !species.isTrainerForbidden();
}
getKey(): string {
@ -509,7 +509,7 @@ function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSl
function getSpeciesFilterRandomPartyMemberFunc(speciesFilter: PokemonSpeciesFilter, trainerSlot: TrainerSlot = TrainerSlot.TRAINER, allowLegendaries?: boolean, postProcess?: (EnemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
const originalSpeciesFilter = speciesFilter;
speciesFilter = (species: PokemonSpecies) => (allowLegendaries || (!species.legendary && !species.pseudoLegendary && !species.mythical)) && !species.isTrainerForbidden() && originalSpeciesFilter(species);
speciesFilter = (species: PokemonSpecies) => (allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical)) && !species.isTrainerForbidden() && originalSpeciesFilter(species);
return (scene: BattleScene, level: integer, strength: PartyMemberStrength) => {
const ret = scene.addEnemyPokemon(getPokemonSpecies(scene.randomSpecies(scene.currentBattle.waveIndex, level, false, speciesFilter).getTrainerSpeciesForLevel(level, true, strength)), level, trainerSlot, undefined, undefined, postProcess);
return ret;

View File

@ -241,10 +241,12 @@ export class EggHatchPhase extends Phase {
doReveal(): void {
const isShiny = this.pokemon.isShiny();
if (this.pokemon.species.mythical)
this.scene.validateAchv(achvs.HATCH_MYTHICAL);
if (this.pokemon.species.subLegendary)
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
if (this.pokemon.species.legendary)
this.scene.validateAchv(achvs.HATCH_LEGENDARY);
if (this.pokemon.species.mythical)
this.scene.validateAchv(achvs.HATCH_MYTHICAL);
if (isShiny)
this.scene.validateAchv(achvs.HATCH_SHINY);
this.eggContainer.setVisible(false);

19
src/enums/buttons.ts Normal file
View File

@ -0,0 +1,19 @@
export enum Button {
UP,
DOWN,
LEFT,
RIGHT,
SUBMIT,
ACTION,
CANCEL,
MENU,
STATS,
CYCLE_SHINY,
CYCLE_FORM,
CYCLE_GENDER,
CYCLE_ABILITY,
CYCLE_NATURE,
CYCLE_VARIANT,
SPEED_UP,
SLOW_DOWN
}

View File

@ -106,7 +106,7 @@ export class Arena {
ret = getPokemonSpecies(species);
if (ret.pseudoLegendary || ret.legendary || ret.mythical) {
if (ret.subLegendary || ret.legendary || ret.mythical) {
switch (true) {
case (ret.baseTotal >= 720):
regen = level < 90;
@ -492,7 +492,7 @@ export class Arena {
}
addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, targetIndex?: BattlerIndex): boolean {
const existingTag = this.getTag(tagType);
const existingTag = this.getTagOnSide(tagType, side);
if (existingTag) {
existingTag.onOverlap(this);
return false;

View File

@ -1913,6 +1913,21 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, effect, statusCureTurn);
this.setFrameRate(4);
// If the user is invulnerable, lets remove their invulnerability when they fall asleep
const invulnerableTags = [
BattlerTagType.UNDERGROUND,
BattlerTagType.UNDERWATER,
BattlerTagType.HIDDEN,
BattlerTagType.FLYING
];
const tag = invulnerableTags.find((t) => this.getTag(t));
if (tag) {
this.removeTag(tag);
this.getMoveQueue().pop();
}
}
this.status = new Status(effect, 0, statusCureTurn?.value);

View File

@ -133,7 +133,7 @@ export class GameMode implements GameModeConfig {
getOverrideSpecies(waveIndex: integer): PokemonSpecies {
if (this.isDaily && this.isWaveFinal(waveIndex)) {
const allFinalBossSpecies = allSpecies.filter(s => (s.pseudoLegendary || 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);
return Utils.randSeedItem(allFinalBossSpecies);
}

277
src/inputs-controller.ts Normal file
View File

@ -0,0 +1,277 @@
import Phaser, {Time} from "phaser";
import * as Utils from "./utils";
import {initTouchControls} from './touch-controls';
import pad_generic from "./configs/pad_generic";
import pad_unlicensedSNES from "./configs/pad_unlicensedSNES";
import pad_xbox360 from "./configs/pad_xbox360";
import pad_dualshock from "./configs/pad_dualshock";
import {Button} from "./enums/buttons";
export interface GamepadMapping {
[key: string]: number;
}
export interface GamepadConfig {
padID: string;
padType: string;
gamepadMapping: GamepadMapping;
}
export interface ActionGamepadMapping {
[key: string]: Button;
}
const repeatInputDelayMillis = 250;
export class InputsController {
private buttonKeys: Phaser.Input.Keyboard.Key[][];
private gamepads: Array<string> = new Array();
private scene: Phaser.Scene;
// buttonLock ensures only a single movement key is firing repeated inputs
// (i.e. by holding down a button) at a time
private buttonLock: Button;
private interactions: Map<Button, Map<string, boolean>> = new Map();
private time: Time;
private player: Map<String, GamepadMapping> = new Map();
constructor(scene: Phaser.Scene) {
this.scene = scene;
this.time = this.scene.time;
this.buttonKeys = [];
for (const b of Utils.getEnumValues(Button)) {
this.interactions[b] = {
pressTime: false,
isPressed: false,
}
}
// We don't want the menu key to be repeated
delete this.interactions[Button.MENU];
delete this.interactions[Button.STATS];
this.init();
}
init(): void {
this.events = new Phaser.Events.EventEmitter();
// Handle the game losing focus
this.scene.game.events.on(Phaser.Core.Events.BLUR, () => {
this.loseFocus()
})
if (typeof this.scene.input.gamepad !== 'undefined') {
this.scene.input.gamepad.on('connected', function (thisGamepad) {
this.refreshGamepads();
this.setupGamepad(thisGamepad);
}, this);
// Check to see if the gamepad has already been setup by the browser
this.scene.input.gamepad.refreshPads();
if (this.scene.input.gamepad.total) {
this.refreshGamepads();
for (const thisGamepad of this.gamepads) {
this.scene.input.gamepad.emit('connected', thisGamepad);
}
}
this.scene.input.gamepad.on('down', this.gamepadButtonDown, this);
this.scene.input.gamepad.on('up', this.gamepadButtonUp, this);
}
// Keyboard
this.setupKeyboardControls();
}
loseFocus(): void {
this.deactivatePressedKey();
}
update(): void {
for (const b of Utils.getEnumValues(Button)) {
if (!this.interactions.hasOwnProperty(b)) continue;
if (this.repeatInputDurationJustPassed(b) && this.interactions[b].isPressed) {
this.events.emit('input_down', {
controller_type: 'repeated',
button: b,
});
this.setLastProcessedMovementTime(b);
}
}
}
setupGamepad(thisGamepad: Phaser.Input.Gamepad.Gamepad): void {
let gamepadID = thisGamepad.id.toLowerCase();
const mappedPad = this.mapGamepad(gamepadID);
this.player['mapping'] = mappedPad.gamepadMapping;
}
refreshGamepads(): void {
// Sometimes, gamepads are undefined. For some reason.
this.gamepads = this.scene.input.gamepad.gamepads.filter(function (el) {
return el != null;
});
for (const [index, thisGamepad] of this.gamepads.entries()) {
thisGamepad.index = index; // Overwrite the gamepad index, in case we had undefined gamepads earlier
}
}
getActionGamepadMapping(): ActionGamepadMapping {
const gamepadMapping = {};
if (!this.player?.mapping) return gamepadMapping;
gamepadMapping[this.player.mapping.LC_N] = Button.UP;
gamepadMapping[this.player.mapping.LC_S] = Button.DOWN;
gamepadMapping[this.player.mapping.LC_W] = Button.LEFT;
gamepadMapping[this.player.mapping.LC_E] = Button.RIGHT;
gamepadMapping[this.player.mapping.TOUCH] = Button.SUBMIT;
gamepadMapping[this.player.mapping.RC_S] = this.scene.abSwapped ? Button.CANCEL : Button.ACTION;
gamepadMapping[this.player.mapping.RC_E] = this.scene.abSwapped ? Button.ACTION : Button.CANCEL;
gamepadMapping[this.player.mapping.SELECT] = Button.STATS;
gamepadMapping[this.player.mapping.START] = Button.MENU;
gamepadMapping[this.player.mapping.RB] = Button.CYCLE_SHINY;
gamepadMapping[this.player.mapping.LB] = Button.CYCLE_FORM;
gamepadMapping[this.player.mapping.LT] = Button.CYCLE_GENDER;
gamepadMapping[this.player.mapping.RT] = Button.CYCLE_ABILITY;
gamepadMapping[this.player.mapping.RC_W] = Button.CYCLE_NATURE;
gamepadMapping[this.player.mapping.RC_N] = Button.CYCLE_VARIANT;
gamepadMapping[this.player.mapping.LS] = Button.SPEED_UP;
gamepadMapping[this.player.mapping.RS] = Button.SLOW_DOWN;
return gamepadMapping;
}
gamepadButtonDown(pad: Phaser.Input.Gamepad.Gamepad, button: Phaser.Input.Gamepad.Button, value: number): void {
if (!this.scene.gamepadSupport) return;
const actionMapping = this.getActionGamepadMapping();
const buttonDown = actionMapping.hasOwnProperty(button.index) && actionMapping[button.index];
if (buttonDown !== undefined) {
this.events.emit('input_down', {
controller_type: 'gamepad',
button: buttonDown,
});
this.setLastProcessedMovementTime(buttonDown);
}
}
gamepadButtonUp(pad: Phaser.Input.Gamepad.Gamepad, button: Phaser.Input.Gamepad.Button, value: number): void {
if (!this.scene.gamepadSupport) return;
const actionMapping = this.getActionGamepadMapping();
const buttonUp = actionMapping.hasOwnProperty(button.index) && actionMapping[button.index];
if (buttonUp !== undefined) {
this.events.emit('input_up', {
controller_type: 'gamepad',
button: buttonUp,
});
this.delLastProcessedMovementTime(buttonUp);
}
}
setupKeyboardControls(): void {
const keyCodes = Phaser.Input.Keyboard.KeyCodes;
const keyConfig = {
[Button.UP]: [keyCodes.UP, keyCodes.W],
[Button.DOWN]: [keyCodes.DOWN, keyCodes.S],
[Button.LEFT]: [keyCodes.LEFT, keyCodes.A],
[Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D],
[Button.SUBMIT]: [keyCodes.ENTER],
[Button.ACTION]: [keyCodes.SPACE, keyCodes.Z],
[Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X],
[Button.MENU]: [keyCodes.ESC, keyCodes.M],
[Button.STATS]: [keyCodes.SHIFT, keyCodes.C],
[Button.CYCLE_SHINY]: [keyCodes.R],
[Button.CYCLE_FORM]: [keyCodes.F],
[Button.CYCLE_GENDER]: [keyCodes.G],
[Button.CYCLE_ABILITY]: [keyCodes.E],
[Button.CYCLE_NATURE]: [keyCodes.N],
[Button.CYCLE_VARIANT]: [keyCodes.V],
[Button.SPEED_UP]: [keyCodes.PLUS],
[Button.SLOW_DOWN]: [keyCodes.MINUS]
};
const mobileKeyConfig = {};
for (const b of Utils.getEnumValues(Button)) {
const keys: Phaser.Input.Keyboard.Key[] = [];
if (keyConfig.hasOwnProperty(b)) {
for (let k of keyConfig[b])
keys.push(this.scene.input.keyboard.addKey(k, false));
mobileKeyConfig[Button[b]] = keys[0];
}
this.buttonKeys[b] = keys;
}
initTouchControls(mobileKeyConfig);
this.listenInputKeyboard();
}
listenInputKeyboard(): void {
this.buttonKeys.forEach((row, index) => {
for (const key of row) {
key.on('down', () => {
this.events.emit('input_down', {
controller_type: 'keyboard',
button: index,
});
this.setLastProcessedMovementTime(index);
});
key.on('up', () => {
this.events.emit('input_up', {
controller_type: 'keyboard',
button: index,
});
this.delLastProcessedMovementTime(index);
});
}
});
}
mapGamepad(id: string): GamepadConfig {
id = id.toLowerCase();
if (id.includes('081f') && id.includes('e401')) {
return pad_unlicensedSNES;
} else if (id.includes('xbox') && id.includes('360')) {
return pad_xbox360;
} else if (id.includes('054c')) {
return pad_dualshock;
}
return pad_generic;
}
/**
* repeatInputDurationJustPassed returns true if @param button has been held down long
* enough to fire a repeated input. A button must claim the buttonLock before
* firing a repeated input - this is to prevent multiple buttons from firing repeatedly.
*/
repeatInputDurationJustPassed(button: Button): boolean {
if (this.buttonLock === null || this.buttonLock !== button) {
return false;
}
if (this.time.now - this.interactions[button].pressTime >= repeatInputDelayMillis) {
this.buttonLock = null;
return true;
}
}
setLastProcessedMovementTime(button: Button): void {
if (!this.interactions.hasOwnProperty(button)) return;
this.buttonLock = button;
this.interactions[button].pressTime = this.time.now;
this.interactions[button].isPressed = true;
}
delLastProcessedMovementTime(button: Button): void {
if (!this.interactions.hasOwnProperty(button)) return;
this.buttonLock = null;
this.interactions[button].pressTime = null;
this.interactions[button].isPressed = false;
}
deactivatePressedKey(): void {
this.buttonLock = null;
for (const b of Utils.getEnumValues(Button)) {
if (!this.interactions.hasOwnProperty(b)) return;
this.interactions[b].pressTime = null;
this.interactions[b].isPressed = false;
}
}
}

View File

@ -24,7 +24,7 @@ export const battle: SimpleTranslationEntries = {
"expGain": "{{pokemonName}} ha ganado\n{{exp}} puntos de experiencia.",
"levelUp": "¡{{pokemonName}} ha subido al \nNv. {{level}}!",
"learnMove": "¡{{pokemonName}} ha aprendido {{moveName}}!",
"learnMovePrompt": "{{pokemonName}} quiere aprender {{moveName}}.",
"learnMovePrompt": "{{pokemonName}} quiere aprender\n{{moveName}}.",
"learnMoveLimitReached": "Pero, {{pokemonName}} ya conoce\ncuatro movimientos.",
"learnMoveReplaceQuestion": "¿Quieres sustituir uno de sus movimientos por {{moveName}}?",
"learnMoveStopTeaching": "¿Prefieres que no aprenda\n{{moveName}}?",

View File

@ -24,7 +24,7 @@ export const battle: SimpleTranslationEntries = {
"expGain": "{{pokemonName}} ha guadagnato\n{{exp}} Punti Esperienza!",
"levelUp": "{{pokemonName}} è salito al \nLivello {{level}}!",
"learnMove": "{{pokemonName}} impara \n{{moveName}}!",
"learnMovePrompt": "{{pokemonName}} vorrebbe imparare \l{{moveName}}.",
"learnMovePrompt": "{{pokemonName}} vorrebbe imparare\n{{moveName}}.",
"learnMoveLimitReached": "Tuttavia, {{pokemonName}} \nconosce già quattro mosse.",
"learnMoveReplaceQuestion": "Vuoi che ne dimentichi una e al suo \nposto la sostituisca con {{moveName}}?",
"learnMoveStopTeaching": "Vuoi smettere di fargli imparare \n{{moveName}}?",

File diff suppressed because it is too large Load Diff

View File

@ -1026,61 +1026,61 @@ export const pokemon: SimpleTranslationEntries = {
"iron_crown": "Capoferreo",
"terapagos": "Terapagos",
"pecharunt": "Pecharunt",
"alola_rattata": "Rattata di Alola",
"alola_raticate": "Raticate di Alola",
"alola_raichu": "Raichu di Alola",
"alola_sandshrew": "Sandshrew di Alola",
"alola_sandslash": "Sandslash di Alola",
"alola_vulpix": "Vulpix di Alola",
"alola_ninetales": "Ninetales di Alola",
"alola_diglett": "Diglett di Alola",
"alola_dugtrio": "Dugtrio di Alola",
"alola_meowth": "Meowth di Alola",
"alola_persian": "Persian di Alola",
"alola_geodude": "Geodude di Alola",
"alola_graveler": "Graveler di Alola",
"alola_golem": "Golem di Alola",
"alola_grimer": "Grimer di Alola",
"alola_muk": "Muk di Alola",
"alola_exeggutor": "Exeggutor di Alola",
"alola_marowak": "Marowak di Alola",
"alola_rattata": "Rattata",
"alola_raticate": "Raticate",
"alola_raichu": "Raichu",
"alola_sandshrew": "Sandshrew",
"alola_sandslash": "Sandslash",
"alola_vulpix": "Vulpix",
"alola_ninetales": "Ninetales",
"alola_diglett": "Diglett",
"alola_dugtrio": "Dugtrio",
"alola_meowth": "Meowth",
"alola_persian": "Persian",
"alola_geodude": "Geodude",
"alola_graveler": "Graveler",
"alola_golem": "Golem",
"alola_grimer": "Grimer",
"alola_muk": "Muk",
"alola_exeggutor": "Exeggutor",
"alola_marowak": "Marowak",
"eternal_floette": "Floette",
"galar_meowth": "Meowth di Galar",
"galar_ponyta": "Ponyta di Galar",
"galar_rapidash": "Rapidash di Galar",
"galar_slowpoke": "Slowpoke di Galar",
"galar_slowbro": "Slowbro di Galar",
"galar_farfetchd": "Farfetch'd di Galar",
"galar_weezing": "Weezing di Galar",
"galar_mr_mime": "Mr. Mime di Galar",
"galar_articuno": "Articuno di Galar",
"galar_zapdos": "Zapdos di Galar",
"galar_moltres": "Moltres di Galar",
"galar_slowking": "Slowking di Galar",
"galar_corsola": "Corsola di Galar",
"galar_zigzagoon": "Zigzagoon di Galar",
"galar_linoone": "Linoone di Galar",
"galar_darumaka": "Darumaka di Galar",
"galar_darmanitan": "Darmanitan di Galar",
"galar_yamask": "Yamask di Galar",
"galar_stunfisk": "Stunfisk di Galar",
"hisui_growlithe": "Growlithe di Hisui",
"hisui_arcanine": "Arcanine di Hisui",
"hisui_voltorb": "Voltorb di Hisui",
"hisui_electrode": "Electrode di Hisui",
"hisui_typhlosion": "Typhlosion di Hisui",
"hisui_qwilfish": "Qwilfish di Hisui",
"hisui_sneasel": "Sneasel di Hisui",
"hisui_samurott": "Samurott di Hisui",
"hisui_lilligant": "Lilligant di Hisui",
"hisui_zorua": "Zorua di Hisui",
"hisui_zoroark": "Zoroark di Hisui",
"hisui_braviary": "Braviary di Hisui",
"hisui_sliggoo": "Sliggoo di Hisui",
"hisui_goodra": "Goodra di Hisui",
"hisui_avalugg": "Avalugg di Hisui",
"hisui_decidueye": "Decidueye di Hisui",
"paldea_tauros": "Tauros di Paldea",
"paldea_wooper": "Wooper di Paldea",
"bloodmoon_ursaluna": "Ursaluna Luna Cremisi",
"galar_meowth": "Meowth",
"galar_ponyta": "Ponyta",
"galar_rapidash": "Rapidash",
"galar_slowpoke": "Slowpoke",
"galar_slowbro": "Slowbro",
"galar_farfetchd": "Farfetch'd",
"galar_weezing": "Weezing",
"galar_mr_mime": "Mr. Mime",
"galar_articuno": "Articuno",
"galar_zapdos": "Zapdos",
"galar_moltres": "Moltres",
"galar_slowking": "Slowking",
"galar_corsola": "Corsola",
"galar_zigzagoon": "Zigzagoon",
"galar_linoone": "Linoone",
"galar_darumaka": "Darumaka",
"galar_darmanitan": "Darmanitan",
"galar_yamask": "Yamask",
"galar_stunfisk": "Stunfisk",
"hisui_growlithe": "Growlithe",
"hisui_arcanine": "Arcanine",
"hisui_voltorb": "Voltorb",
"hisui_electrode": "Electrode",
"hisui_typhlosion": "Typhlosion",
"hisui_qwilfish": "Qwilfish",
"hisui_sneasel": "Sneasel",
"hisui_samurott": "Samurott",
"hisui_lilligant": "Lilligant",
"hisui_zorua": "Zorua",
"hisui_zoroark": "Zoroark",
"hisui_braviary": "Braviary",
"hisui_sliggoo": "Sliggoo",
"hisui_goodra": "Goodra",
"hisui_avalugg": "Avalugg",
"hisui_decidueye": "Decidueye",
"paldea_tauros": "Tauros",
"paldea_wooper": "Wooper",
"bloodmoon_ursaluna": "Ursaluna",
} as const;

View File

@ -2308,18 +2308,21 @@ export class MovePhase extends BattlePhase {
// Assume conditions affecting targets only apply to moves with a single target
let success = this.move.getMove().applyConditions(this.pokemon, targets[0], this.move.getMove());
let failedText = null;
let cancelled = new Utils.BooleanHolder(false);
let failedText = this.move.getMove().getFailedText(this.pokemon, targets[0], this.move.getMove(), cancelled);
if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove()))
success = false;
else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) {
success = false;
failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain.terrainType);
if (failedText == null)
failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain.terrainType);
}
if (success)
this.scene.unshiftPhase(this.getEffectPhase());
else {
this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual });
this.showFailedText(failedText);
if (!cancelled.value)
this.showFailedText(failedText);
}
this.end();
@ -2521,6 +2524,9 @@ export class MoveEffectPhase extends PokemonPhase {
});
}));
}
// Trigger effect which should only apply one time after all targeted effects have already applied
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_TARGET,
user, null, this.move.getMove())
Promise.allSettled(applyAttrs).then(() => this.end());
});
});
@ -2869,7 +2875,7 @@ export class StatChangePhase extends PokemonPhase {
if (relLevelStats.length > 1) {
statsFragment = relLevelStats.length >= 5
? 'stats'
: `${relLevelStats.slice(0, -1).map(s => getBattleStatName(s)).join(', ')}, and ${getBattleStatName(relLevelStats[relLevelStats.length - 1])}`;
: `${relLevelStats.slice(0, -1).map(s => getBattleStatName(s)).join(', ')}${relLevelStats.length > 2 ? ',' : ''} and ${getBattleStatName(relLevelStats[relLevelStats.length - 1])}`;
} else
statsFragment = getBattleStatName(relLevelStats[0]);
messages.push(getPokemonMessage(this.getPokemon(), `'s ${statsFragment} ${getBattleStatLevelChangeDescription(Math.abs(parseInt(rl)), levels >= 1)}!`));
@ -4145,7 +4151,10 @@ export class AttemptCapturePhase extends PokemonPhase {
if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1)
this.scene.validateAchv(achvs.HIDDEN_ABILITY);
if (pokemon.species.pseudoLegendary || pokemon.species.legendary)
if (pokemon.species.subLegendary)
this.scene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
if (pokemon.species.legendary)
this.scene.validateAchv(achvs.CATCH_LEGENDARY);
if (pokemon.species.mythical)

View File

@ -134,11 +134,13 @@ export const achvs = {
SPLICE: new Achv('Infinite Fusion', 'Splice two Pokémon together with DNA Splicers', 'dna_splicers', 10),
MINI_BLACK_HOLE: new ModifierAchv('A Hole Lot of Items', 'Acquire a Mini Black Hole', 'mini_black_hole', 25, modifier => modifier instanceof TurnHeldItemTransferModifier).setSecret(),
CATCH_MYTHICAL: new Achv('Mythical', 'Catch a mythical Pokémon', 'strange_ball', 50).setSecret(),
CATCH_LEGENDARY: new Achv('Legendary', 'Catch a legendary Pokémon', 'mb', 75).setSecret(),
CATCH_SUB_LEGENDARY: new Achv('(Sub-)Legendary', 'Catch a sub-legendary Pokémon', 'rb', 75).setSecret(),
CATCH_LEGENDARY: new Achv('Legendary', 'Catch a legendary Pokémon', 'mb', 100).setSecret(),
SEE_SHINY: new Achv('Shiny', 'Find a shiny Pokémon in the wild', 'pb_gold', 75),
SHINY_PARTY: new Achv('That\'s Dedication', 'Have a full party of shiny Pokémon', 'shiny_charm', 100).setSecret(true),
HATCH_MYTHICAL: new Achv('Mythical Egg', 'Hatch a mythical Pokémon from an egg', 'pair_of_tickets', 75).setSecret(),
HATCH_LEGENDARY: new Achv('Legendary Egg', 'Hatch a legendary Pokémon from an egg', 'mystic_ticket', 100).setSecret(),
HATCH_SUB_LEGENDARY: new Achv('Sub-Legendary Egg', 'Hatch a sub-legendary Pokémon from an egg', 'mystic_ticket', 100).setSecret(),
HATCH_LEGENDARY: new Achv('Legendary Egg', 'Hatch a legendary Pokémon from an egg', 'mystic_ticket', 125).setSecret(),
HATCH_SHINY: new Achv('Shiny Egg', 'Hatch a shiny Pokémon from an egg', 'golden_mystic_ticket', 100).setSecret(),
HIDDEN_ABILITY: new Achv('Hidden Potential', 'Catch a Pokémon with a hidden ability', 'ability_charm', 75),
PERFECT_IVS: new Achv('Certificate of Authenticity', 'Get perfect IVs on a Pokémon', 'blunder_policy', 100),

View File

@ -361,8 +361,11 @@ export class GameData {
this.starterData = systemData.starterData;
}
if (systemData.gameStats)
if (systemData.gameStats) {
if (systemData.gameStats.legendaryPokemonCaught !== undefined && systemData.gameStats.subLegendaryPokemonCaught === undefined)
this.fixLegendaryStats(systemData);
this.gameStats = systemData.gameStats;
}
if (systemData.unlocks) {
for (let key of Object.keys(systemData.unlocks)) {
@ -1011,7 +1014,9 @@ export class GameData {
if (incrementCount) {
dexEntry.seenCount++;
this.gameStats.pokemonSeen++;
if (!trainer && pokemon.species.pseudoLegendary || pokemon.species.legendary)
if (!trainer && pokemon.species.subLegendary)
this.gameStats.subLegendaryPokemonSeen++;
else if (!trainer && pokemon.species.legendary)
this.gameStats.legendaryPokemonSeen++;
else if (!trainer && pokemon.species.mythical)
this.gameStats.mythicalPokemonSeen++;
@ -1049,7 +1054,9 @@ export class GameData {
if (!fromEgg) {
dexEntry.caughtCount++;
this.gameStats.pokemonCaught++;
if (pokemon.species.pseudoLegendary || pokemon.species.legendary)
if (pokemon.species.subLegendary)
this.gameStats.subLegendaryPokemonCaught++;
else if (pokemon.species.legendary)
this.gameStats.legendaryPokemonCaught++;
else if (pokemon.species.mythical)
this.gameStats.mythicalPokemonCaught++;
@ -1058,7 +1065,9 @@ export class GameData {
} else {
dexEntry.hatchedCount++;
this.gameStats.pokemonHatched++;
if (pokemon.species.pseudoLegendary || pokemon.species.legendary)
if (pokemon.species.subLegendary)
this.gameStats.subLegendaryPokemonHatched++;
else if (pokemon.species.legendary)
this.gameStats.legendaryPokemonHatched++;
else if (pokemon.species.mythical)
this.gameStats.mythicalPokemonHatched++;
@ -1317,4 +1326,22 @@ export class GameData {
for (let starterId of defaultStarterSpecies)
systemData.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
}
fixLegendaryStats(systemData: SystemSaveData): void {
systemData.gameStats.subLegendaryPokemonSeen = 0;
systemData.gameStats.subLegendaryPokemonCaught = 0;
systemData.gameStats.subLegendaryPokemonHatched = 0;
allSpecies.filter(s => s.subLegendary).forEach(s => {
const dexEntry = systemData.dexData[s.speciesId];
systemData.gameStats.subLegendaryPokemonSeen += dexEntry.seenCount;
systemData.gameStats.legendaryPokemonSeen = Math.max(systemData.gameStats.legendaryPokemonSeen - dexEntry.seenCount, 0);
systemData.gameStats.subLegendaryPokemonCaught += dexEntry.caughtCount;
systemData.gameStats.legendaryPokemonCaught = Math.max(systemData.gameStats.legendaryPokemonCaught - dexEntry.caughtCount, 0);
systemData.gameStats.subLegendaryPokemonHatched += dexEntry.hatchedCount;
systemData.gameStats.legendaryPokemonHatched = Math.max(systemData.gameStats.legendaryPokemonHatched - dexEntry.hatchedCount, 0);
});
systemData.gameStats.subLegendaryPokemonSeen = Math.max(systemData.gameStats.subLegendaryPokemonSeen, systemData.gameStats.subLegendaryPokemonCaught);
systemData.gameStats.legendaryPokemonSeen = Math.max(systemData.gameStats.legendaryPokemonSeen, systemData.gameStats.legendaryPokemonCaught);
systemData.gameStats.mythicalPokemonSeen = Math.max(systemData.gameStats.mythicalPokemonSeen, systemData.gameStats.mythicalPokemonCaught);
}
}

View File

@ -20,6 +20,9 @@ export class GameStats {
public pokemonDefeated: integer;
public pokemonCaught: integer;
public pokemonHatched: integer;
public subLegendaryPokemonSeen: integer;
public subLegendaryPokemonCaught: integer;
public subLegendaryPokemonHatched: integer;
public legendaryPokemonSeen: integer;
public legendaryPokemonCaught: integer;
public legendaryPokemonHatched: integer;
@ -56,6 +59,10 @@ export class GameStats {
this.pokemonDefeated = source?.pokemonDefeated || 0;
this.pokemonCaught = source?.pokemonCaught || 0;
this.pokemonHatched = source?.pokemonHatched || 0;
// Currently handled by migration
this.subLegendaryPokemonSeen = source?.subLegendaryPokemonSeen;
this.subLegendaryPokemonCaught = source?.subLegendaryPokemonCaught;
this.subLegendaryPokemonHatched = source?.subLegendaryPokemonHatched;
this.legendaryPokemonSeen = source?.legendaryPokemonSeen || 0;
this.legendaryPokemonCaught = source?.legendaryPokemonCaught || 0;
this.legendaryPokemonHatched = source?.legendaryPokemonHatched || 0;

View File

@ -25,6 +25,7 @@ export enum Setting {
Fusion_Palette_Swaps = "FUSION_PALETTE_SWAPS",
Player_Gender = "PLAYER_GENDER",
Gamepad_Support = "GAMEPAD_SUPPORT",
Swap_A_and_B = "SWAP_A_B", // Swaps which gamepad button handles ACTION and CANCEL
Touch_Controls = "TOUCH_CONTROLS",
Vibration = "VIBRATION"
}
@ -56,6 +57,7 @@ export const settingOptions: SettingOptions = {
[Setting.Fusion_Palette_Swaps]: [ 'Off', 'On' ],
[Setting.Player_Gender]: [ 'Boy', 'Girl' ],
[Setting.Gamepad_Support]: [ 'Auto', 'Disabled' ],
[Setting.Swap_A_and_B]: [ 'Enabled', 'Disabled' ],
[Setting.Touch_Controls]: [ 'Auto', 'Disabled' ],
[Setting.Vibration]: [ 'Auto', 'Disabled' ]
};
@ -79,6 +81,7 @@ export const settingDefaults: SettingDefaults = {
[Setting.Fusion_Palette_Swaps]: 1,
[Setting.Player_Gender]: 0,
[Setting.Gamepad_Support]: 0,
[Setting.Swap_A_and_B]: 1, // Set to 'Disabled' by default
[Setting.Touch_Controls]: 0,
[Setting.Vibration]: 0
};
@ -148,6 +151,9 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer)
case Setting.Gamepad_Support:
scene.gamepadSupport = settingOptions[setting][value] !== 'Disabled';
break;
case Setting.Swap_A_and_B:
scene.abSwapped = settingOptions[setting][value] !== 'Disabled';
break;
case Setting.Touch_Controls:
scene.enableTouchControls = settingOptions[setting][value] !== 'Disabled' && hasTouchscreen();
const touchControls = document.getElementById('touchControls');

154
src/ui-inputs.ts Normal file
View File

@ -0,0 +1,154 @@
import Phaser from "phaser";
import {Mode} from "./ui/ui";
import {InputsController} from "./inputs-controller";
import MessageUiHandler from "./ui/message-ui-handler";
import StarterSelectUiHandler from "./ui/starter-select-ui-handler";
import {Setting, settingOptions} from "./system/settings";
import SettingsUiHandler from "./ui/settings-ui-handler";
import {Button} from "./enums/buttons";
export interface ActionKeys {
[key in Button]: () => void;
}
export class UiInputs {
private scene: Phaser.Scene;
private events: Phaser.Events;
private inputsController: InputsController;
constructor(scene: Phaser.Scene, inputsController: InputsController) {
this.scene = scene;
this.inputsController = inputsController;
this.init();
}
init(): void {
this.events = this.inputsController.events;
this.listenInputs();
}
listenInputs(): void {
this.events.on('input_down', (event) => {
const actions = this.getActionsKeyDown();
if (!actions.hasOwnProperty(event.button)) return;
actions[event.button]();
}, this);
this.events.on('input_up', (event) => {
const actions = this.getActionsKeyUp();
if (!actions.hasOwnProperty(event.button)) return;
actions[event.button]();
}, this);
}
doVibration(inputSuccess: boolean, vibrationLength: number): void {
if (inputSuccess && this.scene.enableVibration && typeof navigator.vibrate !== 'undefined')
navigator.vibrate(vibrationLength);
}
getActionsKeyDown(): ActionKeys {
const actions = {};
actions[Button.UP] = () => this.buttonDirection(Button.UP);
actions[Button.DOWN] = () => this.buttonDirection(Button.DOWN);
actions[Button.LEFT] = () => this.buttonDirection(Button.LEFT);
actions[Button.RIGHT] = () => this.buttonDirection(Button.RIGHT);
actions[Button.SUBMIT] = () => this.buttonTouch();
actions[Button.ACTION] = () => this.buttonAb(Button.ACTION);
actions[Button.CANCEL] = () => this.buttonAb(Button.CANCEL);
actions[Button.MENU] = () => this.buttonMenu();
actions[Button.STATS] = () => this.buttonStats(true);
actions[Button.CYCLE_SHINY] = () => this.buttonCycleOption(Button.CYCLE_SHINY);
actions[Button.CYCLE_FORM] = () => this.buttonCycleOption(Button.CYCLE_FORM);
actions[Button.CYCLE_GENDER] = () => this.buttonCycleOption(Button.CYCLE_GENDER);
actions[Button.CYCLE_ABILITY] = () => this.buttonCycleOption(Button.CYCLE_ABILITY);
actions[Button.CYCLE_NATURE] = () => this.buttonCycleOption(Button.CYCLE_NATURE);
actions[Button.CYCLE_VARIANT] = () => this.buttonCycleOption(Button.CYCLE_VARIANT);
actions[Button.SPEED_UP] = () => this.buttonSpeedChange();
actions[Button.SLOW_DOWN] = () => this.buttonSpeedChange(false);
return actions;
}
getActionsKeyUp(): ActionKeys {
const actions = {};
actions[Button.STATS] = () => this.buttonStats(false);
return actions;
}
buttonDirection(direction: Button): void {
const inputSuccess = this.scene.ui.processInput(direction);
const vibrationLength = 5;
this.doVibration(inputSuccess, vibrationLength);
}
buttonAb(button: Button): void {
this.scene.ui.processInput(button);
}
buttonTouch(): void {
this.scene.ui.processInput(Button.SUBMIT) || this.scene.ui.processInput(Button.ACTION);
}
buttonStats(pressed: boolean = true): void {
if (pressed) {
for (let p of this.scene.getField().filter(p => p?.isActive(true)))
p.toggleStats(true);
} else {
for (let p of this.scene.getField().filter(p => p?.isActive(true)))
p.toggleStats(false);
}
}
buttonMenu(): void {
if (this.scene.disableMenu)
return;
switch (this.scene.ui?.getMode()) {
case Mode.MESSAGE:
if (!(this.scene.ui.getHandler() as MessageUiHandler).pendingPrompt)
return;
case Mode.TITLE:
case Mode.COMMAND:
case Mode.FIGHT:
case Mode.BALL:
case Mode.TARGET_SELECT:
case Mode.SAVE_SLOT:
case Mode.PARTY:
case Mode.SUMMARY:
case Mode.STARTER_SELECT:
case Mode.CONFIRM:
case Mode.OPTION_SELECT:
this.scene.ui.setOverlayMode(Mode.MENU);
break;
case Mode.MENU:
case Mode.SETTINGS:
case Mode.ACHIEVEMENTS:
this.scene.ui.revertMode();
this.scene.playSound('select');
break;
default:
return
}
}
buttonCycleOption(button: Button): void {
if (this.scene.ui?.getHandler() instanceof StarterSelectUiHandler) {
this.scene.ui.processInput(button);
}
}
buttonSpeedChange(up = true): void {
if (up) {
if (this.scene.gameSpeed < 5) {
this.scene.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) + 1);
if (this.scene.ui?.getMode() === Mode.SETTINGS)
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
}
return;
}
if (this.scene.gameSpeed > 1) {
this.scene.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) - 1, 0));
if (this.scene.ui?.getMode() === Mode.SETTINGS)
(this.scene.ui.getHandler() as SettingsUiHandler).show([]);
}
}
}

View File

@ -1,10 +1,11 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import { addWindow } from "./ui-theme";
import * as Utils from "../utils";
import { argbFromRgba } from "@material/material-color-utilities";
import {Button} from "../enums/buttons";
export interface OptionSelectConfig {
xOffset?: number;

View File

@ -1,9 +1,10 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { Achv, achvs } from "../system/achv";
import MessageUiHandler from "./message-ui-handler";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export default class AchvsUiHandler extends MessageUiHandler {
private achvsContainer: Phaser.GameObjects.Container;

View File

@ -1,6 +1,7 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import {Button} from "../enums/buttons";
export default abstract class AwaitableUiHandler extends UiHandler {
protected awaitingActionInput: boolean;

View File

@ -1,11 +1,12 @@
import { CommandPhase } from "../phases";
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { getPokeballName } from "../data/pokeball";
import { addTextObject, TextStyle } from "./text";
import { Command } from "./command-ui-handler";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export default class BallUiHandler extends UiHandler {
private pokeballSelectContainer: Phaser.GameObjects.Container;

View File

@ -1,4 +1,4 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text";
import { Mode } from "./ui";
import * as Utils from "../utils";
@ -6,6 +6,7 @@ import MessageUiHandler from "./message-ui-handler";
import { getStatName, Stat } from "../data/pokemon-stat";
import { addWindow } from "./ui-theme";
import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
import {Button} from "../enums/buttons";
export default class BattleMessageUiHandler extends MessageUiHandler {
private levelUpStatsContainer: Phaser.GameObjects.Container;

View File

@ -1,10 +1,11 @@
import { CommandPhase } from "../phases";
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { addTextObject, TextStyle } from "./text";
import PartyUiHandler, { PartyUiMode } from "./party-ui-handler";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import i18next from '../plugins/i18n';
import {Button} from "../enums/buttons";
export enum Command {
FIGHT = 0,

View File

@ -1,7 +1,8 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import AbstractOptionSelectUiHandler, { OptionSelectConfig } from "./abstact-option-select-ui-handler";
import { Mode } from "./ui";
import i18next from "i18next";
import {Button} from "../enums/buttons";
export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
private switchCheck: boolean;

View File

@ -1,4 +1,4 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { Mode } from "./ui";
import { TextStyle, addTextObject, getEggTierTextTint } from "./text";
import MessageUiHandler from "./message-ui-handler";
@ -9,6 +9,7 @@ import { getPokemonSpecies } from "../data/pokemon-species";
import { addWindow } from "./ui-theme";
import { Tutorial, handleTutorial } from "../tutorial";
import { EggTier } from "../data/enums/egg-type";
import {Button} from "../enums/buttons";
const defaultText = 'Select a machine.';

View File

@ -1,7 +1,8 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { EggHatchPhase } from "../egg-hatch-phase";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import {Button} from "../enums/buttons";
export default class EggHatchSceneHandler extends UiHandler {
public eggHatchContainer: Phaser.GameObjects.Container;

View File

@ -1,4 +1,4 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { Mode } from "./ui";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
import { TextStyle, addTextObject } from "./text";
@ -6,6 +6,7 @@ import MessageUiHandler from "./message-ui-handler";
import { EGG_SEED, Egg, GachaType, getEggGachaTypeDescriptor, getEggHatchWavesMessage, getEggDescriptor } from "../data/egg";
import * as Utils from "../utils";
import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export default class EggListUiHandler extends MessageUiHandler {
private eggListContainer: Phaser.GameObjects.Container;

View File

@ -1,7 +1,8 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import MessageUiHandler from "./message-ui-handler";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import {Button} from "../enums/buttons";
export default class EvolutionSceneHandler extends MessageUiHandler {
public evolutionContainer: Phaser.GameObjects.Container;

View File

@ -1,4 +1,4 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { addTextObject, TextStyle } from "./text";
import { Type } from "../data/type";
import { Command } from "./command-ui-handler";
@ -8,6 +8,7 @@ import * as Utils from "../utils";
import { CommandPhase } from "../phases";
import { MoveCategory } from "#app/data/move.js";
import i18next from '../plugins/i18n';
import {Button} from "../enums/buttons";
export default class FightUiHandler extends UiHandler {
private movesContainer: Phaser.GameObjects.Container;

View File

@ -1,4 +1,4 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { ModalConfig, ModalUiHandler } from "./modal-ui-handler";
import { Mode } from "./ui";
import { TextStyle, addTextInputObject, addTextObject } from "./text";
@ -6,6 +6,7 @@ import { WindowVariant, addWindow } from "./ui-theme";
import InputText from "phaser3-rex-plugins/plugins/inputtext";
import * as Utils from "../utils";
import i18next from '../plugins/i18n';
import {Button} from "../enums/buttons";
export interface FormModalConfig extends ModalConfig {
errorMessage?: string;

View File

@ -1,4 +1,4 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { TextStyle, addTextObject, getTextColor } from "./text";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
@ -6,6 +6,7 @@ import { addWindow } from "./ui-theme";
import * as Utils from "../utils";
import { DexAttr, GameData } from "../system/game-data";
import { speciesStarters } from "../data/pokemon-species";
import {Button} from "../enums/buttons";
interface DisplayStat {
label?: string;
@ -65,13 +66,16 @@ const displayStats: DisplayStats = {
pokemonDefeated: 'Pokémon Defeated',
pokemonCaught: 'Pokémon Caught',
pokemonHatched: 'Eggs Hatched',
legendaryPokemonSeen: 'Legendary Encounters?',
legendaryPokemonCaught: 'Legendaries Caught?',
legendaryPokemonHatched: 'Legendaries Hatched?',
mythicalPokemonSeen: 'Mythical Encounters?',
subLegendaryPokemonSeen: 'Sub-Legends Seen?',
subLegendaryPokemonCaught: 'Sub-Legends Caught?',
subLegendaryPokemonHatched: 'Sub-Legends Hatched?',
legendaryPokemonSeen: 'Legends Seen?',
legendaryPokemonCaught: 'Legends Caught?',
legendaryPokemonHatched: 'Legends Hatched?',
mythicalPokemonSeen: 'Mythicals Seen?',
mythicalPokemonCaught: 'Mythicals Caught?',
mythicalPokemonHatched: 'Mythicals Hatched?',
shinyPokemonSeen: 'Shiny Encounters?',
shinyPokemonSeen: 'Shinies Seen?',
shinyPokemonCaught: 'Shinies Caught?',
shinyPokemonHatched: 'Shinies Hatched?',
pokemonFused: 'Pokémon Fused?',

View File

@ -1,4 +1,4 @@
import BattleScene, { Button, bypassLogin } from "../battle-scene";
import BattleScene, { bypassLogin } from "../battle-scene";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import * as Utils from "../utils";
@ -9,6 +9,7 @@ import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui
import { Tutorial, handleTutorial } from "../tutorial";
import { updateUserInfo } from "../account";
import i18next from '../plugins/i18n';
import {Button} from "../enums/buttons";
export enum MenuOptions {
GAME_SETTINGS,

View File

@ -1,8 +1,9 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import { WindowVariant, addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export interface ModalConfig {
buttonActions: Function[];

View File

@ -1,4 +1,4 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { getPlayerShopModifierTypeOptionsForWave, ModifierTypeOption } from "../modifier/modifier-type";
import { getPokeballAtlasKey, PokeballType } from "../data/pokeball";
import { addTextObject, getModifierTierTextTint, getTextColor, TextStyle } from "./text";
@ -6,6 +6,7 @@ import AwaitableUiHandler from "./awaitable-ui-handler";
import { Mode } from "./ui";
import { LockModifierTiersModifier, PokemonHeldItemModifier } from "../modifier/modifier";
import { handleTutorial, Tutorial } from "../tutorial";
import {Button} from "../enums/buttons";
export const SHOP_OPTIONS_ROW_LIMIT = 6;

View File

@ -1,5 +1,5 @@
import { CommandPhase } from "../phases";
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { PlayerPokemon, PokemonMove } from "../field/pokemon";
import { addTextObject, TextStyle } from "./text";
import { Command } from "./command-ui-handler";
@ -16,6 +16,7 @@ import { pokemonEvolutions } from "../data/pokemon-evolutions";
import { addWindow } from "./ui-theme";
import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms";
import { getVariantTint } from "#app/data/variant";
import {Button} from "../enums/buttons";
const defaultMessage = 'Choose a Pokémon.';

View File

@ -1,4 +1,4 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { gameModes } from "../game-mode";
import { SessionSaveData } from "../system/game-data";
import { TextStyle, addTextObject } from "./text";
@ -9,6 +9,7 @@ import PokemonData from "../system/pokemon-data";
import { PokemonHeldItemModifier } from "../modifier/modifier";
import MessageUiHandler from "./message-ui-handler";
import i18next from "i18next";
import {Button} from "../enums/buttons";
const sessionSlotCount = 5;

View File

@ -1,10 +1,11 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { Setting, reloadSettings, settingDefaults, settingOptions } from "../system/settings";
import { hasTouchscreen, isMobile } from "../touch-controls";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export default class SettingsUiHandler extends UiHandler {
private settingsContainer: Phaser.GameObjects.Container;

View File

@ -1,5 +1,5 @@
import BattleScene, { Button, starterColors } from "../battle-scene";
import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
import BattleScene, { starterColors } from "../battle-scene";
import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters, starterPassiveAbilities, getStarterValueFriendshipCap } from "../data/pokemon-species";
import { Species } from "../data/enums/species";
import { TextStyle, addBBCodeTextObject, addTextObject } from "./text";
import { Mode } from "./ui";
@ -28,6 +28,7 @@ import { OptionSelectItem } from "./abstact-option-select-ui-handler";
import { pokemonPrevolutions } from "#app/data/pokemon-evolutions";
import { Variant, getVariantTint } from "#app/data/variant";
import i18next from "i18next";
import {Button} from "../enums/buttons";
export type StarterSelectCallback = (starters: Starter[]) => void;
@ -118,6 +119,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[];
private pokemonEggMoveLabels: Phaser.GameObjects.Text[];
private pokemonCandyIcon: Phaser.GameObjects.Sprite;
private pokemonCandyDarknessOverlay: Phaser.GameObjects.Sprite;
private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite;
private pokemonCandyCountText: Phaser.GameObjects.Text;
private pokemonCaughtHatchedContainer: Phaser.GameObjects.Container;
@ -430,7 +432,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonLuckText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonLuckText);
this.pokemonCandyIcon = this.scene.add.sprite(1, 12, 'items', 'candy');
this.pokemonCandyIcon = this.scene.add.sprite(4.5, 18, 'candy');
this.pokemonCandyIcon.setScale(0.5);
this.pokemonCandyIcon.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonCandyIcon);
@ -439,11 +441,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonFormText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonFormText);
this.pokemonCandyOverlayIcon = this.scene.add.sprite(1, 12, 'items', 'candy_overlay');
this.pokemonCandyOverlayIcon = this.scene.add.sprite(4.5, 18, 'candy_overlay');
this.pokemonCandyOverlayIcon.setScale(0.5);
this.pokemonCandyOverlayIcon.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonCandyOverlayIcon);
this.pokemonCandyDarknessOverlay = this.scene.add.sprite(4.5, 18, 'candy');
this.pokemonCandyDarknessOverlay.setScale(0.5);
this.pokemonCandyDarknessOverlay.setOrigin(0, 0);
this.pokemonCandyDarknessOverlay.setTint(0x000000);
this.pokemonCandyDarknessOverlay.setAlpha(0.5);
this.pokemonCandyDarknessOverlay.setInteractive(new Phaser.Geom.Rectangle(0, 0, 16, 16), Phaser.Geom.Rectangle.Contains);
this.starterSelectContainer.add(this.pokemonCandyDarknessOverlay);
this.pokemonCandyCountText = addTextObject(this.scene, 14, 18, 'x0', TextStyle.WINDOW_ALT, { fontSize: '56px' });
this.pokemonCandyCountText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonCandyCountText);
@ -1284,16 +1294,32 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCaughtHatchedContainer.setVisible(true);
if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) {
this.pokemonCaughtHatchedContainer.setY(16);
[ this.pokemonCandyIcon, this.pokemonCandyOverlayIcon, this.pokemonCandyCountText ].map(c => c.setVisible(false));
[ this.pokemonCandyIcon, this.pokemonCandyOverlayIcon, this.pokemonCandyDarknessOverlay, this.pokemonCandyCountText ].map(c => c.setVisible(false));
} else {
this.pokemonCaughtHatchedContainer.setY(25);
this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0])));
this.pokemonCandyIcon.setVisible(true);
this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1])));
this.pokemonCandyOverlayIcon.setVisible(true);
this.pokemonCandyDarknessOverlay.setVisible(true);
this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`);
this.pokemonCandyCountText.setVisible(true);
this.pokemonFormText.setVisible(true);
var currentFriendship = this.scene.gameData.starterData[this.lastSpecies.speciesId].friendship;
if (!currentFriendship || currentFriendship === undefined)
currentFriendship = 0;
const friendshipCap = getStarterValueFriendshipCap(speciesStarters[this.lastSpecies.speciesId]);
const candyCropY = 16 - (16 * (currentFriendship / friendshipCap));
if (this.pokemonCandyDarknessOverlay.visible) {
this.pokemonCandyDarknessOverlay.on('pointerover', () => (this.scene as BattleScene).ui.showTooltip(null, `${currentFriendship}/${friendshipCap}`, true));
this.pokemonCandyDarknessOverlay.on('pointerout', () => (this.scene as BattleScene).ui.hideTooltip());
}
this.pokemonCandyDarknessOverlay.setCrop(0,0,16, candyCropY);
this.pokemonCandyDarknessOverlay.setCrop(0,0,16, candyCropY);
}
this.iconAnimHandler.addOrUpdate(this.starterSelectGenIconContainers[species.generation - 1].getAt(this.genSpecies[species.generation - 1].indexOf(species)) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.PASSIVE);
@ -1342,6 +1368,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCaughtHatchedContainer.setVisible(false);
this.pokemonCandyIcon.setVisible(false);
this.pokemonCandyOverlayIcon.setVisible(false);
this.pokemonCandyDarknessOverlay.setVisible(false);
this.pokemonCandyCountText.setVisible(false);
this.pokemonFormText.setVisible(false);
@ -1369,6 +1396,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCaughtHatchedContainer.setVisible(false);
this.pokemonCandyIcon.setVisible(false);
this.pokemonCandyOverlayIcon.setVisible(false);
this.pokemonCandyDarknessOverlay.setVisible(false);
this.pokemonCandyCountText.setVisible(false);
this.pokemonFormText.setVisible(false);

View File

@ -1,4 +1,4 @@
import BattleScene, { Button, starterColors } from "../battle-scene";
import BattleScene, { starterColors } from "../battle-scene";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import * as Utils from "../utils";
@ -19,6 +19,7 @@ import { Nature, getNatureStatMultiplier } from "../data/nature";
import { loggedInUser } from "../account";
import { PlayerGender } from "../system/game-data";
import { Variant, getVariantTint } from "#app/data/variant";
import {Button} from "../enums/buttons";
enum Page {
PROFILE,

View File

@ -1,10 +1,11 @@
import { BattlerIndex } from "../battle";
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { Moves } from "../data/enums/moves";
import { Mode } from "./ui";
import UiHandler from "./ui-handler";
import * as Utils from "../utils";
import { getMoveTargets } from "../data/move";
import {Button} from "../enums/buttons";
export type TargetSelectCallback = (cursor: integer) => void;

View File

@ -1,6 +1,7 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { TextStyle, getTextColor } from "./text";
import UI, { Mode } from "./ui";
import {Button} from "../enums/buttons";
export default abstract class UiHandler {
protected scene: BattleScene;

View File

@ -1,4 +1,4 @@
import { Button, default as BattleScene } from '../battle-scene';
import { default as BattleScene } from '../battle-scene';
import UiHandler from './ui-handler';
import BattleMessageUiHandler from './battle-message-ui-handler';
import CommandUiHandler from './command-ui-handler';
@ -35,6 +35,7 @@ import SavingIconHandler from './saving-icon-handler';
import UnavailableModalUiHandler from './unavailable-modal-ui-handler';
import OutdatedModalUiHandler from './outdated-modal-ui-handler';
import SessionReloadModalUiHandler from './session-reload-modal-ui-handler';
import {Button} from "../enums/buttons";
export enum Mode {
MESSAGE,

View File

@ -1,9 +1,10 @@
import BattleScene, { Button } from "../battle-scene";
import BattleScene from "../battle-scene";
import { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "../system/voucher";
import MessageUiHandler from "./message-ui-handler";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
const itemRows = 4;
const itemCols = 17;

View File

@ -226,7 +226,7 @@ export const apiUrl = isLocal ? serverUrl : 'api';
export function setCookie(cName: string, cValue: string): void {
const expiration = new Date();
expiration.setTime(new Date().getTime() + 3600000 * 24 * 7);
expiration.setTime(new Date().getTime() + 3600000 * 24 * 30 * 3/*7*/);
document.cookie = `${cName}=${cValue};SameSite=Strict;path=/;expires=${expiration.toUTCString()}`;
}