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", "image": "676-dandy.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 71, "w": 65,
"h": 71 "h": 65
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 56, "w": 96,
"h": 71 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 22,
"y": 0, "y": 15,
"w": 56, "w": 50,
"h": 71 "h": 65
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 56, "w": 50,
"h": 71 "h": 65
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-debutante.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 67, "w": 61,
"h": 67 "h": 61
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 56, "w": 96,
"h": 67 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 20,
"y": 0, "y": 19,
"w": 56, "w": 50,
"h": 67 "h": 61
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 56, "w": 50,
"h": 67 "h": 61
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-diamond.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 74, "w": 68,
"h": 74 "h": 68
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 53, "w": 96,
"h": 74 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 24,
"y": 0, "y": 12,
"w": 53, "w": 47,
"h": 74 "h": 68
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 53, "w": 47,
"h": 74 "h": 68
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-heart.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 72, "w": 66,
"h": 72 "h": 66
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 54, "w": 96,
"h": 72 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 24,
"y": 0, "y": 14,
"w": 54, "w": 48,
"h": 72 "h": 66
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 54, "w": 48,
"h": 72 "h": 66
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-kabuki.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 70, "w": 64,
"h": 70 "h": 64
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 58, "w": 96,
"h": 70 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 22,
"y": 0, "y": 16,
"w": 58, "w": 52,
"h": 70 "h": 64
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 58, "w": 52,
"h": 70 "h": 64
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-la-reine.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 69, "w": 63,
"h": 69 "h": 63
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 55, "w": 96,
"h": 69 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 23,
"y": 0, "y": 17,
"w": 55, "w": 49,
"h": 69 "h": 63
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 55, "w": 49,
"h": 69 "h": 63
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-matron.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 68, "w": 62,
"h": 68 "h": 62
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 52, "w": 96,
"h": 68 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 23,
"y": 0, "y": 18,
"w": 52, "w": 46,
"h": 68 "h": 62
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 52, "w": 46,
"h": 68 "h": 62
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-pharaoh.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 69, "w": 63,
"h": 69 "h": 63
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 52, "w": 96,
"h": 69 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 25,
"y": 0, "y": 17,
"w": 52, "w": 46,
"h": 69 "h": 63
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 52, "w": 46,
"h": 69 "h": 63
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-star.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 72, "w": 66,
"h": 72 "h": 66
}, },
"scale": 1, "scale": 0.5,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 56, "w": 96,
"h": 72 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 24,
"y": 0, "y": 14,
"w": 56, "w": 50,
"h": 72 "h": 66
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 56, "w": 50,
"h": 72 "h": 66
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-dandy.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 72, "w": 66,
"h": 72 "h": 66
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 62, "w": 96,
"h": 72 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 20,
"y": 0, "y": 14,
"w": 62, "w": 56,
"h": 72 "h": 66
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 62, "w": 56,
"h": 72 "h": 66
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-debutante.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 68, "w": 62,
"h": 68 "h": 62
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 57, "w": 96,
"h": 68 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 25,
"y": 0, "y": 18,
"w": 57, "w": 51,
"h": 68 "h": 62
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 57, "w": 51,
"h": 68 "h": 62
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-diamond.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 74, "w": 68,
"h": 74 "h": 68
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 51, "w": 96,
"h": 74 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 27,
"y": 0, "y": 12,
"w": 51, "w": 45,
"h": 74 "h": 68
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 51, "w": 45,
"h": 74 "h": 68
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-heart.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 71, "w": 65,
"h": 71 "h": 65
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 52, "w": 96,
"h": 71 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 26,
"y": 0, "y": 15,
"w": 52, "w": 46,
"h": 71 "h": 65
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 52, "w": 46,
"h": 71 "h": 65
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-kabuki.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 71, "w": 65,
"h": 71 "h": 65
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 55, "w": 96,
"h": 71 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 23,
"y": 0, "y": 15,
"w": 55, "w": 49,
"h": 71 "h": 65
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 55, "w": 49,
"h": 71 "h": 65
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-la-reine.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 69, "w": 63,
"h": 69 "h": 63
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 54, "w": 96,
"h": 69 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 24,
"y": 0, "y": 17,
"w": 54, "w": 48,
"h": 69 "h": 63
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 54, "w": 48,
"h": 69 "h": 63
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-matron.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 68, "w": 62,
"h": 68 "h": 62
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 53, "w": 96,
"h": 68 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 26,
"y": 0, "y": 18,
"w": 53, "w": 47,
"h": 68 "h": 62
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 53, "w": 47,
"h": 68 "h": 62
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-pharaoh.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 70, "w": 64,
"h": 70 "h": 64
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 53, "w": 96,
"h": 70 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 25,
"y": 0, "y": 16,
"w": 53, "w": 47,
"h": 70 "h": 64
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 53, "w": 47,
"h": 70 "h": 64
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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", "image": "676-star.png",
"format": "RGBA8888", "format": "RGBA8888",
"size": { "size": {
"w": 72, "w": 66,
"h": 72 "h": 66
}, },
"scale": 1, "scale": 0.333,
"frames": [ "frames": [
{ {
"filename": "0001.png", "filename": "0001.png",
"rotated": false, "rotated": false,
"trimmed": false, "trimmed": false,
"sourceSize": { "sourceSize": {
"w": 54, "w": 96,
"h": 72 "h": 96
}, },
"spriteSourceSize": { "spriteSourceSize": {
"x": 0, "x": 24,
"y": 0, "y": 14,
"w": 54, "w": 48,
"h": 72 "h": 66
}, },
"frame": { "frame": {
"x": 0, "x": 0,
"y": 0, "y": 0,
"w": 54, "w": 48,
"h": 72 "h": 66
} }
} }
] ]
@ -36,6 +36,6 @@
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "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 UI, { Mode } from './ui/ui';
import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from './phases'; import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from './phases';
import Pokemon, { PlayerPokemon, EnemyPokemon } from './field/pokemon'; 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 DamageNumberHandler from './field/damage-number-handler';
import PokemonInfoContainer from './ui/pokemon-info-container'; import PokemonInfoContainer from './ui/pokemon-info-container';
import { biomeDepths, getBiomeName } from './data/biomes'; import { biomeDepths, getBiomeName } from './data/biomes';
import { initTouchControls } from './touch-controls';
import { UiTheme } from './enums/ui-theme'; import { UiTheme } from './enums/ui-theme';
import { SceneBase } from './scene-base'; import { SceneBase } from './scene-base';
import CandyBar from './ui/candy-bar'; import CandyBar from './ui/candy-bar';
import { Variant, variantData } from './data/variant'; import { Variant, variantData } from './data/variant';
import { Localizable } from './plugins/i18n'; import { Localizable } from './plugins/i18n';
import { STARTING_WAVE_OVERRIDE, OPP_SPECIES_OVERRIDE, SEED_OVERRIDE, STARTING_BIOME_OVERRIDE } from './overrides'; 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"; 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; export const startingWave = STARTING_WAVE_OVERRIDE || 1;
const expSpriteKeys: string[] = []; const expSpriteKeys: string[] = [];
const repeatInputDelayMillis = 250;
export let starterColors: StarterColors; export let starterColors: StarterColors;
interface StarterColors { interface StarterColors {
[key: string]: [string, string] [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 { export interface PokeballCounts {
[pb: string]: integer; [pb: string]: integer;
} }
@ -103,6 +83,8 @@ export type AnySound = Phaser.Sound.WebAudioSound | Phaser.Sound.HTML5AudioSound
export default class BattleScene extends SceneBase { export default class BattleScene extends SceneBase {
public rexUI: UIPlugin; public rexUI: UIPlugin;
public inputController: InputsController;
public uiInputs: UiInputs;
public sessionPlayTime: integer = null; public sessionPlayTime: integer = null;
public masterVolume: number = 0.5; public masterVolume: number = 0.5;
@ -123,7 +105,8 @@ export default class BattleScene extends SceneBase {
public gamepadSupport: boolean = true; public gamepadSupport: boolean = true;
public enableTouchControls: boolean = false; public enableTouchControls: boolean = false;
public enableVibration: boolean = false; public enableVibration: boolean = false;
public abSwapped: boolean = false;
public disableMenu: boolean = false; public disableMenu: boolean = false;
public gameData: GameData; public gameData: GameData;
@ -191,34 +174,6 @@ export default class BattleScene extends SceneBase {
private bgmResumeTimer: Phaser.Time.TimerEvent; private bgmResumeTimer: Phaser.Time.TimerEvent;
private bgmCache: Set<string> = new Set(); private bgmCache: Set<string> = new Set();
private playTimeTimer: Phaser.Time.TimerEvent; 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 rngCounter: integer = 0;
public rngSeedOverride: string = ''; public rngSeedOverride: string = '';
@ -263,7 +218,7 @@ export default class BattleScene extends SceneBase {
return ret; return ret;
}; };
} }
populateAnims(); populateAnims();
await this.initVariantData(); await this.initVariantData();
@ -271,13 +226,13 @@ export default class BattleScene extends SceneBase {
create() { create() {
initGameSpeed.apply(this); initGameSpeed.apply(this);
this.inputController = new InputsController(this);
this.uiInputs = new UiInputs(this, this.inputController);
this.gameData = new GameData(this); this.gameData = new GameData(this);
addUiThemeOverrides(this); addUiThemeOverrides(this);
this.setupControls();
this.load.setBaseURL(); this.load.setBaseURL();
this.spritePipeline = new SpritePipeline(this.game); this.spritePipeline = new SpritePipeline(this.game);
@ -290,7 +245,7 @@ export default class BattleScene extends SceneBase {
} }
update() { update() {
this.checkInput(); this.inputController.update();
this.ui?.update(); this.ui?.update();
} }
@ -609,42 +564,6 @@ export default class BattleScene extends SceneBase {
return true; 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[] { getParty(): PlayerPokemon[] {
return this.party; return this.party;
} }
@ -1116,7 +1035,7 @@ export default class BattleScene extends SceneBase {
return 5; return 5;
let isBoss: boolean; let isBoss: boolean;
if (forceBoss || (species && (species.pseudoLegendary || species.legendary || species.mythical))) if (forceBoss || (species && (species.subLegendary || species.legendary || species.mythical)))
isBoss = true; isBoss = true;
else { else {
this.executeWithSeedOffset(() => { this.executeWithSeedOffset(() => {
@ -1348,177 +1267,6 @@ export default class BattleScene extends SceneBase {
return biomes[Utils.randSeedInt(biomes.length)]; 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 { isBgmPlaying(): boolean {
return this.bgm && this.bgm.isPlaying; return this.bgm && this.bgm.isPlaying;
} }
@ -1676,11 +1424,13 @@ export default class BattleScene extends SceneBase {
return 13.122; return 13.122;
case 'battle_unova_gym': case 'battle_unova_gym':
return 19.145; 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; return 13.855;
case 'battle_legendary_k': case 'battle_legendary_kyurem': //BW Kyurem Battle
return 18.314; return 18.314;
case 'battle_legendary_rz': case 'battle_legendary_res_zek': //BW Reshiram & Zekrom Battle
return 18.329; return 18.329;
case 'battle_rival': case 'battle_rival':
return 13.689; return 13.689;

View File

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

View File

@ -340,7 +340,7 @@ export async function printPokemon() {
const speciesKey = (pokemonSpecies as any).key as string; const speciesKey = (pokemonSpecies as any).key as string;
enumStr += ` ${speciesKey}${pokemonSpecies.speciesId >= 2000 ? ` = ${pokemonSpecies.speciesId}` : ''},\n`; 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) { if (pokemonSpecies.forms.length > 1) {
pokemonSpeciesStr += `, ${pokemonSpecies.canChangeForm},`; pokemonSpeciesStr += `, ${pokemonSpecies.canChangeForm},`;
for (let form of pokemonSpecies.forms) for (let form of pokemonSpecies.forms)

View File

@ -12,7 +12,7 @@ import * as Utils from "../utils";
import { WeatherType } from "./weather"; import { WeatherType } from "./weather";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type"; 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 { Abilities } from "./enums/abilities";
import { allAbilities } from './ability'; import { allAbilities } from './ability';
import { PokemonHeldItemModifier } from "../modifier/modifier"; import { PokemonHeldItemModifier } from "../modifier/modifier";
@ -326,6 +326,15 @@ export default class Move implements Localizable {
return true; 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 { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
let score = 0; let score = 0;
@ -422,6 +431,10 @@ export abstract class MoveAttr {
return null; return null;
} }
getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
return null;
}
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
return 0; return 0;
} }
@ -434,7 +447,9 @@ export abstract class MoveAttr {
export enum MoveEffectTrigger { export enum MoveEffectTrigger {
PRE_APPLY, PRE_APPLY,
POST_APPLY, POST_APPLY,
HIT HIT,
/** Triggers one time after all target effects have applied */
POST_TARGET,
} }
export class MoveEffectAttr extends MoveAttr { 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 { export class HalfSacrificialAttr extends MoveEffectAttr {
constructor() { 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 { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) if (!super.apply(user, target, move, args))
return false; return false;
const cancelled = new Utils.BooleanHolder(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); applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
if (!cancelled.value){ if (!cancelled.value){
user.damageAndUpdate(Math.ceil(user.getMaxHp()/2), HitResult.OTHER, false, true, true); 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; return true;
} }
@ -1452,8 +1481,8 @@ export class StatChangeAttr extends MoveEffectAttr {
private condition: MoveConditionFunc; private condition: MoveConditionFunc;
private showMessage: boolean; private showMessage: boolean;
constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) { constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT) {
super(selfTarget, MoveEffectTrigger.HIT, firstHitOnly); super(selfTarget, moveEffectTrigger, firstHitOnly);
this.stats = typeof(stats) === 'number' this.stats = typeof(stats) === 'number'
? [ stats as BattleStat ] ? [ stats as BattleStat ]
: stats as BattleStat[]; : stats as BattleStat[];
@ -2692,26 +2721,19 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
export class CurseAttr extends MoveEffectAttr { export class CurseAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean { 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 (user.getTypes(true).includes(Type.GHOST)) {
if (target.getTag(BattlerTagType.CURSED)) { if (target.getTag(BattlerTagType.CURSED)) {
user.scene.queueMessage('But it failed!'); user.scene.queueMessage('But it failed!');
return false; 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.damageAndUpdate(curseRecoilDamage, HitResult.OTHER, false, true, true);
user.scene.queueMessage(getPokemonMessage(user, ` cut its own HP\nand laid a curse on the ${target.name}!`)); 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); target.addTag(BattlerTagType.CURSED, 0, move.id, user.id);
return true; return true;
} else { } else {
target = user; user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF], 1));
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(), true, [BattleStat.SPD], -1));
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), this.selfTarget, [BattleStat.SPD], -1));
return true; return true;
} }
} }
@ -2876,7 +2898,7 @@ export class AddArenaTagAttr extends MoveEffectAttr {
public tagType: ArenaTagType; public tagType: ArenaTagType;
public turnCount: integer; public turnCount: integer;
private failOnOverlap: boolean; private failOnOverlap: boolean;
private selfSideTarget: boolean; public selfSideTarget: boolean;
constructor(tagType: ArenaTagType, turnCount?: integer, failOnOverlap: boolean = false, selfSideTarget: boolean = false) { constructor(tagType: ArenaTagType, turnCount?: integer, failOnOverlap: boolean = false, selfSideTarget: boolean = false) {
super(true, MoveEffectTrigger.POST_APPLY, true); super(true, MoveEffectTrigger.POST_APPLY, true);
@ -2909,9 +2931,10 @@ export class AddArenaTagAttr extends MoveEffectAttr {
export class AddArenaTrapTagAttr extends AddArenaTagAttr { export class AddArenaTrapTagAttr extends AddArenaTagAttr {
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user, target, move) => { 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; 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; return tag.layers < tag.maxLayers;
}; };
} }
@ -2963,16 +2986,14 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
return new Promise(resolve => { 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 // 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 //Apply effects before switch out i.e. poison point, flame body, etc
applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, new PokemonMove(move.id), null); applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, new PokemonMove(move.id), null);
return resolve(false); return resolve(false);
} }
// Move the switch out logic inside the conditional block // Move the switch out logic inside the conditional block
// This ensures that the switch out only happens when the conditions are met // This ensures that the switch out only happens when the conditions are met
const switchOutTarget = this.user ? user : target; const switchOutTarget = this.user ? user : target;
@ -3019,15 +3040,24 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
resolve(true); resolve(true);
}); });
} }
getCondition(): MoveConditionFunc { 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 { getSwitchOutCondition(): MoveConditionFunc {
return (user, target, move) => { return (user, target, move) => {
const switchOutTarget = (this.user ? user : target); const switchOutTarget = (this.user ? user : target);
const player = switchOutTarget instanceof PlayerPokemon; 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 (!player && !user.scene.currentBattle.battleType) {
if (this.batonPass) if (this.batonPass)
@ -3791,6 +3821,9 @@ const failOnMaxCondition: MoveConditionFunc = (user, target, move) => !target.is
const failIfDampCondition: MoveConditionFunc = (user, target, move) => { const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
user.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled)); 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; return !cancelled.value;
} }
@ -4384,10 +4417,8 @@ export function initMoves() {
.condition((user, target, move) => user.status?.effect === StatusEffect.SLEEP) .condition((user, target, move) => user.status?.effect === StatusEffect.SLEEP)
.soundBased(), .soundBased(),
new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2) new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2)
.attr(StatChangeAttr, BattleStat.SPD, -1, true) .attr(CurseAttr)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], 1, true) .ignoresProtect(true),
.target(MoveTarget.USER)
.partial(),
new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2)
.attr(LowHpPowerAttr), .attr(LowHpPowerAttr),
new StatusMove(Moves.CONVERSION_2, Type.NORMAL, -1, 30, -1, 0, 2) new StatusMove(Moves.CONVERSION_2, Type.NORMAL, -1, 30, -1, 0, 2)
@ -5479,7 +5510,7 @@ export function initMoves() {
.soundBased() .soundBased()
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new StatusMove(Moves.PARTING_SHOT, Type.DARK, 100, 20, 100, 0, 6) 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) .attr(ForceSwitchOutAttr, true, false)
.soundBased(), .soundBased(),
new StatusMove(Moves.TOPSY_TURVY, Type.DARK, -1, 20, -1, 0, 6) 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) new AttackMove(Moves.PIKA_PAPOW, Type.ELECTRIC, MoveCategory.SPECIAL, -1, -1, 20, -1, 0, 7)
.attr(FriendshipPowerAttr), .attr(FriendshipPowerAttr),
new AttackMove(Moves.BOUNCY_BUBBLE, Type.WATER, MoveCategory.SPECIAL, 60, 100, 20, -1, 0, 7) new AttackMove(Moves.BOUNCY_BUBBLE, Type.WATER, MoveCategory.SPECIAL, 60, 100, 20, -1, 0, 7)
.attr(HitHealAttr) .attr(HitHealAttr, 1.0)
.triageMove(), .triageMove(),
new AttackMove(Moves.BUZZY_BUZZ, Type.ELECTRIC, MoveCategory.SPECIAL, 60, 100, 20, 100, 0, 7) new AttackMove(Moves.BUZZY_BUZZ, Type.ELECTRIC, MoveCategory.SPECIAL, 60, 100, 20, 100, 0, 7)
.attr(StatusEffectAttr, StatusEffect.PARALYSIS), .attr(StatusEffectAttr, StatusEffect.PARALYSIS),
@ -6231,7 +6262,7 @@ export function initMoves() {
.partial(), .partial(),
new SelfStatusMove(Moves.TAKE_HEART, Type.PSYCHIC, -1, 10, -1, 0, 8) new SelfStatusMove(Moves.TAKE_HEART, Type.PSYCHIC, -1, 10, -1, 0, 8)
.attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF ], 1, true) .attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF ], 1, true)
.partial(), .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN, StatusEffect.SLEEP),
/* Unused /* Unused
new AttackMove(Moves.G_MAX_WILDFIRE, Type.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8) new AttackMove(Moves.G_MAX_WILDFIRE, Type.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8)
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)

View File

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

View File

@ -212,7 +212,7 @@ export class TrainerConfig {
this.battleBgm = 'battle_trainer'; this.battleBgm = 'battle_trainer';
this.victoryBgm = 'victory_trainer'; this.victoryBgm = 'victory_trainer';
this.partyTemplates = [ trainerPartyTemplates.TWO_AVG ]; 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 { 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 { function getSpeciesFilterRandomPartyMemberFunc(speciesFilter: PokemonSpeciesFilter, trainerSlot: TrainerSlot = TrainerSlot.TRAINER, allowLegendaries?: boolean, postProcess?: (EnemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
const originalSpeciesFilter = speciesFilter; 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) => { 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); const ret = scene.addEnemyPokemon(getPokemonSpecies(scene.randomSpecies(scene.currentBattle.waveIndex, level, false, speciesFilter).getTrainerSpeciesForLevel(level, true, strength)), level, trainerSlot, undefined, undefined, postProcess);
return ret; return ret;

View File

@ -241,10 +241,12 @@ export class EggHatchPhase extends Phase {
doReveal(): void { doReveal(): void {
const isShiny = this.pokemon.isShiny(); const isShiny = this.pokemon.isShiny();
if (this.pokemon.species.mythical) if (this.pokemon.species.subLegendary)
this.scene.validateAchv(achvs.HATCH_MYTHICAL); this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
if (this.pokemon.species.legendary) if (this.pokemon.species.legendary)
this.scene.validateAchv(achvs.HATCH_LEGENDARY); this.scene.validateAchv(achvs.HATCH_LEGENDARY);
if (this.pokemon.species.mythical)
this.scene.validateAchv(achvs.HATCH_MYTHICAL);
if (isShiny) if (isShiny)
this.scene.validateAchv(achvs.HATCH_SHINY); this.scene.validateAchv(achvs.HATCH_SHINY);
this.eggContainer.setVisible(false); 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); ret = getPokemonSpecies(species);
if (ret.pseudoLegendary || ret.legendary || ret.mythical) { if (ret.subLegendary || ret.legendary || ret.mythical) {
switch (true) { switch (true) {
case (ret.baseTotal >= 720): case (ret.baseTotal >= 720):
regen = level < 90; 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 { 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) { if (existingTag) {
existingTag.onOverlap(this); existingTag.onOverlap(this);
return false; return false;

View File

@ -1913,6 +1913,21 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, effect, statusCureTurn); applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, effect, statusCureTurn);
this.setFrameRate(4); 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); this.status = new Status(effect, 0, statusCureTurn?.value);

View File

@ -133,7 +133,7 @@ export class GameMode implements GameModeConfig {
getOverrideSpecies(waveIndex: integer): PokemonSpecies { getOverrideSpecies(waveIndex: integer): PokemonSpecies {
if (this.isDaily && this.isWaveFinal(waveIndex)) { 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); && s.baseTotal >= 600 && s.speciesId !== Species.ETERNATUS && s.speciesId !== Species.ARCEUS);
return Utils.randSeedItem(allFinalBossSpecies); 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.", "expGain": "{{pokemonName}} ha ganado\n{{exp}} puntos de experiencia.",
"levelUp": "¡{{pokemonName}} ha subido al \nNv. {{level}}!", "levelUp": "¡{{pokemonName}} ha subido al \nNv. {{level}}!",
"learnMove": "¡{{pokemonName}} ha aprendido {{moveName}}!", "learnMove": "¡{{pokemonName}} ha aprendido {{moveName}}!",
"learnMovePrompt": "{{pokemonName}} quiere aprender {{moveName}}.", "learnMovePrompt": "{{pokemonName}} quiere aprender\n{{moveName}}.",
"learnMoveLimitReached": "Pero, {{pokemonName}} ya conoce\ncuatro movimientos.", "learnMoveLimitReached": "Pero, {{pokemonName}} ya conoce\ncuatro movimientos.",
"learnMoveReplaceQuestion": "¿Quieres sustituir uno de sus movimientos por {{moveName}}?", "learnMoveReplaceQuestion": "¿Quieres sustituir uno de sus movimientos por {{moveName}}?",
"learnMoveStopTeaching": "¿Prefieres que no aprenda\n{{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!", "expGain": "{{pokemonName}} ha guadagnato\n{{exp}} Punti Esperienza!",
"levelUp": "{{pokemonName}} è salito al \nLivello {{level}}!", "levelUp": "{{pokemonName}} è salito al \nLivello {{level}}!",
"learnMove": "{{pokemonName}} impara \n{{moveName}}!", "learnMove": "{{pokemonName}} impara \n{{moveName}}!",
"learnMovePrompt": "{{pokemonName}} vorrebbe imparare \l{{moveName}}.", "learnMovePrompt": "{{pokemonName}} vorrebbe imparare\n{{moveName}}.",
"learnMoveLimitReached": "Tuttavia, {{pokemonName}} \nconosce già quattro mosse.", "learnMoveLimitReached": "Tuttavia, {{pokemonName}} \nconosce già quattro mosse.",
"learnMoveReplaceQuestion": "Vuoi che ne dimentichi una e al suo \nposto la sostituisca con {{moveName}}?", "learnMoveReplaceQuestion": "Vuoi che ne dimentichi una e al suo \nposto la sostituisca con {{moveName}}?",
"learnMoveStopTeaching": "Vuoi smettere di fargli imparare \n{{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", "iron_crown": "Capoferreo",
"terapagos": "Terapagos", "terapagos": "Terapagos",
"pecharunt": "Pecharunt", "pecharunt": "Pecharunt",
"alola_rattata": "Rattata di Alola", "alola_rattata": "Rattata",
"alola_raticate": "Raticate di Alola", "alola_raticate": "Raticate",
"alola_raichu": "Raichu di Alola", "alola_raichu": "Raichu",
"alola_sandshrew": "Sandshrew di Alola", "alola_sandshrew": "Sandshrew",
"alola_sandslash": "Sandslash di Alola", "alola_sandslash": "Sandslash",
"alola_vulpix": "Vulpix di Alola", "alola_vulpix": "Vulpix",
"alola_ninetales": "Ninetales di Alola", "alola_ninetales": "Ninetales",
"alola_diglett": "Diglett di Alola", "alola_diglett": "Diglett",
"alola_dugtrio": "Dugtrio di Alola", "alola_dugtrio": "Dugtrio",
"alola_meowth": "Meowth di Alola", "alola_meowth": "Meowth",
"alola_persian": "Persian di Alola", "alola_persian": "Persian",
"alola_geodude": "Geodude di Alola", "alola_geodude": "Geodude",
"alola_graveler": "Graveler di Alola", "alola_graveler": "Graveler",
"alola_golem": "Golem di Alola", "alola_golem": "Golem",
"alola_grimer": "Grimer di Alola", "alola_grimer": "Grimer",
"alola_muk": "Muk di Alola", "alola_muk": "Muk",
"alola_exeggutor": "Exeggutor di Alola", "alola_exeggutor": "Exeggutor",
"alola_marowak": "Marowak di Alola", "alola_marowak": "Marowak",
"eternal_floette": "Floette", "eternal_floette": "Floette",
"galar_meowth": "Meowth di Galar", "galar_meowth": "Meowth",
"galar_ponyta": "Ponyta di Galar", "galar_ponyta": "Ponyta",
"galar_rapidash": "Rapidash di Galar", "galar_rapidash": "Rapidash",
"galar_slowpoke": "Slowpoke di Galar", "galar_slowpoke": "Slowpoke",
"galar_slowbro": "Slowbro di Galar", "galar_slowbro": "Slowbro",
"galar_farfetchd": "Farfetch'd di Galar", "galar_farfetchd": "Farfetch'd",
"galar_weezing": "Weezing di Galar", "galar_weezing": "Weezing",
"galar_mr_mime": "Mr. Mime di Galar", "galar_mr_mime": "Mr. Mime",
"galar_articuno": "Articuno di Galar", "galar_articuno": "Articuno",
"galar_zapdos": "Zapdos di Galar", "galar_zapdos": "Zapdos",
"galar_moltres": "Moltres di Galar", "galar_moltres": "Moltres",
"galar_slowking": "Slowking di Galar", "galar_slowking": "Slowking",
"galar_corsola": "Corsola di Galar", "galar_corsola": "Corsola",
"galar_zigzagoon": "Zigzagoon di Galar", "galar_zigzagoon": "Zigzagoon",
"galar_linoone": "Linoone di Galar", "galar_linoone": "Linoone",
"galar_darumaka": "Darumaka di Galar", "galar_darumaka": "Darumaka",
"galar_darmanitan": "Darmanitan di Galar", "galar_darmanitan": "Darmanitan",
"galar_yamask": "Yamask di Galar", "galar_yamask": "Yamask",
"galar_stunfisk": "Stunfisk di Galar", "galar_stunfisk": "Stunfisk",
"hisui_growlithe": "Growlithe di Hisui", "hisui_growlithe": "Growlithe",
"hisui_arcanine": "Arcanine di Hisui", "hisui_arcanine": "Arcanine",
"hisui_voltorb": "Voltorb di Hisui", "hisui_voltorb": "Voltorb",
"hisui_electrode": "Electrode di Hisui", "hisui_electrode": "Electrode",
"hisui_typhlosion": "Typhlosion di Hisui", "hisui_typhlosion": "Typhlosion",
"hisui_qwilfish": "Qwilfish di Hisui", "hisui_qwilfish": "Qwilfish",
"hisui_sneasel": "Sneasel di Hisui", "hisui_sneasel": "Sneasel",
"hisui_samurott": "Samurott di Hisui", "hisui_samurott": "Samurott",
"hisui_lilligant": "Lilligant di Hisui", "hisui_lilligant": "Lilligant",
"hisui_zorua": "Zorua di Hisui", "hisui_zorua": "Zorua",
"hisui_zoroark": "Zoroark di Hisui", "hisui_zoroark": "Zoroark",
"hisui_braviary": "Braviary di Hisui", "hisui_braviary": "Braviary",
"hisui_sliggoo": "Sliggoo di Hisui", "hisui_sliggoo": "Sliggoo",
"hisui_goodra": "Goodra di Hisui", "hisui_goodra": "Goodra",
"hisui_avalugg": "Avalugg di Hisui", "hisui_avalugg": "Avalugg",
"hisui_decidueye": "Decidueye di Hisui", "hisui_decidueye": "Decidueye",
"paldea_tauros": "Tauros di Paldea", "paldea_tauros": "Tauros",
"paldea_wooper": "Wooper di Paldea", "paldea_wooper": "Wooper",
"bloodmoon_ursaluna": "Ursaluna Luna Cremisi", "bloodmoon_ursaluna": "Ursaluna",
} as const; } 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 // 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 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())) if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove()))
success = false; success = false;
else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) { else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) {
success = false; 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) if (success)
this.scene.unshiftPhase(this.getEffectPhase()); this.scene.unshiftPhase(this.getEffectPhase());
else { else {
this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual }); 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(); 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()); Promise.allSettled(applyAttrs).then(() => this.end());
}); });
}); });
@ -2869,7 +2875,7 @@ export class StatChangePhase extends PokemonPhase {
if (relLevelStats.length > 1) { if (relLevelStats.length > 1) {
statsFragment = relLevelStats.length >= 5 statsFragment = relLevelStats.length >= 5
? 'stats' ? '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 } else
statsFragment = getBattleStatName(relLevelStats[0]); statsFragment = getBattleStatName(relLevelStats[0]);
messages.push(getPokemonMessage(this.getPokemon(), `'s ${statsFragment} ${getBattleStatLevelChangeDescription(Math.abs(parseInt(rl)), levels >= 1)}!`)); 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) if (speciesForm.abilityHidden && (pokemon.fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex) === speciesForm.getAbilityCount() - 1)
this.scene.validateAchv(achvs.HIDDEN_ABILITY); 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); this.scene.validateAchv(achvs.CATCH_LEGENDARY);
if (pokemon.species.mythical) 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), 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(), 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_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), 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), 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_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(), 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), 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), 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; 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; this.gameStats = systemData.gameStats;
}
if (systemData.unlocks) { if (systemData.unlocks) {
for (let key of Object.keys(systemData.unlocks)) { for (let key of Object.keys(systemData.unlocks)) {
@ -1011,7 +1014,9 @@ export class GameData {
if (incrementCount) { if (incrementCount) {
dexEntry.seenCount++; dexEntry.seenCount++;
this.gameStats.pokemonSeen++; 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++; this.gameStats.legendaryPokemonSeen++;
else if (!trainer && pokemon.species.mythical) else if (!trainer && pokemon.species.mythical)
this.gameStats.mythicalPokemonSeen++; this.gameStats.mythicalPokemonSeen++;
@ -1049,7 +1054,9 @@ export class GameData {
if (!fromEgg) { if (!fromEgg) {
dexEntry.caughtCount++; dexEntry.caughtCount++;
this.gameStats.pokemonCaught++; 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++; this.gameStats.legendaryPokemonCaught++;
else if (pokemon.species.mythical) else if (pokemon.species.mythical)
this.gameStats.mythicalPokemonCaught++; this.gameStats.mythicalPokemonCaught++;
@ -1058,7 +1065,9 @@ export class GameData {
} else { } else {
dexEntry.hatchedCount++; dexEntry.hatchedCount++;
this.gameStats.pokemonHatched++; 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++; this.gameStats.legendaryPokemonHatched++;
else if (pokemon.species.mythical) else if (pokemon.species.mythical)
this.gameStats.mythicalPokemonHatched++; this.gameStats.mythicalPokemonHatched++;
@ -1317,4 +1326,22 @@ export class GameData {
for (let starterId of defaultStarterSpecies) for (let starterId of defaultStarterSpecies)
systemData.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1; 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 pokemonDefeated: integer;
public pokemonCaught: integer; public pokemonCaught: integer;
public pokemonHatched: integer; public pokemonHatched: integer;
public subLegendaryPokemonSeen: integer;
public subLegendaryPokemonCaught: integer;
public subLegendaryPokemonHatched: integer;
public legendaryPokemonSeen: integer; public legendaryPokemonSeen: integer;
public legendaryPokemonCaught: integer; public legendaryPokemonCaught: integer;
public legendaryPokemonHatched: integer; public legendaryPokemonHatched: integer;
@ -56,6 +59,10 @@ export class GameStats {
this.pokemonDefeated = source?.pokemonDefeated || 0; this.pokemonDefeated = source?.pokemonDefeated || 0;
this.pokemonCaught = source?.pokemonCaught || 0; this.pokemonCaught = source?.pokemonCaught || 0;
this.pokemonHatched = source?.pokemonHatched || 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.legendaryPokemonSeen = source?.legendaryPokemonSeen || 0;
this.legendaryPokemonCaught = source?.legendaryPokemonCaught || 0; this.legendaryPokemonCaught = source?.legendaryPokemonCaught || 0;
this.legendaryPokemonHatched = source?.legendaryPokemonHatched || 0; this.legendaryPokemonHatched = source?.legendaryPokemonHatched || 0;

View File

@ -25,6 +25,7 @@ export enum Setting {
Fusion_Palette_Swaps = "FUSION_PALETTE_SWAPS", Fusion_Palette_Swaps = "FUSION_PALETTE_SWAPS",
Player_Gender = "PLAYER_GENDER", Player_Gender = "PLAYER_GENDER",
Gamepad_Support = "GAMEPAD_SUPPORT", Gamepad_Support = "GAMEPAD_SUPPORT",
Swap_A_and_B = "SWAP_A_B", // Swaps which gamepad button handles ACTION and CANCEL
Touch_Controls = "TOUCH_CONTROLS", Touch_Controls = "TOUCH_CONTROLS",
Vibration = "VIBRATION" Vibration = "VIBRATION"
} }
@ -56,6 +57,7 @@ export const settingOptions: SettingOptions = {
[Setting.Fusion_Palette_Swaps]: [ 'Off', 'On' ], [Setting.Fusion_Palette_Swaps]: [ 'Off', 'On' ],
[Setting.Player_Gender]: [ 'Boy', 'Girl' ], [Setting.Player_Gender]: [ 'Boy', 'Girl' ],
[Setting.Gamepad_Support]: [ 'Auto', 'Disabled' ], [Setting.Gamepad_Support]: [ 'Auto', 'Disabled' ],
[Setting.Swap_A_and_B]: [ 'Enabled', 'Disabled' ],
[Setting.Touch_Controls]: [ 'Auto', 'Disabled' ], [Setting.Touch_Controls]: [ 'Auto', 'Disabled' ],
[Setting.Vibration]: [ 'Auto', 'Disabled' ] [Setting.Vibration]: [ 'Auto', 'Disabled' ]
}; };
@ -79,6 +81,7 @@ export const settingDefaults: SettingDefaults = {
[Setting.Fusion_Palette_Swaps]: 1, [Setting.Fusion_Palette_Swaps]: 1,
[Setting.Player_Gender]: 0, [Setting.Player_Gender]: 0,
[Setting.Gamepad_Support]: 0, [Setting.Gamepad_Support]: 0,
[Setting.Swap_A_and_B]: 1, // Set to 'Disabled' by default
[Setting.Touch_Controls]: 0, [Setting.Touch_Controls]: 0,
[Setting.Vibration]: 0 [Setting.Vibration]: 0
}; };
@ -148,6 +151,9 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer)
case Setting.Gamepad_Support: case Setting.Gamepad_Support:
scene.gamepadSupport = settingOptions[setting][value] !== 'Disabled'; scene.gamepadSupport = settingOptions[setting][value] !== 'Disabled';
break; break;
case Setting.Swap_A_and_B:
scene.abSwapped = settingOptions[setting][value] !== 'Disabled';
break;
case Setting.Touch_Controls: case Setting.Touch_Controls:
scene.enableTouchControls = settingOptions[setting][value] !== 'Disabled' && hasTouchscreen(); scene.enableTouchControls = settingOptions[setting][value] !== 'Disabled' && hasTouchscreen();
const touchControls = document.getElementById('touchControls'); 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 { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { argbFromRgba } from "@material/material-color-utilities"; import { argbFromRgba } from "@material/material-color-utilities";
import {Button} from "../enums/buttons";
export interface OptionSelectConfig { export interface OptionSelectConfig {
xOffset?: number; 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 { Achv, achvs } from "../system/achv";
import MessageUiHandler from "./message-ui-handler"; import MessageUiHandler from "./message-ui-handler";
import { TextStyle, addTextObject } from "./text"; import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export default class AchvsUiHandler extends MessageUiHandler { export default class AchvsUiHandler extends MessageUiHandler {
private achvsContainer: Phaser.GameObjects.Container; 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 { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import {Button} from "../enums/buttons";
export default abstract class AwaitableUiHandler extends UiHandler { export default abstract class AwaitableUiHandler extends UiHandler {
protected awaitingActionInput: boolean; protected awaitingActionInput: boolean;

View File

@ -1,11 +1,12 @@
import { CommandPhase } from "../phases"; import { CommandPhase } from "../phases";
import BattleScene, { Button } from "../battle-scene"; import BattleScene from "../battle-scene";
import { getPokeballName } from "../data/pokeball"; import { getPokeballName } from "../data/pokeball";
import { addTextObject, TextStyle } from "./text"; import { addTextObject, TextStyle } from "./text";
import { Command } from "./command-ui-handler"; import { Command } from "./command-ui-handler";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export default class BallUiHandler extends UiHandler { export default class BallUiHandler extends UiHandler {
private pokeballSelectContainer: Phaser.GameObjects.Container; 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 { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import * as Utils from "../utils"; import * as Utils from "../utils";
@ -6,6 +6,7 @@ import MessageUiHandler from "./message-ui-handler";
import { getStatName, Stat } from "../data/pokemon-stat"; import { getStatName, Stat } from "../data/pokemon-stat";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
import {Button} from "../enums/buttons";
export default class BattleMessageUiHandler extends MessageUiHandler { export default class BattleMessageUiHandler extends MessageUiHandler {
private levelUpStatsContainer: Phaser.GameObjects.Container; private levelUpStatsContainer: Phaser.GameObjects.Container;

View File

@ -1,10 +1,11 @@
import { CommandPhase } from "../phases"; import { CommandPhase } from "../phases";
import BattleScene, { Button } from "../battle-scene"; import BattleScene from "../battle-scene";
import { addTextObject, TextStyle } from "./text"; import { addTextObject, TextStyle } from "./text";
import PartyUiHandler, { PartyUiMode } from "./party-ui-handler"; import PartyUiHandler, { PartyUiMode } from "./party-ui-handler";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import i18next from '../plugins/i18n'; import i18next from '../plugins/i18n';
import {Button} from "../enums/buttons";
export enum Command { export enum Command {
FIGHT = 0, 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 AbstractOptionSelectUiHandler, { OptionSelectConfig } from "./abstact-option-select-ui-handler";
import { Mode } from "./ui"; import { Mode } from "./ui";
import i18next from "i18next"; import i18next from "i18next";
import {Button} from "../enums/buttons";
export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
private switchCheck: boolean; 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 { Mode } from "./ui";
import { TextStyle, addTextObject, getEggTierTextTint } from "./text"; import { TextStyle, addTextObject, getEggTierTextTint } from "./text";
import MessageUiHandler from "./message-ui-handler"; import MessageUiHandler from "./message-ui-handler";
@ -9,6 +9,7 @@ import { getPokemonSpecies } from "../data/pokemon-species";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import { Tutorial, handleTutorial } from "../tutorial"; import { Tutorial, handleTutorial } from "../tutorial";
import { EggTier } from "../data/enums/egg-type"; import { EggTier } from "../data/enums/egg-type";
import {Button} from "../enums/buttons";
const defaultText = 'Select a machine.'; 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 { EggHatchPhase } from "../egg-hatch-phase";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import {Button} from "../enums/buttons";
export default class EggHatchSceneHandler extends UiHandler { export default class EggHatchSceneHandler extends UiHandler {
public eggHatchContainer: Phaser.GameObjects.Container; 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 { Mode } from "./ui";
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
import { TextStyle, addTextObject } from "./text"; 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 { EGG_SEED, Egg, GachaType, getEggGachaTypeDescriptor, getEggHatchWavesMessage, getEggDescriptor } from "../data/egg";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export default class EggListUiHandler extends MessageUiHandler { export default class EggListUiHandler extends MessageUiHandler {
private eggListContainer: Phaser.GameObjects.Container; 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 MessageUiHandler from "./message-ui-handler";
import { TextStyle, addTextObject } from "./text"; import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import {Button} from "../enums/buttons";
export default class EvolutionSceneHandler extends MessageUiHandler { export default class EvolutionSceneHandler extends MessageUiHandler {
public evolutionContainer: Phaser.GameObjects.Container; 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 { addTextObject, TextStyle } from "./text";
import { Type } from "../data/type"; import { Type } from "../data/type";
import { Command } from "./command-ui-handler"; import { Command } from "./command-ui-handler";
@ -8,6 +8,7 @@ import * as Utils from "../utils";
import { CommandPhase } from "../phases"; import { CommandPhase } from "../phases";
import { MoveCategory } from "#app/data/move.js"; import { MoveCategory } from "#app/data/move.js";
import i18next from '../plugins/i18n'; import i18next from '../plugins/i18n';
import {Button} from "../enums/buttons";
export default class FightUiHandler extends UiHandler { export default class FightUiHandler extends UiHandler {
private movesContainer: Phaser.GameObjects.Container; 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 { ModalConfig, ModalUiHandler } from "./modal-ui-handler";
import { Mode } from "./ui"; import { Mode } from "./ui";
import { TextStyle, addTextInputObject, addTextObject } from "./text"; 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 InputText from "phaser3-rex-plugins/plugins/inputtext";
import * as Utils from "../utils"; import * as Utils from "../utils";
import i18next from '../plugins/i18n'; import i18next from '../plugins/i18n';
import {Button} from "../enums/buttons";
export interface FormModalConfig extends ModalConfig { export interface FormModalConfig extends ModalConfig {
errorMessage?: string; 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 { TextStyle, addTextObject, getTextColor } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
@ -6,6 +6,7 @@ import { addWindow } from "./ui-theme";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { DexAttr, GameData } from "../system/game-data"; import { DexAttr, GameData } from "../system/game-data";
import { speciesStarters } from "../data/pokemon-species"; import { speciesStarters } from "../data/pokemon-species";
import {Button} from "../enums/buttons";
interface DisplayStat { interface DisplayStat {
label?: string; label?: string;
@ -65,13 +66,16 @@ const displayStats: DisplayStats = {
pokemonDefeated: 'Pokémon Defeated', pokemonDefeated: 'Pokémon Defeated',
pokemonCaught: 'Pokémon Caught', pokemonCaught: 'Pokémon Caught',
pokemonHatched: 'Eggs Hatched', pokemonHatched: 'Eggs Hatched',
legendaryPokemonSeen: 'Legendary Encounters?', subLegendaryPokemonSeen: 'Sub-Legends Seen?',
legendaryPokemonCaught: 'Legendaries Caught?', subLegendaryPokemonCaught: 'Sub-Legends Caught?',
legendaryPokemonHatched: 'Legendaries Hatched?', subLegendaryPokemonHatched: 'Sub-Legends Hatched?',
mythicalPokemonSeen: 'Mythical Encounters?', legendaryPokemonSeen: 'Legends Seen?',
legendaryPokemonCaught: 'Legends Caught?',
legendaryPokemonHatched: 'Legends Hatched?',
mythicalPokemonSeen: 'Mythicals Seen?',
mythicalPokemonCaught: 'Mythicals Caught?', mythicalPokemonCaught: 'Mythicals Caught?',
mythicalPokemonHatched: 'Mythicals Hatched?', mythicalPokemonHatched: 'Mythicals Hatched?',
shinyPokemonSeen: 'Shiny Encounters?', shinyPokemonSeen: 'Shinies Seen?',
shinyPokemonCaught: 'Shinies Caught?', shinyPokemonCaught: 'Shinies Caught?',
shinyPokemonHatched: 'Shinies Hatched?', shinyPokemonHatched: 'Shinies Hatched?',
pokemonFused: 'Pokémon Fused?', 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 { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import * as Utils from "../utils"; import * as Utils from "../utils";
@ -9,6 +9,7 @@ import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui
import { Tutorial, handleTutorial } from "../tutorial"; import { Tutorial, handleTutorial } from "../tutorial";
import { updateUserInfo } from "../account"; import { updateUserInfo } from "../account";
import i18next from '../plugins/i18n'; import i18next from '../plugins/i18n';
import {Button} from "../enums/buttons";
export enum MenuOptions { export enum MenuOptions {
GAME_SETTINGS, 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 { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import { WindowVariant, addWindow } from "./ui-theme"; import { WindowVariant, addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export interface ModalConfig { export interface ModalConfig {
buttonActions: Function[]; 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 { getPlayerShopModifierTypeOptionsForWave, ModifierTypeOption } from "../modifier/modifier-type";
import { getPokeballAtlasKey, PokeballType } from "../data/pokeball"; import { getPokeballAtlasKey, PokeballType } from "../data/pokeball";
import { addTextObject, getModifierTierTextTint, getTextColor, TextStyle } from "./text"; import { addTextObject, getModifierTierTextTint, getTextColor, TextStyle } from "./text";
@ -6,6 +6,7 @@ import AwaitableUiHandler from "./awaitable-ui-handler";
import { Mode } from "./ui"; import { Mode } from "./ui";
import { LockModifierTiersModifier, PokemonHeldItemModifier } from "../modifier/modifier"; import { LockModifierTiersModifier, PokemonHeldItemModifier } from "../modifier/modifier";
import { handleTutorial, Tutorial } from "../tutorial"; import { handleTutorial, Tutorial } from "../tutorial";
import {Button} from "../enums/buttons";
export const SHOP_OPTIONS_ROW_LIMIT = 6; export const SHOP_OPTIONS_ROW_LIMIT = 6;

View File

@ -1,5 +1,5 @@
import { CommandPhase } from "../phases"; import { CommandPhase } from "../phases";
import BattleScene, { Button } from "../battle-scene"; import BattleScene from "../battle-scene";
import { PlayerPokemon, PokemonMove } from "../field/pokemon"; import { PlayerPokemon, PokemonMove } from "../field/pokemon";
import { addTextObject, TextStyle } from "./text"; import { addTextObject, TextStyle } from "./text";
import { Command } from "./command-ui-handler"; import { Command } from "./command-ui-handler";
@ -16,6 +16,7 @@ import { pokemonEvolutions } from "../data/pokemon-evolutions";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms"; import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms";
import { getVariantTint } from "#app/data/variant"; import { getVariantTint } from "#app/data/variant";
import {Button} from "../enums/buttons";
const defaultMessage = 'Choose a Pokémon.'; 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 { gameModes } from "../game-mode";
import { SessionSaveData } from "../system/game-data"; import { SessionSaveData } from "../system/game-data";
import { TextStyle, addTextObject } from "./text"; import { TextStyle, addTextObject } from "./text";
@ -9,6 +9,7 @@ import PokemonData from "../system/pokemon-data";
import { PokemonHeldItemModifier } from "../modifier/modifier"; import { PokemonHeldItemModifier } from "../modifier/modifier";
import MessageUiHandler from "./message-ui-handler"; import MessageUiHandler from "./message-ui-handler";
import i18next from "i18next"; import i18next from "i18next";
import {Button} from "../enums/buttons";
const sessionSlotCount = 5; 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 { Setting, reloadSettings, settingDefaults, settingOptions } from "../system/settings";
import { hasTouchscreen, isMobile } from "../touch-controls"; import { hasTouchscreen, isMobile } from "../touch-controls";
import { TextStyle, addTextObject } from "./text"; import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
export default class SettingsUiHandler extends UiHandler { export default class SettingsUiHandler extends UiHandler {
private settingsContainer: Phaser.GameObjects.Container; private settingsContainer: Phaser.GameObjects.Container;

View File

@ -1,5 +1,5 @@
import BattleScene, { Button, starterColors } from "../battle-scene"; import BattleScene, { starterColors } from "../battle-scene";
import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters, starterPassiveAbilities, getStarterValueFriendshipCap } from "../data/pokemon-species";
import { Species } from "../data/enums/species"; import { Species } from "../data/enums/species";
import { TextStyle, addBBCodeTextObject, addTextObject } from "./text"; import { TextStyle, addBBCodeTextObject, addTextObject } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
@ -28,6 +28,7 @@ import { OptionSelectItem } from "./abstact-option-select-ui-handler";
import { pokemonPrevolutions } from "#app/data/pokemon-evolutions"; import { pokemonPrevolutions } from "#app/data/pokemon-evolutions";
import { Variant, getVariantTint } from "#app/data/variant"; import { Variant, getVariantTint } from "#app/data/variant";
import i18next from "i18next"; import i18next from "i18next";
import {Button} from "../enums/buttons";
export type StarterSelectCallback = (starters: Starter[]) => void; export type StarterSelectCallback = (starters: Starter[]) => void;
@ -118,6 +119,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[]; private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[];
private pokemonEggMoveLabels: Phaser.GameObjects.Text[]; private pokemonEggMoveLabels: Phaser.GameObjects.Text[];
private pokemonCandyIcon: Phaser.GameObjects.Sprite; private pokemonCandyIcon: Phaser.GameObjects.Sprite;
private pokemonCandyDarknessOverlay: Phaser.GameObjects.Sprite;
private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite; private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite;
private pokemonCandyCountText: Phaser.GameObjects.Text; private pokemonCandyCountText: Phaser.GameObjects.Text;
private pokemonCaughtHatchedContainer: Phaser.GameObjects.Container; private pokemonCaughtHatchedContainer: Phaser.GameObjects.Container;
@ -430,7 +432,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonLuckText.setOrigin(0, 0); this.pokemonLuckText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonLuckText); 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.setScale(0.5);
this.pokemonCandyIcon.setOrigin(0, 0); this.pokemonCandyIcon.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonCandyIcon); this.starterSelectContainer.add(this.pokemonCandyIcon);
@ -439,11 +441,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonFormText.setOrigin(0, 0); this.pokemonFormText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonFormText); 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.setScale(0.5);
this.pokemonCandyOverlayIcon.setOrigin(0, 0); this.pokemonCandyOverlayIcon.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonCandyOverlayIcon); 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 = addTextObject(this.scene, 14, 18, 'x0', TextStyle.WINDOW_ALT, { fontSize: '56px' });
this.pokemonCandyCountText.setOrigin(0, 0); this.pokemonCandyCountText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonCandyCountText); this.starterSelectContainer.add(this.pokemonCandyCountText);
@ -1284,16 +1294,32 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCaughtHatchedContainer.setVisible(true); this.pokemonCaughtHatchedContainer.setVisible(true);
if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) { if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) {
this.pokemonCaughtHatchedContainer.setY(16); 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 { } else {
this.pokemonCaughtHatchedContainer.setY(25); this.pokemonCaughtHatchedContainer.setY(25);
this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0])));
this.pokemonCandyIcon.setVisible(true); this.pokemonCandyIcon.setVisible(true);
this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1])));
this.pokemonCandyOverlayIcon.setVisible(true); this.pokemonCandyOverlayIcon.setVisible(true);
this.pokemonCandyDarknessOverlay.setVisible(true);
this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`); this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`);
this.pokemonCandyCountText.setVisible(true); this.pokemonCandyCountText.setVisible(true);
this.pokemonFormText.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); 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.pokemonCaughtHatchedContainer.setVisible(false);
this.pokemonCandyIcon.setVisible(false); this.pokemonCandyIcon.setVisible(false);
this.pokemonCandyOverlayIcon.setVisible(false); this.pokemonCandyOverlayIcon.setVisible(false);
this.pokemonCandyDarknessOverlay.setVisible(false);
this.pokemonCandyCountText.setVisible(false); this.pokemonCandyCountText.setVisible(false);
this.pokemonFormText.setVisible(false); this.pokemonFormText.setVisible(false);
@ -1369,6 +1396,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCaughtHatchedContainer.setVisible(false); this.pokemonCaughtHatchedContainer.setVisible(false);
this.pokemonCandyIcon.setVisible(false); this.pokemonCandyIcon.setVisible(false);
this.pokemonCandyOverlayIcon.setVisible(false); this.pokemonCandyOverlayIcon.setVisible(false);
this.pokemonCandyDarknessOverlay.setVisible(false);
this.pokemonCandyCountText.setVisible(false); this.pokemonCandyCountText.setVisible(false);
this.pokemonFormText.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 { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import * as Utils from "../utils"; import * as Utils from "../utils";
@ -19,6 +19,7 @@ import { Nature, getNatureStatMultiplier } from "../data/nature";
import { loggedInUser } from "../account"; import { loggedInUser } from "../account";
import { PlayerGender } from "../system/game-data"; import { PlayerGender } from "../system/game-data";
import { Variant, getVariantTint } from "#app/data/variant"; import { Variant, getVariantTint } from "#app/data/variant";
import {Button} from "../enums/buttons";
enum Page { enum Page {
PROFILE, PROFILE,

View File

@ -1,10 +1,11 @@
import { BattlerIndex } from "../battle"; import { BattlerIndex } from "../battle";
import BattleScene, { Button } from "../battle-scene"; import BattleScene from "../battle-scene";
import { Moves } from "../data/enums/moves"; import { Moves } from "../data/enums/moves";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { getMoveTargets } from "../data/move"; import { getMoveTargets } from "../data/move";
import {Button} from "../enums/buttons";
export type TargetSelectCallback = (cursor: integer) => void; 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 { TextStyle, getTextColor } from "./text";
import UI, { Mode } from "./ui"; import UI, { Mode } from "./ui";
import {Button} from "../enums/buttons";
export default abstract class UiHandler { export default abstract class UiHandler {
protected scene: BattleScene; 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 UiHandler from './ui-handler';
import BattleMessageUiHandler from './battle-message-ui-handler'; import BattleMessageUiHandler from './battle-message-ui-handler';
import CommandUiHandler from './command-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 UnavailableModalUiHandler from './unavailable-modal-ui-handler';
import OutdatedModalUiHandler from './outdated-modal-ui-handler'; import OutdatedModalUiHandler from './outdated-modal-ui-handler';
import SessionReloadModalUiHandler from './session-reload-modal-ui-handler'; import SessionReloadModalUiHandler from './session-reload-modal-ui-handler';
import {Button} from "../enums/buttons";
export enum Mode { export enum Mode {
MESSAGE, 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 { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "../system/voucher";
import MessageUiHandler from "./message-ui-handler"; import MessageUiHandler from "./message-ui-handler";
import { TextStyle, addTextObject } from "./text"; import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui"; import { Mode } from "./ui";
import { addWindow } from "./ui-theme"; import { addWindow } from "./ui-theme";
import {Button} from "../enums/buttons";
const itemRows = 4; const itemRows = 4;
const itemCols = 17; const itemCols = 17;

View File

@ -226,7 +226,7 @@ export const apiUrl = isLocal ? serverUrl : 'api';
export function setCookie(cName: string, cValue: string): void { export function setCookie(cName: string, cValue: string): void {
const expiration = new Date(); 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()}`; document.cookie = `${cName}=${cValue};SameSite=Strict;path=/;expires=${expiration.toUTCString()}`;
} }