Merge branch 'beta' into fusionevomovefix
@ -123,6 +123,7 @@ Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to
|
||||
- Involuntary-Twitch
|
||||
- selstar
|
||||
- koda_want_to_sleep
|
||||
- thedreadedden
|
||||
|
||||
### 🎨 Move Animations
|
||||
- Pokémon Reborn
|
||||
|
87
package-lock.json
generated
@ -16,6 +16,7 @@
|
||||
"i18next-http-backend": "^2.6.1",
|
||||
"i18next-korean-postposition-processor": "^1.0.0",
|
||||
"json-stable-stringify": "^1.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"phaser": "^3.70.0",
|
||||
"phaser3-rex-plugins": "^1.1.84"
|
||||
},
|
||||
@ -2723,6 +2724,11 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||
@ -4045,6 +4051,11 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@ -4072,6 +4083,11 @@
|
||||
"node": ">=0.8.19"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ini": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
|
||||
@ -4481,6 +4497,17 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
@ -4648,6 +4675,14 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
@ -5237,6 +5272,11 @@
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||
},
|
||||
"node_modules/papaparse": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
|
||||
@ -5485,6 +5525,11 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"node_modules/prompts": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
|
||||
@ -5551,6 +5596,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream/node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||
},
|
||||
"node_modules/rechoir": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
|
||||
@ -5741,6 +5805,11 @@
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/safe-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
|
||||
@ -5800,6 +5869,11 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@ -5917,6 +5991,14 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
@ -6473,6 +6555,11 @@
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.8",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
|
||||
|
@ -55,6 +55,7 @@
|
||||
"i18next-http-backend": "^2.6.1",
|
||||
"i18next-korean-postposition-processor": "^1.0.0",
|
||||
"json-stable-stringify": "^1.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"phaser": "^3.70.0",
|
||||
"phaser3-rex-plugins": "^1.1.84"
|
||||
},
|
||||
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.3 KiB |
@ -1,776 +1,299 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "47.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 230,
|
||||
"h": 230
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "0009.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 12,
|
||||
"w": 56,
|
||||
"h": 49
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 56,
|
||||
"h": 49
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0010.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 12,
|
||||
"w": 56,
|
||||
"h": 49
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 56,
|
||||
"h": 49
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0027.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 12,
|
||||
"w": 62,
|
||||
"h": 51
|
||||
},
|
||||
"frame": {
|
||||
"x": 56,
|
||||
"y": 0,
|
||||
"w": 62,
|
||||
"h": 51
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0028.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 12,
|
||||
"w": 62,
|
||||
"h": 51
|
||||
},
|
||||
"frame": {
|
||||
"x": 56,
|
||||
"y": 0,
|
||||
"w": 62,
|
||||
"h": 51
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0007.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 7,
|
||||
"y": 8,
|
||||
"w": 55,
|
||||
"h": 53
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 0,
|
||||
"w": 55,
|
||||
"h": 53
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0008.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 7,
|
||||
"y": 8,
|
||||
"w": 55,
|
||||
"h": 53
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 0,
|
||||
"w": 55,
|
||||
"h": 53
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0011.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 7,
|
||||
"y": 7,
|
||||
"w": 55,
|
||||
"h": 54
|
||||
},
|
||||
"frame": {
|
||||
"x": 173,
|
||||
"y": 0,
|
||||
"w": 55,
|
||||
"h": 54
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0012.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 7,
|
||||
"y": 7,
|
||||
"w": 55,
|
||||
"h": 54
|
||||
},
|
||||
"frame": {
|
||||
"x": 173,
|
||||
"y": 0,
|
||||
"w": 55,
|
||||
"h": 54
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0005.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 5,
|
||||
"w": 53,
|
||||
"h": 56
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 49,
|
||||
"w": 53,
|
||||
"h": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0006.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 5,
|
||||
"w": 53,
|
||||
"h": 56
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 49,
|
||||
"w": 53,
|
||||
"h": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0025.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"w": 62,
|
||||
"h": 55
|
||||
},
|
||||
"frame": {
|
||||
"x": 53,
|
||||
"y": 51,
|
||||
"w": 62,
|
||||
"h": 55
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0026.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"w": 62,
|
||||
"h": 55
|
||||
},
|
||||
"frame": {
|
||||
"x": 53,
|
||||
"y": 51,
|
||||
"w": 62,
|
||||
"h": 55
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0013.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 4,
|
||||
"w": 53,
|
||||
"h": 57
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 53,
|
||||
"w": 53,
|
||||
"h": 57
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0014.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 4,
|
||||
"w": 53,
|
||||
"h": 57
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 53,
|
||||
"w": 53,
|
||||
"h": 57
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0029.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 7,
|
||||
"w": 62,
|
||||
"h": 56
|
||||
},
|
||||
"frame": {
|
||||
"x": 168,
|
||||
"y": 54,
|
||||
"w": 62,
|
||||
"h": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0030.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 7,
|
||||
"w": 62,
|
||||
"h": 56
|
||||
},
|
||||
"frame": {
|
||||
"x": 168,
|
||||
"y": 54,
|
||||
"w": 62,
|
||||
"h": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0023.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 5,
|
||||
"w": 61,
|
||||
"h": 58
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 106,
|
||||
"w": 61,
|
||||
"h": 58
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0024.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 5,
|
||||
"w": 61,
|
||||
"h": 58
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 106,
|
||||
"w": 61,
|
||||
"h": 58
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0003.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 2,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 106,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0004.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 2,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 106,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
}
|
||||
},
|
||||
{ "frames": [
|
||||
{
|
||||
"filename": "0001.png",
|
||||
"frame": { "x": 0, "y": 58, "w": 55, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 110,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 5, "w": 55, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0002.png",
|
||||
"frame": { "x": 0, "y": 58, "w": 55, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 110,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 5, "w": 55, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0019.png",
|
||||
"filename": "0003.png",
|
||||
"frame": { "x": 166, "y": 114, "w": 52, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 110,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 9, "y": 5, "w": 52, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0020.png",
|
||||
"filename": "0004.png",
|
||||
"frame": { "x": 166, "y": 114, "w": 52, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
"spriteSourceSize": { "x": 9, "y": 5, "w": 52, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
{
|
||||
"filename": "0005.png",
|
||||
"frame": { "x": 0, "y": 169, "w": 51, "h": 54 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 11, "y": 7, "w": 51, "h": 54 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 110,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
}
|
||||
{
|
||||
"filename": "0006.png",
|
||||
"frame": { "x": 0, "y": 169, "w": 51, "h": 54 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 11, "y": 7, "w": 51, "h": 54 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0007.png",
|
||||
"frame": { "x": 104, "y": 166, "w": 53, "h": 52 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 10, "y": 9, "w": 53, "h": 52 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0008.png",
|
||||
"frame": { "x": 104, "y": 166, "w": 53, "h": 52 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 10, "y": 9, "w": 53, "h": 52 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0009.png",
|
||||
"frame": { "x": 157, "y": 170, "w": 55, "h": 49 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 9, "y": 12, "w": 55, "h": 49 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0010.png",
|
||||
"frame": { "x": 157, "y": 170, "w": 55, "h": 49 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 9, "y": 12, "w": 55, "h": 49 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0011.png",
|
||||
"frame": { "x": 0, "y": 115, "w": 53, "h": 54 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 10, "y": 8, "w": 53, "h": 54 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0012.png",
|
||||
"frame": { "x": 0, "y": 115, "w": 53, "h": 54 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 10, "y": 8, "w": 53, "h": 54 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0013.png",
|
||||
"frame": { "x": 53, "y": 116, "w": 51, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 11, "y": 6, "w": 51, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0014.png",
|
||||
"frame": { "x": 53, "y": 116, "w": 51, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 11, "y": 6, "w": 51, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0015.png",
|
||||
"frame": { "x": 114, "y": 109, "w": 52, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 2,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 172,
|
||||
"y": 110,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 9, "y": 5, "w": 52, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0016.png",
|
||||
"frame": { "x": 114, "y": 109, "w": 52, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 2,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 172,
|
||||
"y": 110,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0031.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"w": 61,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 164,
|
||||
"w": 61,
|
||||
"h": 59
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0032.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"w": 61,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 164,
|
||||
"w": 61,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 9, "y": 5, "w": 52, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0017.png",
|
||||
"frame": { "x": 59, "y": 57, "w": 55, "h": 59 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 169,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 3, "w": 55, "h": 59 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0018.png",
|
||||
"frame": { "x": 59, "y": 57, "w": 55, "h": 59 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 169,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 3, "w": 55, "h": 59 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0035.png",
|
||||
"filename": "0019.png",
|
||||
"frame": { "x": 0, "y": 58, "w": 55, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 169,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 5, "w": 55, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0036.png",
|
||||
"filename": "0020.png",
|
||||
"frame": { "x": 0, "y": 58, "w": 55, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 169,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 5, "w": 55, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0021.png",
|
||||
"frame": { "x": 178, "y": 56, "w": 57, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 169,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 4, "y": 5, "w": 57, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0022.png",
|
||||
"frame": { "x": 178, "y": 56, "w": 57, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
"spriteSourceSize": { "x": 4, "y": 5, "w": 57, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
{
|
||||
"filename": "0023.png",
|
||||
"frame": { "x": 119, "y": 0, "w": 59, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 7, "w": 59, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 169,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
}
|
||||
{
|
||||
"filename": "0024.png",
|
||||
"frame": { "x": 119, "y": 0, "w": 59, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 7, "w": 59, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0025.png",
|
||||
"frame": { "x": 178, "y": 0, "w": 60, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 9, "w": 60, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0026.png",
|
||||
"frame": { "x": 178, "y": 0, "w": 60, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 9, "w": 60, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0027.png",
|
||||
"frame": { "x": 114, "y": 57, "w": 62, "h": 52 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 2, "y": 12, "w": 62, "h": 52 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0028.png",
|
||||
"frame": { "x": 114, "y": 57, "w": 62, "h": 52 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 2, "y": 12, "w": 62, "h": 52 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0029.png",
|
||||
"frame": { "x": 59, "y": 0, "w": 60, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 8, "w": 60, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0030.png",
|
||||
"frame": { "x": 59, "y": 0, "w": 60, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 8, "w": 60, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0031.png",
|
||||
"frame": { "x": 0, "y": 0, "w": 59, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 6, "w": 59, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0032.png",
|
||||
"frame": { "x": 0, "y": 0, "w": 59, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 6, "w": 59, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0033.png",
|
||||
"frame": { "x": 178, "y": 56, "w": 57, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 169,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 4, "y": 5, "w": 57, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0034.png",
|
||||
"frame": { "x": 178, "y": 56, "w": 57, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
"spriteSourceSize": { "x": 4, "y": 5, "w": 57, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
{
|
||||
"filename": "0035.png",
|
||||
"frame": { "x": 59, "y": 57, "w": 55, "h": 59 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 6, "y": 3, "w": 55, "h": 59 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 169,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
}
|
||||
}
|
||||
]
|
||||
{
|
||||
"filename": "0036.png",
|
||||
"frame": { "x": 59, "y": 57, "w": 55, "h": 59 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 6, "y": 3, "w": 55, "h": 59 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:b28fe643197bcc1def0e0ac2ba9f4e67:516d08c8e1ff13b49a109b082ef12860:fe45e2d628a6cef0908f7b82468c8798$"
|
||||
"app": "https://www.aseprite.org/",
|
||||
"version": "1.3.8.1-x64",
|
||||
"image": "47.png",
|
||||
"format": "I8",
|
||||
"size": { "w": 238, "h": 223 },
|
||||
"scale": "1"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.3 KiB |
@ -1,776 +1,299 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "47.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 230,
|
||||
"h": 230
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "0009.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 12,
|
||||
"w": 56,
|
||||
"h": 49
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 56,
|
||||
"h": 49
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0010.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 12,
|
||||
"w": 56,
|
||||
"h": 49
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 56,
|
||||
"h": 49
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0027.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 12,
|
||||
"w": 62,
|
||||
"h": 51
|
||||
},
|
||||
"frame": {
|
||||
"x": 56,
|
||||
"y": 0,
|
||||
"w": 62,
|
||||
"h": 51
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0028.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 12,
|
||||
"w": 62,
|
||||
"h": 51
|
||||
},
|
||||
"frame": {
|
||||
"x": 56,
|
||||
"y": 0,
|
||||
"w": 62,
|
||||
"h": 51
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0007.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 7,
|
||||
"y": 8,
|
||||
"w": 55,
|
||||
"h": 53
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 0,
|
||||
"w": 55,
|
||||
"h": 53
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0008.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 7,
|
||||
"y": 8,
|
||||
"w": 55,
|
||||
"h": 53
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 0,
|
||||
"w": 55,
|
||||
"h": 53
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0011.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 7,
|
||||
"y": 7,
|
||||
"w": 55,
|
||||
"h": 54
|
||||
},
|
||||
"frame": {
|
||||
"x": 173,
|
||||
"y": 0,
|
||||
"w": 55,
|
||||
"h": 54
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0012.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 7,
|
||||
"y": 7,
|
||||
"w": 55,
|
||||
"h": 54
|
||||
},
|
||||
"frame": {
|
||||
"x": 173,
|
||||
"y": 0,
|
||||
"w": 55,
|
||||
"h": 54
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0005.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 5,
|
||||
"w": 53,
|
||||
"h": 56
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 49,
|
||||
"w": 53,
|
||||
"h": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0006.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 5,
|
||||
"w": 53,
|
||||
"h": 56
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 49,
|
||||
"w": 53,
|
||||
"h": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0025.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"w": 62,
|
||||
"h": 55
|
||||
},
|
||||
"frame": {
|
||||
"x": 53,
|
||||
"y": 51,
|
||||
"w": 62,
|
||||
"h": 55
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0026.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"w": 62,
|
||||
"h": 55
|
||||
},
|
||||
"frame": {
|
||||
"x": 53,
|
||||
"y": 51,
|
||||
"w": 62,
|
||||
"h": 55
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0013.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 4,
|
||||
"w": 53,
|
||||
"h": 57
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 53,
|
||||
"w": 53,
|
||||
"h": 57
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0014.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 8,
|
||||
"y": 4,
|
||||
"w": 53,
|
||||
"h": 57
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 53,
|
||||
"w": 53,
|
||||
"h": 57
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0029.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 7,
|
||||
"w": 62,
|
||||
"h": 56
|
||||
},
|
||||
"frame": {
|
||||
"x": 168,
|
||||
"y": 54,
|
||||
"w": 62,
|
||||
"h": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0030.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 7,
|
||||
"w": 62,
|
||||
"h": 56
|
||||
},
|
||||
"frame": {
|
||||
"x": 168,
|
||||
"y": 54,
|
||||
"w": 62,
|
||||
"h": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0023.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 5,
|
||||
"w": 61,
|
||||
"h": 58
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 106,
|
||||
"w": 61,
|
||||
"h": 58
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0024.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 5,
|
||||
"w": 61,
|
||||
"h": 58
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 106,
|
||||
"w": 61,
|
||||
"h": 58
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0003.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 2,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 106,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0004.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 2,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 106,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
}
|
||||
},
|
||||
{ "frames": [
|
||||
{
|
||||
"filename": "0001.png",
|
||||
"frame": { "x": 0, "y": 58, "w": 55, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 110,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 5, "w": 55, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0002.png",
|
||||
"frame": { "x": 0, "y": 58, "w": 55, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 110,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 5, "w": 55, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0019.png",
|
||||
"filename": "0003.png",
|
||||
"frame": { "x": 166, "y": 114, "w": 52, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 110,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 9, "y": 5, "w": 52, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0020.png",
|
||||
"filename": "0004.png",
|
||||
"frame": { "x": 166, "y": 114, "w": 52, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
"spriteSourceSize": { "x": 9, "y": 5, "w": 52, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
{
|
||||
"filename": "0005.png",
|
||||
"frame": { "x": 0, "y": 169, "w": 51, "h": 54 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 11, "y": 7, "w": 51, "h": 54 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"frame": {
|
||||
"x": 115,
|
||||
"y": 110,
|
||||
"w": 57,
|
||||
"h": 59
|
||||
}
|
||||
{
|
||||
"filename": "0006.png",
|
||||
"frame": { "x": 0, "y": 169, "w": 51, "h": 54 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 11, "y": 7, "w": 51, "h": 54 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0007.png",
|
||||
"frame": { "x": 104, "y": 166, "w": 53, "h": 52 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 10, "y": 9, "w": 53, "h": 52 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0008.png",
|
||||
"frame": { "x": 104, "y": 166, "w": 53, "h": 52 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 10, "y": 9, "w": 53, "h": 52 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0009.png",
|
||||
"frame": { "x": 157, "y": 170, "w": 55, "h": 49 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 9, "y": 12, "w": 55, "h": 49 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0010.png",
|
||||
"frame": { "x": 157, "y": 170, "w": 55, "h": 49 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 9, "y": 12, "w": 55, "h": 49 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0011.png",
|
||||
"frame": { "x": 0, "y": 115, "w": 53, "h": 54 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 10, "y": 8, "w": 53, "h": 54 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0012.png",
|
||||
"frame": { "x": 0, "y": 115, "w": 53, "h": 54 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 10, "y": 8, "w": 53, "h": 54 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0013.png",
|
||||
"frame": { "x": 53, "y": 116, "w": 51, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 11, "y": 6, "w": 51, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0014.png",
|
||||
"frame": { "x": 53, "y": 116, "w": 51, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 11, "y": 6, "w": 51, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0015.png",
|
||||
"frame": { "x": 114, "y": 109, "w": 52, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 2,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 172,
|
||||
"y": 110,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 9, "y": 5, "w": 52, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0016.png",
|
||||
"frame": { "x": 114, "y": 109, "w": 52, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 6,
|
||||
"y": 2,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 172,
|
||||
"y": 110,
|
||||
"w": 54,
|
||||
"h": 59
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0031.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"w": 61,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 164,
|
||||
"w": 61,
|
||||
"h": 59
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "0032.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"w": 61,
|
||||
"h": 59
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 164,
|
||||
"w": 61,
|
||||
"h": 59
|
||||
}
|
||||
"spriteSourceSize": { "x": 9, "y": 5, "w": 52, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0017.png",
|
||||
"frame": { "x": 59, "y": 57, "w": 55, "h": 59 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 169,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 3, "w": 55, "h": 59 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0018.png",
|
||||
"frame": { "x": 59, "y": 57, "w": 55, "h": 59 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 169,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 3, "w": 55, "h": 59 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0035.png",
|
||||
"filename": "0019.png",
|
||||
"frame": { "x": 0, "y": 58, "w": 55, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 169,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 5, "w": 55, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0036.png",
|
||||
"filename": "0020.png",
|
||||
"frame": { "x": 0, "y": 58, "w": 55, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 61,
|
||||
"y": 169,
|
||||
"w": 57,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 6, "y": 5, "w": 55, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0021.png",
|
||||
"frame": { "x": 178, "y": 56, "w": 57, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 169,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 4, "y": 5, "w": 57, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0022.png",
|
||||
"frame": { "x": 178, "y": 56, "w": 57, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
"spriteSourceSize": { "x": 4, "y": 5, "w": 57, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
{
|
||||
"filename": "0023.png",
|
||||
"frame": { "x": 119, "y": 0, "w": 59, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 7, "w": 59, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 169,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
}
|
||||
{
|
||||
"filename": "0024.png",
|
||||
"frame": { "x": 119, "y": 0, "w": 59, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 7, "w": 59, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0025.png",
|
||||
"frame": { "x": 178, "y": 0, "w": 60, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 9, "w": 60, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0026.png",
|
||||
"frame": { "x": 178, "y": 0, "w": 60, "h": 56 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 9, "w": 60, "h": 56 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0027.png",
|
||||
"frame": { "x": 114, "y": 57, "w": 62, "h": 52 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 2, "y": 12, "w": 62, "h": 52 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0028.png",
|
||||
"frame": { "x": 114, "y": 57, "w": 62, "h": 52 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 2, "y": 12, "w": 62, "h": 52 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0029.png",
|
||||
"frame": { "x": 59, "y": 0, "w": 60, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 8, "w": 60, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0030.png",
|
||||
"frame": { "x": 59, "y": 0, "w": 60, "h": 57 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 8, "w": 60, "h": 57 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0031.png",
|
||||
"frame": { "x": 0, "y": 0, "w": 59, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 6, "w": 59, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0032.png",
|
||||
"frame": { "x": 0, "y": 0, "w": 59, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 3, "y": 6, "w": 59, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0033.png",
|
||||
"frame": { "x": 178, "y": 56, "w": 57, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 169,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
}
|
||||
"spriteSourceSize": { "x": 4, "y": 5, "w": 57, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
{
|
||||
"filename": "0034.png",
|
||||
"frame": { "x": 178, "y": 56, "w": 57, "h": 58 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 62,
|
||||
"h": 63
|
||||
"spriteSourceSize": { "x": 4, "y": 5, "w": 57, "h": 58 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
{
|
||||
"filename": "0035.png",
|
||||
"frame": { "x": 59, "y": 57, "w": 55, "h": 59 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 6, "y": 3, "w": 55, "h": 59 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
},
|
||||
"frame": {
|
||||
"x": 118,
|
||||
"y": 169,
|
||||
"w": 60,
|
||||
"h": 61
|
||||
}
|
||||
}
|
||||
]
|
||||
{
|
||||
"filename": "0036.png",
|
||||
"frame": { "x": 59, "y": 57, "w": 55, "h": 59 },
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": { "x": 6, "y": 3, "w": 55, "h": 59 },
|
||||
"sourceSize": { "w": 65, "h": 65 }
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:38ba9918eb8f68ab2190b03c6512ef47:46578d6dd482a1b04fa7c2884107a0f5:fe45e2d628a6cef0908f7b82468c8798$"
|
||||
"app": "https://www.aseprite.org/",
|
||||
"version": "1.3.8.1-x64",
|
||||
"image": "47.png",
|
||||
"format": "I8",
|
||||
"size": { "w": 238, "h": 223 },
|
||||
"scale": "1"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.2 KiB |
@ -3008,7 +3008,8 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
validateAchv(achv: Achv, args?: unknown[]): boolean {
|
||||
if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) {
|
||||
if ((!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE)
|
||||
&& achv.validate(this, args)) {
|
||||
this.gameData.achvUnlocks[achv.id] = new Date().getTime();
|
||||
this.ui.achvBar.showAchv(achv);
|
||||
if (vouchers.hasOwnProperty(achv.id)) {
|
||||
|
@ -4112,9 +4112,13 @@ export class PostBattleAbAttr extends AbAttr {
|
||||
}
|
||||
|
||||
export class PostBattleLootAbAttr extends PostBattleAbAttr {
|
||||
/**
|
||||
* @param args - `[0]`: boolean for if the battle ended in a victory
|
||||
* @returns `true` if successful
|
||||
*/
|
||||
applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||
const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot;
|
||||
if (!simulated && postBattleLoot.length) {
|
||||
if (!simulated && postBattleLoot.length && args[0]) {
|
||||
const randItem = Utils.randSeedItem(postBattleLoot);
|
||||
//@ts-ignore - TODO see below
|
||||
if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true, undefined, false)) { // TODO: fix. This is a promise!?
|
||||
@ -4575,28 +4579,28 @@ export class MoneyAbAttr extends PostBattleAbAttr {
|
||||
/**
|
||||
* @param pokemon {@linkcode Pokemon} that is the user of this ability.
|
||||
* @param passive N/A
|
||||
* @param args N/A
|
||||
* @returns true
|
||||
* @param args - `[0]`: boolean for if the battle ended in a victory
|
||||
* @returns `true` if successful
|
||||
*/
|
||||
applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||
if (!simulated) {
|
||||
if (!simulated && args[0]) {
|
||||
pokemon.scene.currentBattle.moneyScattered += pokemon.scene.getWaveMoneyAmount(0.2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a stat change after a Pokémon is summoned,
|
||||
* conditioned on the presence of a specific arena tag.
|
||||
*
|
||||
* @extends {PostSummonStatStageChangeAbAttr}
|
||||
* @extends PostSummonStatStageChangeAbAttr
|
||||
*/
|
||||
export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageChangeAbAttr {
|
||||
/**
|
||||
* The type of arena tag that conditions the stat change.
|
||||
* @private
|
||||
* @type {ArenaTagType}
|
||||
*/
|
||||
private tagType: ArenaTagType;
|
||||
|
||||
@ -4972,7 +4976,7 @@ class ForceSwitchOutHelper {
|
||||
pokemon.scene.clearEnemyHeldItemModifiers();
|
||||
|
||||
if (switchOutTarget.hp) {
|
||||
pokemon.scene.pushPhase(new BattleEndPhase(pokemon.scene));
|
||||
pokemon.scene.pushPhase(new BattleEndPhase(pokemon.scene, false));
|
||||
pokemon.scene.pushPhase(new NewBattlePhase(pokemon.scene));
|
||||
}
|
||||
}
|
||||
@ -5779,9 +5783,10 @@ export function initAbilities() {
|
||||
.attr(WonderSkinAbAttr)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.ANALYTIC, 5)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) =>
|
||||
!!target?.getLastXMoves(1).find(m => m.turn === target?.scene.currentBattle.turn)
|
||||
|| user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command !== Command.FIGHT, 1.3),
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => {
|
||||
const movePhase = user?.scene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.id !== user.id);
|
||||
return Utils.isNullOrUndefined(movePhase);
|
||||
}, 1.3),
|
||||
new Ability(Abilities.ILLUSION, 5)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
@ -5929,10 +5934,10 @@ export function initAbilities() {
|
||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1),
|
||||
new Ability(Abilities.WIMP_OUT, 7)
|
||||
.attr(PostDamageForceSwitchAbAttr)
|
||||
.edgeCase(), // Should not trigger when hurting itself in confusion
|
||||
.edgeCase(), // Should not trigger when hurting itself in confusion, causes Fake Out to fail turn 1 and succeed turn 2 if pokemon is switched out before battle start via playing in Switch Mode
|
||||
new Ability(Abilities.EMERGENCY_EXIT, 7)
|
||||
.attr(PostDamageForceSwitchAbAttr)
|
||||
.edgeCase(), // Should not trigger when hurting itself in confusion
|
||||
.edgeCase(), // Should not trigger when hurting itself in confusion, causes Fake Out to fail turn 1 and succeed turn 2 if pokemon is switched out before battle start via playing in Switch Mode
|
||||
new Ability(Abilities.WATER_COMPACTION, 7)
|
||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2),
|
||||
new Ability(Abilities.MERCILESS, 7)
|
||||
@ -6340,8 +6345,7 @@ export function initAbilities() {
|
||||
.attr(IgnoreOpponentStatStagesAbAttr, [ Stat.EVA ])
|
||||
.ignorable(),
|
||||
new Ability(Abilities.SUPERSWEET_SYRUP, 9)
|
||||
.attr(PostSummonStatStageChangeAbAttr, [ Stat.EVA ], -1)
|
||||
.condition(getOncePerBattleCondition(Abilities.SUPERSWEET_SYRUP)),
|
||||
.attr(PostSummonStatStageChangeAbAttr, [ Stat.EVA ], -1),
|
||||
new Ability(Abilities.HOSPITALITY, 9)
|
||||
.attr(PostSummonAllyHealAbAttr, 4, true),
|
||||
new Ability(Abilities.TOXIC_CHAIN, 9)
|
||||
|
46
src/data/balance/special-species-groups.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Species } from "#enums/species";
|
||||
|
||||
/**
|
||||
* A list of all {@link https://bulbapedia.bulbagarden.net/wiki/Paradox_Pok%C3%A9mon | Paradox Pokemon}, NOT including the legendaries Miraidon and Koraidon.
|
||||
*/
|
||||
export const NON_LEGEND_PARADOX_POKEMON = [
|
||||
Species.GREAT_TUSK,
|
||||
Species.SCREAM_TAIL,
|
||||
Species.BRUTE_BONNET,
|
||||
Species.FLUTTER_MANE,
|
||||
Species.SLITHER_WING,
|
||||
Species.SANDY_SHOCKS,
|
||||
Species.ROARING_MOON,
|
||||
Species.WALKING_WAKE,
|
||||
Species.GOUGING_FIRE,
|
||||
Species.RAGING_BOLT,
|
||||
Species.IRON_TREADS,
|
||||
Species.IRON_BUNDLE,
|
||||
Species.IRON_HANDS,
|
||||
Species.IRON_JUGULIS,
|
||||
Species.IRON_MOTH,
|
||||
Species.IRON_THORNS,
|
||||
Species.IRON_VALIANT,
|
||||
Species.IRON_LEAVES,
|
||||
Species.IRON_BOULDER,
|
||||
Species.IRON_CROWN,
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of all {@link https://bulbapedia.bulbagarden.net/wiki/Ultra_Beast | Ultra Beasts}, NOT including legendaries such as Necrozma or the Cosmog line.
|
||||
*
|
||||
* Note that all of these Ultra Beasts are still considered Sub-Legendary.
|
||||
*/
|
||||
export const NON_LEGEND_ULTRA_BEASTS = [
|
||||
Species.NIHILEGO,
|
||||
Species.BUZZWOLE,
|
||||
Species.PHEROMOSA,
|
||||
Species.XURKITREE,
|
||||
Species.CELESTEELA,
|
||||
Species.KARTANA,
|
||||
Species.GUZZLORD,
|
||||
Species.POIPOLE,
|
||||
Species.NAGANADEL,
|
||||
Species.STAKATAKA,
|
||||
Species.BLACEPHALON,
|
||||
];
|
147
src/data/move.ts
@ -1385,16 +1385,40 @@ export class UserHpDamageAttr extends FixedDamageAttr {
|
||||
}
|
||||
|
||||
export class TargetHalfHpDamageAttr extends FixedDamageAttr {
|
||||
// the initial amount of hp the target had before the first hit
|
||||
// used for multi lens
|
||||
private initialHp: number;
|
||||
constructor() {
|
||||
super(0);
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
(args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(target.hp / 2);
|
||||
|
||||
// first, determine if the hit is coming from multi lens or not
|
||||
const lensCount = user.getHeldItems().find(i => i instanceof PokemonMultiHitModifier)?.getStackCount() ?? 0;
|
||||
if (lensCount <= 0) {
|
||||
// no multi lenses; we can just halve the target's hp and call it a day
|
||||
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(target.hp / 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
// figure out what hit # we're on
|
||||
switch (user.turnData.hitCount - user.turnData.hitsLeft) {
|
||||
case 0:
|
||||
// first hit of move; update initialHp tracker
|
||||
this.initialHp = target.hp;
|
||||
default:
|
||||
// multi lens added hit; use initialHp tracker to ensure correct damage
|
||||
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(this.initialHp / 2);
|
||||
return true;
|
||||
break;
|
||||
case lensCount + 1:
|
||||
// parental bond added hit; calc damage as normal
|
||||
(args[0] as Utils.NumberHolder).value = Utils.toDmgValue(target.hp / 2);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
||||
return target.getHpRatio() > 0.5 ? Math.floor(((target.getHpRatio() - 0.5) * -24) + 4) : -20;
|
||||
}
|
||||
@ -5967,50 +5991,113 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
|
||||
* If it did, the user of U-turn or Volt Switch will not be switched out.
|
||||
*/
|
||||
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr) &&
|
||||
(move.id === Moves.U_TURN || move.id === Moves.VOLT_SWITCH || move.id === Moves.FLIP_TURN)
|
||||
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr)
|
||||
&& [ Moves.U_TURN, Moves.VOLT_SWITCH, Moves.FLIP_TURN ].includes(move.id)
|
||||
) {
|
||||
if (this.hpDroppedBelowHalf(target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Switch out logic for the player's Pokemon
|
||||
if (switchOutTarget.scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
||||
|
||||
// Find indices of off-field Pokemon that are eligible to be switched into
|
||||
const eligibleNewIndices: number[] = [];
|
||||
switchOutTarget.scene.getPlayerParty().forEach((pokemon, index) => {
|
||||
if (pokemon.isAllowedInBattle() && !pokemon.isOnField()) {
|
||||
eligibleNewIndices.push(index);
|
||||
}
|
||||
});
|
||||
|
||||
if (eligibleNewIndices.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (switchOutTarget.hp > 0) {
|
||||
if (this.switchType === SwitchType.FORCE_SWITCH) {
|
||||
switchOutTarget.leaveField(true);
|
||||
const slotIndex = eligibleNewIndices[user.randSeedInt(eligibleNewIndices.length)];
|
||||
user.scene.prependToPhase(
|
||||
new SwitchSummonPhase(
|
||||
user.scene,
|
||||
this.switchType,
|
||||
switchOutTarget.getFieldIndex(),
|
||||
slotIndex,
|
||||
false,
|
||||
true
|
||||
),
|
||||
MoveEndPhase
|
||||
);
|
||||
} else {
|
||||
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||
user.scene.prependToPhase(new SwitchPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase);
|
||||
user.scene.prependToPhase(
|
||||
new SwitchPhase(
|
||||
user.scene,
|
||||
this.switchType,
|
||||
switchOutTarget.getFieldIndex(),
|
||||
true,
|
||||
true
|
||||
),
|
||||
MoveEndPhase
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (user.scene.currentBattle.battleType !== BattleType.WILD) {
|
||||
// Switch out logic for trainer battles
|
||||
if (switchOutTarget.scene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
||||
} else if (user.scene.currentBattle.battleType !== BattleType.WILD) { // Switch out logic for enemy trainers
|
||||
// Find indices of off-field Pokemon that are eligible to be switched into
|
||||
const eligibleNewIndices: number[] = [];
|
||||
switchOutTarget.scene.getEnemyParty().forEach((pokemon, index) => {
|
||||
if (pokemon.isAllowedInBattle() && !pokemon.isOnField()) {
|
||||
eligibleNewIndices.push(index);
|
||||
}
|
||||
});
|
||||
|
||||
if (eligibleNewIndices.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (switchOutTarget.hp > 0) {
|
||||
// for opponent switching out
|
||||
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||
user.scene.prependToPhase(new SwitchSummonPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(),
|
||||
(user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0),
|
||||
false, false), MoveEndPhase);
|
||||
}
|
||||
if (this.switchType === SwitchType.FORCE_SWITCH) {
|
||||
switchOutTarget.leaveField(true);
|
||||
const slotIndex = eligibleNewIndices[user.randSeedInt(eligibleNewIndices.length)];
|
||||
user.scene.prependToPhase(
|
||||
new SwitchSummonPhase(
|
||||
user.scene,
|
||||
this.switchType,
|
||||
switchOutTarget.getFieldIndex(),
|
||||
slotIndex,
|
||||
false,
|
||||
false
|
||||
),
|
||||
MoveEndPhase
|
||||
);
|
||||
} else {
|
||||
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||
user.scene.prependToPhase(
|
||||
new SwitchSummonPhase(
|
||||
user.scene,
|
||||
this.switchType,
|
||||
switchOutTarget.getFieldIndex(),
|
||||
(user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0),
|
||||
false,
|
||||
false
|
||||
),
|
||||
MoveEndPhase
|
||||
);
|
||||
}
|
||||
}
|
||||
} else { // Switch out logic for wild pokemon
|
||||
/**
|
||||
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
|
||||
* If it did, the user of U-turn or Volt Switch will not be switched out.
|
||||
*/
|
||||
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr) &&
|
||||
(move.id === Moves.U_TURN || move.id === Moves.VOLT_SWITCH) || move.id === Moves.FLIP_TURN) {
|
||||
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr)
|
||||
&& [ Moves.U_TURN, Moves.VOLT_SWITCH, Moves.FLIP_TURN ].includes(move.id)
|
||||
) {
|
||||
if (this.hpDroppedBelowHalf(target)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch out logic for everything else (eg: WILD battles)
|
||||
if (user.scene.currentBattle.waveIndex % 10 === 0) {
|
||||
return false;
|
||||
}
|
||||
@ -6035,7 +6122,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
user.scene.clearEnemyHeldItemModifiers();
|
||||
|
||||
if (switchOutTarget.hp) {
|
||||
user.scene.pushPhase(new BattleEndPhase(user.scene));
|
||||
user.scene.pushPhase(new BattleEndPhase(user.scene, false));
|
||||
user.scene.pushPhase(new NewBattlePhase(user.scene));
|
||||
}
|
||||
}
|
||||
@ -7777,11 +7864,10 @@ export function initMoves() {
|
||||
.windMove(),
|
||||
new AttackMove(Moves.WING_ATTACK, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, -1, 0, 1),
|
||||
new StatusMove(Moves.WHIRLWIND, Type.NORMAL, -1, 20, -1, -6, 1)
|
||||
.attr(ForceSwitchOutAttr)
|
||||
.attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH)
|
||||
.ignoresSubstitute()
|
||||
.hidesTarget()
|
||||
.windMove()
|
||||
.partial(), // Should force random switches
|
||||
.windMove(),
|
||||
new ChargingAttackMove(Moves.FLY, Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, -1, 0, 1)
|
||||
.chargeText(i18next.t("moveTriggers:flewUpHigh", { pokemonName: "{USER}" }))
|
||||
.chargeAttr(SemiInvulnerableAttr, BattlerTagType.FLYING)
|
||||
@ -7857,10 +7943,9 @@ export function initMoves() {
|
||||
.soundBased()
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
new StatusMove(Moves.ROAR, Type.NORMAL, -1, 20, -1, -6, 1)
|
||||
.attr(ForceSwitchOutAttr)
|
||||
.attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH)
|
||||
.soundBased()
|
||||
.hidesTarget()
|
||||
.partial(), // Should force random switching
|
||||
.hidesTarget(),
|
||||
new StatusMove(Moves.SING, Type.NORMAL, 55, 15, -1, 0, 1)
|
||||
.attr(StatusEffectAttr, StatusEffect.SLEEP)
|
||||
.soundBased(),
|
||||
@ -9222,8 +9307,8 @@ export function initMoves() {
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK ], 1, true)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPD ], 2, true),
|
||||
new AttackMove(Moves.CIRCLE_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5)
|
||||
.attr(ForceSwitchOutAttr)
|
||||
.partial(), // Should force random switches
|
||||
.attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH)
|
||||
.hidesTarget(),
|
||||
new AttackMove(Moves.INCINERATE, Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
||||
.attr(RemoveHeldItemAttr, true),
|
||||
@ -9291,9 +9376,8 @@ export function initMoves() {
|
||||
new AttackMove(Moves.FROST_BREATH, Type.ICE, MoveCategory.SPECIAL, 60, 90, 10, 100, 0, 5)
|
||||
.attr(CritOnlyAttr),
|
||||
new AttackMove(Moves.DRAGON_TAIL, Type.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5)
|
||||
.attr(ForceSwitchOutAttr)
|
||||
.hidesTarget()
|
||||
.partial(), // Should force random switches
|
||||
.attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH)
|
||||
.hidesTarget(),
|
||||
new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true),
|
||||
new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5)
|
||||
@ -10554,6 +10638,7 @@ export function initMoves() {
|
||||
new AttackMove(Moves.TWIN_BEAM, Type.PSYCHIC, MoveCategory.SPECIAL, 40, 100, 10, -1, 0, 9)
|
||||
.attr(MultiHitAttr, MultiHitType._2),
|
||||
new AttackMove(Moves.RAGE_FIST, Type.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 9)
|
||||
.partial() // Counter resets every wave instead of on arena reset
|
||||
.attr(HitCountPowerAttr)
|
||||
.punchingMove(),
|
||||
new AttackMove(Moves.ARMOR_CANNON, Type.FIRE, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9)
|
||||
|
@ -8,7 +8,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils";
|
||||
import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getRandomPlayerPokemon, getRandomSpeciesByStarterCost } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||
@ -174,7 +174,7 @@ export const DarkDealEncounter: MysteryEncounter =
|
||||
const roll = randSeedInt(100);
|
||||
const starterTier: number | [number, number] =
|
||||
roll >= 65 ? 6 : roll >= 15 ? 7 : roll >= 5 ? 8 : [ 9, 10 ];
|
||||
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes));
|
||||
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterCost(starterTier, excludedBosses, bossTypes));
|
||||
const pokemonConfig: EnemyPokemonConfig = {
|
||||
species: bossSpecies,
|
||||
isBoss: true,
|
||||
|
@ -9,9 +9,9 @@ import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { IntegerHolder, randSeedInt } from "#app/utils";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterTier, trainerThrowPokeball } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterCost, trainerThrowPokeball } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
@ -19,6 +19,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
|
||||
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
||||
import { SummonPhase } from "#app/phases/summon-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounters/safariZone";
|
||||
@ -261,7 +262,7 @@ async function summonSafariPokemon(scene: BattleScene) {
|
||||
let enemySpecies;
|
||||
let pokemon;
|
||||
scene.executeWithSeedOffset(() => {
|
||||
enemySpecies = getPokemonSpecies(getRandomSpeciesByStarterTier([ 0, 5 ], undefined, undefined, false, false, false));
|
||||
enemySpecies = getSafariSpeciesSpawn();
|
||||
const level = scene.currentBattle.getLevelForWave();
|
||||
enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(level, true, false, scene.gameMode));
|
||||
pokemon = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, false);
|
||||
@ -526,3 +527,10 @@ async function doEndTurn(scene: BattleScene, cursorIndex: number) {
|
||||
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns A random species that has at most 5 starter cost and is not Mythical, Paradox, etc.
|
||||
*/
|
||||
export function getSafariSpeciesSpawn(): PokemonSpecies {
|
||||
return getPokemonSpecies(getRandomSpeciesByStarterCost([ 0, 5 ], NON_LEGEND_PARADOX_POKEMON, undefined, false, false, false));
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import { EggSourceType } from "#enums/egg-source-types";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { achvs } from "#app/system/achv";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { Type } from "#enums/type";
|
||||
import { getPokeballTintColor } from "#app/data/pokeball";
|
||||
@ -520,12 +519,6 @@ function removePokemonFromPartyAndStoreHeldItems(scene: BattleScene, encounter:
|
||||
];
|
||||
}
|
||||
|
||||
function checkAchievement(scene: BattleScene) {
|
||||
if (scene.arena.biomeType === Biome.SPACE) {
|
||||
scene.validateAchv(achvs.BREEDERS_IN_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
function restorePartyAndHeldItems(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
// Restore original party
|
||||
@ -617,8 +610,6 @@ function onGameOver(scene: BattleScene) {
|
||||
function doPostEncounterCleanup(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
if (!encounter.misc.encounterFailed) {
|
||||
// Give achievement if in Space biome
|
||||
checkAchievement(scene);
|
||||
// Give 20 friendship to the chosen pokemon
|
||||
encounter.misc.chosenPokemon.addFriendship(FRIENDSHIP_ADDED);
|
||||
restorePartyAndHeldItems(scene);
|
||||
|
@ -4,8 +4,8 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { catchPokemon, getRandomSpeciesByStarterTier, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { catchPokemon, getRandomSpeciesByStarterCost, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { Species } from "#enums/species";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
@ -17,6 +17,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups";
|
||||
|
||||
/** the i18n namespace for this encounter */
|
||||
const namespace = "mysteryEncounters/thePokemonSalesman";
|
||||
@ -60,12 +61,12 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
|
||||
let species = getPokemonSpecies(getRandomSpeciesByStarterTier([ 0, 5 ], undefined, undefined, false, false, false));
|
||||
let species = getSalesmanSpeciesOffer();
|
||||
let tries = 0;
|
||||
|
||||
// Reroll any species that don't have HAs
|
||||
while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && tries < 5) {
|
||||
species = getPokemonSpecies(getRandomSpeciesByStarterTier([ 0, 5 ], undefined, undefined, false, false, false));
|
||||
species = getSalesmanSpeciesOffer();
|
||||
tries++;
|
||||
}
|
||||
|
||||
@ -164,3 +165,10 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* @returns A random species that has at most 5 starter cost and is not Mythical, Paradox, etc.
|
||||
*/
|
||||
export function getSalesmanSpeciesOffer(): PokemonSpecies {
|
||||
return getPokemonSpecies(getRandomSpeciesByStarterCost([ 0, 5 ], NON_LEGEND_PARADOX_POKEMON, undefined, false, false, false));
|
||||
}
|
||||
|
@ -731,7 +731,7 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
|
||||
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
||||
scene.pushPhase(new EggLapsePhase(scene));
|
||||
} else if (!scene.getEnemyParty().find(p => encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true))) {
|
||||
scene.pushPhase(new BattleEndPhase(scene));
|
||||
scene.pushPhase(new BattleEndPhase(scene, true));
|
||||
if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
||||
scene.pushPhase(new TrainerVictoryPhase(scene));
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ export function getHighestStatTotalPlayerPokemon(scene: BattleScene, isAllowed:
|
||||
* @param allowMythical
|
||||
* @returns
|
||||
*/
|
||||
export function getRandomSpeciesByStarterTier(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[], allowSubLegendary: boolean = true, allowLegendary: boolean = true, allowMythical: boolean = true): Species {
|
||||
export function getRandomSpeciesByStarterCost(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[], allowSubLegendary: boolean = true, allowLegendary: boolean = true, allowMythical: boolean = true): Species {
|
||||
let min = Array.isArray(starterTiers) ? starterTiers[0] : starterTiers;
|
||||
let max = Array.isArray(starterTiers) ? starterTiers[1] : starterTiers;
|
||||
|
||||
|
@ -10,5 +10,7 @@ export enum SwitchType {
|
||||
/** Transfers stat stages and other effects from the returning Pokemon to the switched in Pokemon */
|
||||
BATON_PASS,
|
||||
/** Transfers the returning Pokemon's Substitute to the switched in Pokemon */
|
||||
SHED_TAIL
|
||||
SHED_TAIL,
|
||||
/** Force switchout to a random party member */
|
||||
FORCE_SWITCH,
|
||||
}
|
||||
|
@ -707,7 +707,7 @@ export class Arena {
|
||||
case Biome.METROPOLIS:
|
||||
return 141.470;
|
||||
case Biome.FOREST:
|
||||
return 4.294;
|
||||
return 0.341;
|
||||
case Biome.SEA:
|
||||
return 0.024;
|
||||
case Biome.SWAMP:
|
||||
|
@ -51,7 +51,7 @@ import { Biome } from "#enums/biome";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { FaintPhase } from "#app/phases/faint-phase";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
@ -2631,8 +2631,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
};
|
||||
}
|
||||
|
||||
// If the attack deals fixed damaged, return a result with that much damage
|
||||
const fixedDamage = new Utils.IntegerHolder(0);
|
||||
// If the attack deals fixed damage, return a result with that much damage
|
||||
const fixedDamage = new Utils.NumberHolder(0);
|
||||
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
|
||||
if (fixedDamage.value) {
|
||||
const multiLensMultiplier = new Utils.NumberHolder(1);
|
||||
@ -3018,7 +3018,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* @returns integer of damage done
|
||||
*/
|
||||
damageAndUpdate(damage: integer, result?: DamageResult, critical: boolean = false, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false, source?: Pokemon): integer {
|
||||
const damagePhase = new DamagePhase(this.scene, this.getBattlerIndex(), damage, result as DamageResult, critical);
|
||||
const damagePhase = new DamageAnimPhase(this.scene, this.getBattlerIndex(), damage, result as DamageResult, critical);
|
||||
this.scene.unshiftPhase(damagePhase);
|
||||
damage = this.damage(damage, ignoreSegments, preventEndure, ignoreFaintPhase);
|
||||
// Damage amount may have changed, but needed to be queued before calling damage function
|
||||
|
@ -86,6 +86,8 @@ class DefaultOverrides {
|
||||
readonly ITEM_UNLOCK_OVERRIDE: Unlockables[] = [];
|
||||
/** Set to `true` to show all tutorials */
|
||||
readonly BYPASS_TUTORIAL_SKIP_OVERRIDE: boolean = false;
|
||||
/** Set to `true` to be able to re-earn already unlocked achievements */
|
||||
readonly ACHIEVEMENTS_REUNLOCK_OVERRIDE: boolean = false;
|
||||
/** Set to `true` to force Paralysis and Freeze to always activate, or `false` to force them to not activate */
|
||||
readonly STATUS_ACTIVATION_OVERRIDE: boolean | null = null;
|
||||
|
||||
|
@ -52,7 +52,7 @@ export class AttemptRunPhase extends PokemonPhase {
|
||||
enemyPokemon.trySetStatus(StatusEffect.FAINT);
|
||||
});
|
||||
|
||||
this.scene.pushPhase(new BattleEndPhase(this.scene));
|
||||
this.scene.pushPhase(new BattleEndPhase(this.scene, false));
|
||||
this.scene.pushPhase(new NewBattlePhase(this.scene));
|
||||
} else {
|
||||
playerPokemon.turnData.failedRunAway = true;
|
||||
|
@ -8,7 +8,7 @@ export class BattleEndPhase extends BattlePhase {
|
||||
/** If true, will increment battles won */
|
||||
isVictory: boolean;
|
||||
|
||||
constructor(scene: BattleScene, isVictory: boolean = true) {
|
||||
constructor(scene: BattleScene, isVictory: boolean) {
|
||||
super(scene);
|
||||
|
||||
this.isVictory = isVictory;
|
||||
@ -17,16 +17,17 @@ export class BattleEndPhase extends BattlePhase {
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
this.scene.gameData.gameStats.battles++;
|
||||
if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex + 1 > this.scene.gameData.gameStats.highestEndlessWave) {
|
||||
this.scene.gameData.gameStats.highestEndlessWave = this.scene.currentBattle.waveIndex + 1;
|
||||
}
|
||||
|
||||
if (this.isVictory) {
|
||||
this.scene.currentBattle.addBattleScore(this.scene);
|
||||
|
||||
this.scene.gameData.gameStats.battles++;
|
||||
if (this.scene.currentBattle.trainer) {
|
||||
this.scene.gameData.gameStats.trainersDefeated++;
|
||||
}
|
||||
if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex + 1 > this.scene.gameData.gameStats.highestEndlessWave) {
|
||||
this.scene.gameData.gameStats.highestEndlessWave = this.scene.currentBattle.waveIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Endless graceful end
|
||||
@ -42,7 +43,7 @@ export class BattleEndPhase extends BattlePhase {
|
||||
}
|
||||
|
||||
for (const pokemon of this.scene.getPokemonAllowedInBattle()) {
|
||||
applyPostBattleAbAttrs(PostBattleAbAttr, pokemon);
|
||||
applyPostBattleAbAttrs(PostBattleAbAttr, pokemon, false, this.isVictory);
|
||||
}
|
||||
|
||||
if (this.scene.currentBattle.moneyScattered) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BattleSpec } from "#app/enums/battle-spec";
|
||||
import { DamageResult, HitResult } from "#app/field/pokemon";
|
||||
import * as Utils from "#app/utils";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import type BattleScene from "#app/battle-scene";
|
||||
import { type BattlerIndex } from "#app/battle";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
import { type DamageResult, HitResult } from "#app/field/pokemon";
|
||||
import { fixedInt } from "#app/utils";
|
||||
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
||||
|
||||
export class DamagePhase extends PokemonPhase {
|
||||
export class DamageAnimPhase extends PokemonPhase {
|
||||
private amount: integer;
|
||||
private damageResult: DamageResult;
|
||||
private critical: boolean;
|
||||
@ -25,7 +25,7 @@ export class DamagePhase extends PokemonPhase {
|
||||
if (this.scene.moveAnimations) {
|
||||
this.scene.toggleInvert(true);
|
||||
}
|
||||
this.scene.time.delayedCall(Utils.fixedInt(1000), () => {
|
||||
this.scene.time.delayedCall(fixedInt(1000), () => {
|
||||
this.scene.toggleInvert(false);
|
||||
this.applyDamage();
|
||||
});
|
@ -34,7 +34,7 @@ export class EggLapsePhase extends Phase {
|
||||
if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 1) {
|
||||
this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => {
|
||||
// show prompt for skip, blocking inputs for 1 second
|
||||
this.scene.ui.showText(i18next.t("battle:eggSkipPrompt"), 0);
|
||||
this.scene.ui.showText(i18next.t("battle:eggSkipPrompt", { eggsToHatch: eggsToHatchCount }), 0);
|
||||
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
|
||||
this.hatchEggsSkipped(eggsToHatch);
|
||||
this.showSummary();
|
||||
|
@ -12,7 +12,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
||||
import { SwitchType } from "#enums/switch-type";
|
||||
import i18next from "i18next";
|
||||
import { DamagePhase } from "./damage-phase";
|
||||
import { DamageAnimPhase } from "./damage-anim-phase";
|
||||
import { GameOverPhase } from "./game-over-phase";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import { SwitchPhase } from "./switch-phase";
|
||||
@ -206,7 +206,7 @@ export class FaintPhase extends PokemonPhase {
|
||||
} else {
|
||||
// Final boss' HP threshold has been bypassed; cancel faint and force check for 2nd phase
|
||||
enemy.hp++;
|
||||
this.scene.unshiftPhase(new DamagePhase(this.scene, enemy.getBattlerIndex(), 0, HitResult.OTHER));
|
||||
this.scene.unshiftPhase(new DamageAnimPhase(this.scene, enemy.getBattlerIndex(), 0, HitResult.OTHER));
|
||||
this.end();
|
||||
}
|
||||
return true;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr } from "#app/data/ability";
|
||||
import { applyPreSwitchOutAbAttrs, PostDamageForceSwitchAbAttr, PreSwitchOutAbAttr } from "#app/data/ability";
|
||||
import { allMoves, ForceSwitchOutAttr } from "#app/data/move";
|
||||
import { getPokeballTintColor } from "#app/data/pokeball";
|
||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
||||
@ -166,10 +166,11 @@ export class SwitchSummonPhase extends SummonPhase {
|
||||
|
||||
const currentCommand = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command;
|
||||
const lastPokemonIsForceSwitchedAndNotFainted = lastUsedMove?.hasAttr(ForceSwitchOutAttr) && !this.lastPokemon.isFainted();
|
||||
const lastPokemonHasForceSwitchAbAttr = this.lastPokemon.hasAbilityWithAttr(PostDamageForceSwitchAbAttr) && !this.lastPokemon.isFainted();
|
||||
|
||||
// Compensate for turn spent summoning
|
||||
// Or compensate for force switch move if switched out pokemon is not fainted
|
||||
if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted) {
|
||||
if (currentCommand === Command.POKEMON || lastPokemonIsForceSwitchedAndNotFainted || lastPokemonHasForceSwitchAbAttr) {
|
||||
pokemon.battleSummonData.turnCount--;
|
||||
pokemon.battleSummonData.waveTurnCount--;
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ import { BattlePhase } from "./battle-phase";
|
||||
import { ModifierRewardPhase } from "./modifier-reward-phase";
|
||||
import { MoneyRewardPhase } from "./money-reward-phase";
|
||||
import { TrainerSlot } from "#app/data/trainer-config";
|
||||
import { Biome } from "#app/enums/biome";
|
||||
import { achvs } from "#app/system/achv";
|
||||
|
||||
export class TrainerVictoryPhase extends BattlePhase {
|
||||
constructor(scene: BattleScene) {
|
||||
@ -34,11 +36,17 @@ export class TrainerVictoryPhase extends BattlePhase {
|
||||
}
|
||||
|
||||
const trainerType = this.scene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct?
|
||||
// Validate Voucher for boss trainers
|
||||
if (vouchers.hasOwnProperty(TrainerType[trainerType])) {
|
||||
if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) {
|
||||
this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [ modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM ][vouchers[TrainerType[trainerType]].voucherType]));
|
||||
}
|
||||
}
|
||||
// Breeders in Space achievement
|
||||
if (this.scene.arena.biomeType === Biome.SPACE
|
||||
&& (trainerType === TrainerType.BREEDER || trainerType === TrainerType.EXPERT_POKEMON_BREEDER)) {
|
||||
this.scene.validateAchv(achvs.BREEDERS_IN_SPACE);
|
||||
}
|
||||
|
||||
this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }), null, () => {
|
||||
const victoryMessages = this.scene.currentBattle.trainer?.getVictoryMessages()!; // TODO: is this bang correct?
|
||||
|
@ -41,7 +41,7 @@ export class VictoryPhase extends PokemonPhase {
|
||||
}
|
||||
|
||||
if (!this.scene.getEnemyParty().find(p => this.scene.currentBattle.battleType === BattleType.WILD ? p.isOnField() : !p?.isFainted(true))) {
|
||||
this.scene.pushPhase(new BattleEndPhase(this.scene));
|
||||
this.scene.pushPhase(new BattleEndPhase(this.scene, true));
|
||||
if (this.scene.currentBattle.battleType === BattleType.TRAINER) {
|
||||
this.scene.pushPhase(new TrainerVictoryPhase(this.scene));
|
||||
}
|
||||
|
@ -358,7 +358,7 @@ export const achvs = {
|
||||
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 18 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, (c, scene) => c instanceof FreshStartChallenge && c.value > 0 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, c => c instanceof InverseBattleChallenge && c.value > 0),
|
||||
BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 100).setSecret(),
|
||||
BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 50).setSecret(),
|
||||
};
|
||||
|
||||
export function initAchievements() {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import i18next from "i18next";
|
||||
import BattleScene from "../../battle-scene";
|
||||
import { hasTouchscreen } from "../../touch-controls";
|
||||
import { updateWindowType } from "../../ui/ui-theme";
|
||||
import { CandyUpgradeNotificationChangedEvent } from "../../events/battle-scene";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { hasTouchscreen } from "#app/touch-controls";
|
||||
import { updateWindowType } from "#app/ui/ui-theme";
|
||||
import { CandyUpgradeNotificationChangedEvent } from "#app/events/battle-scene";
|
||||
import SettingsUiHandler from "#app/ui/settings/settings-ui-handler";
|
||||
import { EaseType } from "#enums/ease-type";
|
||||
import { MoneyFormat } from "#enums/money-format";
|
||||
@ -44,6 +44,7 @@ const OFF_ON: SettingOption[] = [
|
||||
label: i18next.t("settings:on")
|
||||
}
|
||||
];
|
||||
|
||||
const AUTO_DISABLED: SettingOption[] = [
|
||||
{
|
||||
value: "Auto",
|
||||
@ -55,6 +56,19 @@ const AUTO_DISABLED: SettingOption[] = [
|
||||
}
|
||||
];
|
||||
|
||||
const TOUCH_CONTROLS_OPTIONS: SettingOption[] = [
|
||||
{
|
||||
value: "Auto",
|
||||
label: i18next.t("settings:auto")
|
||||
},
|
||||
{
|
||||
value: "Disabled",
|
||||
label: i18next.t("settings:disabled"),
|
||||
needConfirmation: true,
|
||||
confirmationMessage: i18next.t("settings:confirmDisableTouch")
|
||||
}
|
||||
];
|
||||
|
||||
const SHOP_CURSOR_TARGET_OPTIONS: SettingOption[] = [
|
||||
{
|
||||
value: "Rewards",
|
||||
@ -100,7 +114,9 @@ export enum SettingType {
|
||||
|
||||
type SettingOption = {
|
||||
value: string,
|
||||
label: string
|
||||
label: string,
|
||||
needConfirmation?: boolean,
|
||||
confirmationMessage?: string
|
||||
};
|
||||
|
||||
export interface Setting {
|
||||
@ -344,13 +360,6 @@ export const Setting: Array<Setting> = [
|
||||
default: 1,
|
||||
type: SettingType.GENERAL
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Touch_Controls,
|
||||
label: i18next.t("settings:touchControls"),
|
||||
options: AUTO_DISABLED,
|
||||
default: 0,
|
||||
type: SettingType.GENERAL
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Vibration,
|
||||
label: i18next.t("settings:vibrations"),
|
||||
@ -358,6 +367,28 @@ export const Setting: Array<Setting> = [
|
||||
default: 0,
|
||||
type: SettingType.GENERAL
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Touch_Controls,
|
||||
label: i18next.t("settings:touchControls"),
|
||||
options: TOUCH_CONTROLS_OPTIONS,
|
||||
default: 0,
|
||||
type: SettingType.GENERAL,
|
||||
isHidden: () => !hasTouchscreen()
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Move_Touch_Controls,
|
||||
label: i18next.t("settings:moveTouchControls"),
|
||||
options: [
|
||||
{
|
||||
value: "Configure",
|
||||
label: i18next.t("settings:change")
|
||||
}
|
||||
],
|
||||
default: 0,
|
||||
type: SettingType.GENERAL,
|
||||
activatable: true,
|
||||
isHidden: () => !hasTouchscreen()
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Language,
|
||||
label: i18next.t("settings:language"),
|
||||
@ -643,20 +674,6 @@ export const Setting: Array<Setting> = [
|
||||
type: SettingType.AUDIO,
|
||||
requireReload: true
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Move_Touch_Controls,
|
||||
label: i18next.t("settings:moveTouchControls"),
|
||||
options: [
|
||||
{
|
||||
value: "Configure",
|
||||
label: i18next.t("settings:change")
|
||||
}
|
||||
],
|
||||
default: 0,
|
||||
type: SettingType.GENERAL,
|
||||
activatable: true,
|
||||
isHidden: () => !hasTouchscreen()
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Shop_Cursor_Target,
|
||||
label: i18next.t("settings:shopCursorTarget"),
|
||||
@ -849,7 +866,7 @@ export function setSetting(scene: BattleScene, setting: string, value: integer):
|
||||
if (scene.ui) {
|
||||
const cancelHandler = () => {
|
||||
scene.ui.revertMode();
|
||||
(scene.ui.getHandler() as SettingsUiHandler).setOptionCursor(0, 0, true);
|
||||
(scene.ui.getHandler() as SettingsUiHandler).setOptionCursor(-1, 0, true);
|
||||
};
|
||||
const changeLocaleHandler = (locale: string): boolean => {
|
||||
try {
|
||||
|
81
src/test/abilities/analytic.test.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { isBetween, toDmgValue } from "#app/utils";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Analytic", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH, Moves.TACKLE ])
|
||||
.ability(Abilities.ANALYTIC)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.startingLevel(200)
|
||||
.enemyLevel(200)
|
||||
.enemySpecies(Species.SNORLAX)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should increase damage if the user moves last", async () => {
|
||||
await game.classicMode.startBattle([ Species.ARCEUS ]);
|
||||
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.TACKLE);
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||
await game.toNextTurn();
|
||||
const damage1 = enemy.getInverseHp();
|
||||
enemy.hp = enemy.getMaxHp();
|
||||
|
||||
game.move.select(Moves.TACKLE);
|
||||
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
expect(isBetween(enemy.getInverseHp(), toDmgValue(damage1 * 1.3) - 3, toDmgValue(damage1 * 1.3) + 3)).toBe(true);
|
||||
});
|
||||
|
||||
it("should increase damage only if the user moves last in doubles", async () => {
|
||||
game.override.battleType("double");
|
||||
await game.classicMode.startBattle([ Species.GENGAR, Species.SHUCKLE ]);
|
||||
|
||||
const [ enemy, ] = game.scene.getEnemyField();
|
||||
|
||||
game.move.select(Moves.TACKLE, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
|
||||
await game.toNextTurn();
|
||||
const damage1 = enemy.getInverseHp();
|
||||
enemy.hp = enemy.getMaxHp();
|
||||
|
||||
game.move.select(Moves.TACKLE, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER ]);
|
||||
await game.toNextTurn();
|
||||
expect(isBetween(enemy.getInverseHp(), toDmgValue(damage1 * 1.3) - 3, toDmgValue(damage1 * 1.3) + 3)).toBe(true);
|
||||
enemy.hp = enemy.getMaxHp();
|
||||
|
||||
game.move.select(Moves.TACKLE, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2 ]);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
expect(enemy.getInverseHp()).toBe(damage1);
|
||||
});
|
||||
});
|
@ -2,7 +2,7 @@ import { BattlerIndex } from "#app/battle";
|
||||
import { allAbilities } from "#app/data/ability";
|
||||
import { Abilities } from "#app/enums/abilities";
|
||||
import { WeatherType } from "#app/enums/weather-type";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { MovePhase } from "#app/phases/move-phase";
|
||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
||||
@ -273,7 +273,7 @@ describe("Abilities - Forecast", () => {
|
||||
const castform = game.scene.getPlayerPokemon()!;
|
||||
|
||||
// Damage phase should come first
|
||||
await game.phaseInterceptor.to(DamagePhase);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase);
|
||||
expect(castform.hp).toBeLessThan(castform.getMaxHp());
|
||||
|
||||
// Then change form
|
||||
|
74
src/test/abilities/honey_gather.test.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import type { CommandPhase } from "#app/phases/command-phase";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("Abilities - Honey Gather", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH, Moves.ROAR, Moves.THUNDERBOLT ])
|
||||
.startingLevel(100)
|
||||
.ability(Abilities.HONEY_GATHER)
|
||||
.passiveAbility(Abilities.RUN_AWAY)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should give money when winning a battle", async () => {
|
||||
await game.classicMode.startBattle([ Species.MILOTIC ]);
|
||||
game.scene.money = 1000;
|
||||
|
||||
game.move.select(Moves.THUNDERBOLT);
|
||||
await game.toNextWave();
|
||||
|
||||
expect(game.scene.money).toBeGreaterThan(1000);
|
||||
});
|
||||
|
||||
it("should not give money when the enemy pokemon flees", async () => {
|
||||
await game.classicMode.startBattle([ Species.MILOTIC ]);
|
||||
game.scene.money = 1000;
|
||||
|
||||
game.move.select(Moves.ROAR);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(game.scene.money).toBe(1000);
|
||||
expect(game.scene.currentBattle.waveIndex).toBe(2);
|
||||
});
|
||||
|
||||
it("should not give money when the player flees", async () => {
|
||||
await game.classicMode.startBattle([ Species.MILOTIC ]);
|
||||
game.scene.money = 1000;
|
||||
|
||||
// something weird is going on with the test framework, so this is required to prevent a crash
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(enemy, "scene", "get").mockReturnValue(game.scene);
|
||||
|
||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||
commandPhase.handleCommand(Command.RUN, 0);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(game.scene.money).toBe(1000);
|
||||
expect(game.scene.currentBattle.waveIndex).toBe(2);
|
||||
});
|
||||
});
|
@ -42,7 +42,7 @@ describe("Abilities - Hustle", () => {
|
||||
|
||||
game.move.select(Moves.TACKLE);
|
||||
await game.move.forceHit();
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(pikachu.getEffectiveStat).toHaveReturnedWith(Math.floor(atk * 1.5));
|
||||
});
|
||||
@ -68,7 +68,7 @@ describe("Abilities - Hustle", () => {
|
||||
vi.spyOn(pikachu, "getAccuracyMultiplier");
|
||||
|
||||
game.move.select(Moves.GIGA_DRAIN);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(pikachu.getEffectiveStat).toHaveReturnedWith(spatk);
|
||||
expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1);
|
||||
@ -86,7 +86,7 @@ describe("Abilities - Hustle", () => {
|
||||
vi.spyOn(allMoves[Moves.FISSURE], "calculateBattleAccuracy");
|
||||
|
||||
game.move.select(Moves.FISSURE);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(enemyPokemon.turnData.damageTaken).toBe(enemyPokemon.getMaxHp());
|
||||
expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1);
|
||||
|
@ -51,7 +51,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
const firstStrikeDamage = enemyStartingHp - enemyPokemon.hp;
|
||||
enemyStartingHp = enemyPokemon.hp;
|
||||
|
||||
@ -129,7 +129,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
|
||||
game.move.select(Moves.SELF_DESTRUCT);
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase", false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(1);
|
||||
}
|
||||
@ -147,7 +147,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
game.move.select(Moves.ROLLOUT);
|
||||
await game.move.forceHit();
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase", false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(1);
|
||||
}
|
||||
@ -181,7 +181,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.COUNTER);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
const playerDamage = leadPokemon.getMaxHp() - leadPokemon.hp;
|
||||
|
||||
@ -221,7 +221,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.select(Moves.EARTHQUAKE);
|
||||
await game.phaseInterceptor.to("DamagePhase", false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||
}
|
||||
@ -238,7 +238,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
|
||||
game.move.select(Moves.MIND_BLOWN);
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase", false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||
|
||||
@ -285,7 +285,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(3);
|
||||
}
|
||||
@ -307,7 +307,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
game.move.select(Moves.SEISMIC_TOSS);
|
||||
await game.move.forceHit();
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(3);
|
||||
|
||||
@ -329,7 +329,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
game.move.select(Moves.HYPER_BEAM);
|
||||
await game.move.forceHit();
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||
expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeUndefined();
|
||||
@ -353,7 +353,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
game.move.select(Moves.ANCHOR_SHOT);
|
||||
await game.move.forceHit();
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined();
|
||||
@ -380,7 +380,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
game.move.select(Moves.SMACK_DOWN);
|
||||
await game.move.forceHit();
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
@ -424,7 +424,7 @@ describe("Abilities - Parental Bond", () => {
|
||||
game.move.select(Moves.WAKE_UP_SLAP);
|
||||
await game.move.forceHit();
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||
expect(enemyPokemon.status?.effect).toBe(StatusEffect.SLEEP);
|
||||
|
@ -28,7 +28,9 @@ describe("Abilities - Speed Boost", () => {
|
||||
|
||||
game.override
|
||||
.battleType("single")
|
||||
.enemySpecies(Species.DRAGAPULT)
|
||||
.enemySpecies(Species.SHUCKLE)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyLevel(100)
|
||||
.ability(Abilities.SPEED_BOOST)
|
||||
.enemyMoveset(Moves.SPLASH)
|
||||
.moveset([ Moves.SPLASH, Moves.U_TURN ]);
|
||||
@ -70,21 +72,23 @@ describe("Abilities - Speed Boost", () => {
|
||||
Species.NINJASK
|
||||
]);
|
||||
|
||||
game.move.select(Moves.U_TURN);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
let playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
|
||||
const [ shuckle, ninjask ] = game.scene.getPlayerParty();
|
||||
|
||||
game.move.select(Moves.U_TURN);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
|
||||
expect(game.scene.getPlayerPokemon()!).toBe(ninjask);
|
||||
expect(ninjask.getStatStage(Stat.SPD)).toBe(0);
|
||||
|
||||
game.move.select(Moves.U_TURN);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toNextTurn();
|
||||
expect(game.scene.getPlayerPokemon()!).toBe(shuckle);
|
||||
expect(shuckle.getStatStage(Stat.SPD)).toBe(0);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(1);
|
||||
expect(shuckle.getStatStage(Stat.SPD)).toBe(1);
|
||||
});
|
||||
|
||||
it("should not trigger this turn if pokemon was switched into combat via normal switch, but the turn after",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
@ -55,7 +55,7 @@ describe("Abilities - Sturdy", () => {
|
||||
enemyPokemon.hp = enemyPokemon.getMaxHp() - 1;
|
||||
|
||||
game.move.select(Moves.CLOSE_COMBAT);
|
||||
await game.phaseInterceptor.to(DamagePhase);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase);
|
||||
|
||||
expect(enemyPokemon.hp).toBe(0);
|
||||
expect(enemyPokemon.isFainted()).toBe(true);
|
||||
@ -81,7 +81,7 @@ describe("Abilities - Sturdy", () => {
|
||||
|
||||
await game.startBattle();
|
||||
game.move.select(Moves.CLOSE_COMBAT);
|
||||
await game.phaseInterceptor.to(DamagePhase);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase);
|
||||
|
||||
const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0];
|
||||
expect(enemyPokemon.hp).toBe(0);
|
||||
|
@ -3,7 +3,7 @@ import { Stat } from "#enums/stat";
|
||||
import { GameModes, getGameMode } from "#app/game-mode";
|
||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
||||
import { LoginPhase } from "#app/phases/login-phase";
|
||||
@ -267,7 +267,7 @@ describe("Test Battle Phase", () => {
|
||||
]);
|
||||
|
||||
game.move.select(moveToUse);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.killPokemon(game.scene.currentBattle.enemyParty[0]);
|
||||
expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true);
|
||||
await game.phaseInterceptor.to(VictoryPhase, false);
|
||||
|
@ -102,7 +102,7 @@ describe("Battle Mechanics - Damage Calculation", () => {
|
||||
|
||||
game.move.select(Moves.JUMP_KICK);
|
||||
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
|
||||
expect(shedinja.hp).toBe(shedinja.getMaxHp() - 1);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
@ -48,7 +48,7 @@ describe("Items - Leftovers", () => {
|
||||
game.move.select(Moves.SPLASH);
|
||||
|
||||
// We should have less hp after the attack
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
|
||||
|
||||
const leadHpAfterDamage = leadPokemon.hp;
|
||||
|
@ -135,4 +135,57 @@ describe("Items - Multi Lens", () => {
|
||||
expect(damageResults[0]).toBe(Math.floor(playerPokemon.level * 0.75));
|
||||
expect(damageResults[1]).toBe(Math.floor(playerPokemon.level * 0.25));
|
||||
});
|
||||
|
||||
it("should result in correct damage for hp% attacks with 1 lens", async () => {
|
||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }])
|
||||
.moveset(Moves.SUPER_FANG)
|
||||
.ability(Abilities.COMPOUND_EYES)
|
||||
.enemyLevel(1000)
|
||||
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
||||
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.SUPER_FANG);
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 5);
|
||||
});
|
||||
|
||||
it("should result in correct damage for hp% attacks with 2 lenses", async () => {
|
||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }])
|
||||
.moveset(Moves.SUPER_FANG)
|
||||
.ability(Abilities.COMPOUND_EYES)
|
||||
.enemyMoveset(Moves.SPLASH)
|
||||
.enemyLevel(1000)
|
||||
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
||||
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.SUPER_FANG);
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 5);
|
||||
});
|
||||
it("should result in correct damage for hp% attacks with 2 lenses + Parental Bond", async () => {
|
||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }])
|
||||
.moveset(Moves.SUPER_FANG)
|
||||
.ability(Abilities.PARENTAL_BOND)
|
||||
.passiveAbility(Abilities.COMPOUND_EYES)
|
||||
.enemyMoveset(Moves.SPLASH)
|
||||
.enemyLevel(1000)
|
||||
.enemySpecies(Species.BLISSEY); // allows for unrealistically high levels of accuracy
|
||||
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.SUPER_FANG);
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.25, 5);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { Status } from "#app/data/status-effect";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { Type } from "#enums/type";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
@ -193,4 +197,122 @@ describe("Moves - Dragon Tail", () => {
|
||||
expect(dratini.hp).toBe(Math.floor(dratini.getMaxHp() / 2));
|
||||
expect(game.scene.getPlayerField().length).toBe(1);
|
||||
});
|
||||
|
||||
it("should force switches randomly", async () => {
|
||||
game.override.enemyMoveset(Moves.DRAGON_TAIL)
|
||||
.startingLevel(100)
|
||||
.enemyLevel(1);
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE ]);
|
||||
|
||||
const [ bulbasaur, charmander, squirtle ] = game.scene.getPlayerParty();
|
||||
|
||||
// Turn 1: Mock an RNG call that calls for switching to 1st backup Pokemon (Charmander)
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.DRAGON_TAIL);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(bulbasaur.isOnField()).toBe(false);
|
||||
expect(charmander.isOnField()).toBe(true);
|
||||
expect(squirtle.isOnField()).toBe(false);
|
||||
expect(bulbasaur.getInverseHp()).toBeGreaterThan(0);
|
||||
|
||||
// Turn 2: Mock an RNG call that calls for switching to 2nd backup Pokemon (Squirtle)
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min + 1;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(bulbasaur.isOnField()).toBe(false);
|
||||
expect(charmander.isOnField()).toBe(false);
|
||||
expect(squirtle.isOnField()).toBe(true);
|
||||
expect(charmander.getInverseHp()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should not force a switch to a challenge-ineligible Pokemon", async () => {
|
||||
game.override.enemyMoveset(Moves.DRAGON_TAIL)
|
||||
.startingLevel(100)
|
||||
.enemyLevel(1);
|
||||
// Mono-Water challenge, Eevee is ineligible
|
||||
game.challengeMode.addChallenge(Challenges.SINGLE_TYPE, Type.WATER + 1, 0);
|
||||
await game.challengeMode.startBattle([ Species.LAPRAS, Species.EEVEE, Species.TOXAPEX, Species.PRIMARINA ]);
|
||||
|
||||
const [ lapras, eevee, toxapex, primarina ] = game.scene.getPlayerParty();
|
||||
|
||||
// Turn 1: Mock an RNG call that would normally call for switching to Eevee, but it is ineligible
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(lapras.isOnField()).toBe(false);
|
||||
expect(eevee.isOnField()).toBe(false);
|
||||
expect(toxapex.isOnField()).toBe(true);
|
||||
expect(primarina.isOnField()).toBe(false);
|
||||
expect(lapras.getInverseHp()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should not force a switch to a fainted Pokemon", async () => {
|
||||
game.override.enemyMoveset([ Moves.SPLASH, Moves.DRAGON_TAIL ])
|
||||
.startingLevel(100)
|
||||
.enemyLevel(1);
|
||||
await game.classicMode.startBattle([ Species.LAPRAS, Species.EEVEE, Species.TOXAPEX, Species.PRIMARINA ]);
|
||||
|
||||
const [ lapras, eevee, toxapex, primarina ] = game.scene.getPlayerParty();
|
||||
|
||||
// Turn 1: Eevee faints
|
||||
eevee.hp = 0;
|
||||
eevee.status = new Status(StatusEffect.FAINT);
|
||||
expect(eevee.isFainted()).toBe(true);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
// Turn 2: Mock an RNG call that would normally call for switching to Eevee, but it is fainted
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.DRAGON_TAIL);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(lapras.isOnField()).toBe(false);
|
||||
expect(eevee.isOnField()).toBe(false);
|
||||
expect(toxapex.isOnField()).toBe(true);
|
||||
expect(primarina.isOnField()).toBe(false);
|
||||
expect(lapras.getInverseHp()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should not force a switch if there are no available Pokemon to switch into", async () => {
|
||||
game.override.enemyMoveset([ Moves.SPLASH, Moves.DRAGON_TAIL ])
|
||||
.startingLevel(100)
|
||||
.enemyLevel(1);
|
||||
await game.classicMode.startBattle([ Species.LAPRAS, Species.EEVEE ]);
|
||||
|
||||
const [ lapras, eevee ] = game.scene.getPlayerParty();
|
||||
|
||||
// Turn 1: Eevee faints
|
||||
eevee.hp = 0;
|
||||
eevee.status = new Status(StatusEffect.FAINT);
|
||||
expect(eevee.isFainted()).toBe(true);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
// Turn 2: Mock an RNG call that would normally call for switching to Eevee, but it is fainted
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.DRAGON_TAIL);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(lapras.isOnField()).toBe(true);
|
||||
expect(eevee.isOnField()).toBe(false);
|
||||
expect(lapras.getInverseHp()).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
@ -51,7 +51,7 @@ describe("Moves - Dynamax Cannon", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
}, 20000);
|
||||
|
||||
@ -65,7 +65,7 @@ describe("Moves - Dynamax Cannon", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
}, 20000);
|
||||
|
||||
@ -82,7 +82,7 @@ describe("Moves - Dynamax Cannon", () => {
|
||||
expect(phase.move.moveId).toBe(dynamaxCannon.id);
|
||||
// Force level cap to be 100
|
||||
vi.spyOn(phase.getFirstTarget()!.scene, "getMaxExpLevel").mockReturnValue(100);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(120);
|
||||
}, 20000);
|
||||
|
||||
@ -99,7 +99,7 @@ describe("Moves - Dynamax Cannon", () => {
|
||||
expect(phase.move.moveId).toBe(dynamaxCannon.id);
|
||||
// Force level cap to be 100
|
||||
vi.spyOn(phase.getFirstTarget()!.scene, "getMaxExpLevel").mockReturnValue(100);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(140);
|
||||
}, 20000);
|
||||
|
||||
@ -116,7 +116,7 @@ describe("Moves - Dynamax Cannon", () => {
|
||||
expect(phase.move.moveId).toBe(dynamaxCannon.id);
|
||||
// Force level cap to be 100
|
||||
vi.spyOn(phase.getFirstTarget()!.scene, "getMaxExpLevel").mockReturnValue(100);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(160);
|
||||
}, 20000);
|
||||
|
||||
@ -133,7 +133,7 @@ describe("Moves - Dynamax Cannon", () => {
|
||||
expect(phase.move.moveId).toBe(dynamaxCannon.id);
|
||||
// Force level cap to be 100
|
||||
vi.spyOn(phase.getFirstTarget()!.scene, "getMaxExpLevel").mockReturnValue(100);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(180);
|
||||
}, 20000);
|
||||
|
||||
@ -150,7 +150,7 @@ describe("Moves - Dynamax Cannon", () => {
|
||||
expect(phase.move.moveId).toBe(dynamaxCannon.id);
|
||||
// Force level cap to be 100
|
||||
vi.spyOn(phase.getFirstTarget()!.scene, "getMaxExpLevel").mockReturnValue(100);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
||||
@ -165,7 +165,7 @@ describe("Moves - Dynamax Cannon", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Stat } from "#enums/stat";
|
||||
import { Species } from "#app/enums/species";
|
||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
@ -56,7 +56,7 @@ describe("Moves - Fissure", () => {
|
||||
game.override.enemyAbility(Abilities.FUR_COAT);
|
||||
|
||||
game.move.select(Moves.FISSURE);
|
||||
await game.phaseInterceptor.to(DamagePhase, true);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, true);
|
||||
|
||||
expect(enemyPokemon.isFainted()).toBe(true);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Stat } from "#enums/stat";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { MovePhase } from "#app/phases/move-phase";
|
||||
@ -58,12 +58,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
||||
@ -81,12 +81,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
||||
@ -104,7 +104,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
@ -114,7 +114,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
||||
@ -133,7 +133,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
@ -142,7 +142,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
}, 20000);
|
||||
|
||||
@ -160,12 +160,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
||||
@ -209,22 +209,22 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
||||
@ -268,22 +268,22 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamagePhase, false);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
});
|
||||
|
@ -41,11 +41,11 @@ describe("Moves - Glaive Rush", () => {
|
||||
enemy.hp = 1000;
|
||||
|
||||
game.move.select(Moves.SHADOW_SNEAK);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
const damageDealt = 1000 - enemy.hp;
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
game.move.select(Moves.SHADOW_SNEAK);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.phaseInterceptor.to("DamageAnimPhase");
|
||||
expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3));
|
||||
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
@ -48,7 +48,7 @@ describe("Moves - Scale Shot", () => {
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||
await game.phaseInterceptor.to(DamagePhase);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase);
|
||||
|
||||
//check that stats haven't changed after one or two hits have occurred
|
||||
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||
|
@ -26,7 +26,7 @@ describe("Moves - Shell Side Arm", () => {
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SHELL_SIDE_ARM ])
|
||||
.moveset([ Moves.SHELL_SIDE_ARM, Moves.SPLASH ])
|
||||
.battleType("single")
|
||||
.startingLevel(100)
|
||||
.enemyLevel(100)
|
||||
@ -69,6 +69,9 @@ describe("Moves - Shell Side Arm", () => {
|
||||
|
||||
vi.spyOn(shellSideArmAttr, "apply");
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.move.select(Moves.SHELL_SIDE_ARM);
|
||||
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
@ -1,11 +1,15 @@
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import { Type } from "#enums/type";
|
||||
import { MoveResult } from "#app/field/pokemon";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { Status } from "#app/data/status-effect";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
|
||||
describe("Moves - Whirlwind", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
@ -25,8 +29,9 @@ describe("Moves - Whirlwind", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleType("single")
|
||||
.moveset(Moves.SPLASH)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.WHIRLWIND)
|
||||
.enemyMoveset([ Moves.SPLASH, Moves.WHIRLWIND ])
|
||||
.enemySpecies(Species.PIDGEY);
|
||||
});
|
||||
|
||||
@ -41,10 +46,114 @@ describe("Moves - Whirlwind", () => {
|
||||
const staraptor = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.select(move);
|
||||
await game.forceEnemyMove(Moves.WHIRLWIND);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(staraptor.findTag((t) => t.tagType === BattlerTagType.FLYING)).toBeDefined();
|
||||
expect(game.scene.getEnemyPokemon()!.getLastXMoves(1)[0].result).toBe(MoveResult.MISS);
|
||||
});
|
||||
|
||||
it("should force switches randomly", async () => {
|
||||
await game.classicMode.startBattle([ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE ]);
|
||||
|
||||
const [ bulbasaur, charmander, squirtle ] = game.scene.getPlayerParty();
|
||||
|
||||
// Turn 1: Mock an RNG call that calls for switching to 1st backup Pokemon (Charmander)
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.WHIRLWIND);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(bulbasaur.isOnField()).toBe(false);
|
||||
expect(charmander.isOnField()).toBe(true);
|
||||
expect(squirtle.isOnField()).toBe(false);
|
||||
|
||||
// Turn 2: Mock an RNG call that calls for switching to 2nd backup Pokemon (Squirtle)
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min + 1;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.WHIRLWIND);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(bulbasaur.isOnField()).toBe(false);
|
||||
expect(charmander.isOnField()).toBe(false);
|
||||
expect(squirtle.isOnField()).toBe(true);
|
||||
});
|
||||
|
||||
it("should not force a switch to a challenge-ineligible Pokemon", async () => {
|
||||
// Mono-Water challenge, Eevee is ineligible
|
||||
game.challengeMode.addChallenge(Challenges.SINGLE_TYPE, Type.WATER + 1, 0);
|
||||
await game.challengeMode.startBattle([ Species.LAPRAS, Species.EEVEE, Species.TOXAPEX, Species.PRIMARINA ]);
|
||||
|
||||
const [ lapras, eevee, toxapex, primarina ] = game.scene.getPlayerParty();
|
||||
|
||||
// Turn 1: Mock an RNG call that would normally call for switching to Eevee, but it is ineligible
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.WHIRLWIND);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(lapras.isOnField()).toBe(false);
|
||||
expect(eevee.isOnField()).toBe(false);
|
||||
expect(toxapex.isOnField()).toBe(true);
|
||||
expect(primarina.isOnField()).toBe(false);
|
||||
});
|
||||
|
||||
it("should not force a switch to a fainted Pokemon", async () => {
|
||||
await game.classicMode.startBattle([ Species.LAPRAS, Species.EEVEE, Species.TOXAPEX, Species.PRIMARINA ]);
|
||||
|
||||
const [ lapras, eevee, toxapex, primarina ] = game.scene.getPlayerParty();
|
||||
|
||||
// Turn 1: Eevee faints
|
||||
eevee.hp = 0;
|
||||
eevee.status = new Status(StatusEffect.FAINT);
|
||||
expect(eevee.isFainted()).toBe(true);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
// Turn 2: Mock an RNG call that would normally call for switching to Eevee, but it is fainted
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.WHIRLWIND);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(lapras.isOnField()).toBe(false);
|
||||
expect(eevee.isOnField()).toBe(false);
|
||||
expect(toxapex.isOnField()).toBe(true);
|
||||
expect(primarina.isOnField()).toBe(false);
|
||||
});
|
||||
|
||||
it("should not force a switch if there are no available Pokemon to switch into", async () => {
|
||||
await game.classicMode.startBattle([ Species.LAPRAS, Species.EEVEE ]);
|
||||
|
||||
const [ lapras, eevee ] = game.scene.getPlayerParty();
|
||||
|
||||
// Turn 1: Eevee faints
|
||||
eevee.hp = 0;
|
||||
eevee.status = new Status(StatusEffect.FAINT);
|
||||
expect(eevee.isFainted()).toBe(true);
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
// Turn 2: Mock an RNG call that would normally call for switching to Eevee, but it is fainted
|
||||
vi.spyOn(game.scene, "randBattleSeedInt").mockImplementation((range, min: number = 0) => {
|
||||
return min;
|
||||
});
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.WHIRLWIND);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(lapras.isOnField()).toBe(true);
|
||||
expect(eevee.isOnField()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
@ -33,7 +33,7 @@ export async function runMysteryEncounterToEnd(game: GameManager, optionNo: numb
|
||||
}, () => game.isCurrentPhase(MysteryEncounterBattlePhase) || game.isCurrentPhase(MysteryEncounterRewardsPhase));
|
||||
|
||||
if (isBattle) {
|
||||
game.onNextPrompt("DamagePhase", Mode.MESSAGE, () => {
|
||||
game.onNextPrompt("DamageAnimPhase", Mode.MESSAGE, () => {
|
||||
game.setMode(Mode.MESSAGE);
|
||||
game.endPhase();
|
||||
}, () => game.isCurrentPhase(CommandPhase));
|
||||
|
173
src/test/mystery-encounter/encounters/safari-zone.test.ts
Normal file
@ -0,0 +1,173 @@
|
||||
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption } from "#test/mystery-encounter/encounter-test-utils";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
|
||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||
import { getSafariSpeciesSpawn, SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter";
|
||||
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups";
|
||||
|
||||
const namespace = "mysteryEncounters/safariZone";
|
||||
const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ];
|
||||
const defaultBiome = Biome.SWAMP;
|
||||
const defaultWave = 45;
|
||||
|
||||
describe("Safari Zone - Mystery Encounter", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
let scene: BattleScene;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
game = new GameManager(phaserGame);
|
||||
scene = game.scene;
|
||||
game.override.mysteryEncounterChance(100);
|
||||
game.override.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
game.override.disableTrainerWaves();
|
||||
|
||||
const biomeMap = new Map<Biome, MysteryEncounterType[]>([
|
||||
[ Biome.VOLCANO, [ MysteryEncounterType.FIGHT_OR_FLIGHT ]],
|
||||
[ Biome.FOREST, [ MysteryEncounterType.SAFARI_ZONE ]],
|
||||
[ Biome.SWAMP, [ MysteryEncounterType.SAFARI_ZONE ]],
|
||||
[ Biome.JUNGLE, [ MysteryEncounterType.SAFARI_ZONE ]],
|
||||
]);
|
||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(biomeMap);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should have the correct properties", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.SAFARI_ZONE, defaultParty);
|
||||
|
||||
expect(SafariZoneEncounter.encounterType).toBe(MysteryEncounterType.SAFARI_ZONE);
|
||||
expect(SafariZoneEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT);
|
||||
expect(SafariZoneEncounter.dialogue).toBeDefined();
|
||||
expect(SafariZoneEncounter.dialogue.intro).toStrictEqual([
|
||||
{ text: `${namespace}:intro` },
|
||||
]);
|
||||
expect(SafariZoneEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}:title`);
|
||||
expect(SafariZoneEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}:description`);
|
||||
expect(SafariZoneEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}:query`);
|
||||
expect(SafariZoneEncounter.options.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should not spawn outside of the forest, swamp, or jungle biomes", async () => {
|
||||
game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT);
|
||||
game.override.startingBiome(Biome.VOLCANO);
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.SAFARI_ZONE);
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(SafariZoneEncounter);
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
scene.currentBattle.waveIndex = defaultWave;
|
||||
|
||||
const { onInit } = encounter;
|
||||
|
||||
expect(encounter.onInit).toBeDefined();
|
||||
|
||||
encounter.populateDialogueTokensFromRequirements(scene);
|
||||
const onInitResult = onInit!(scene);
|
||||
expect(onInitResult).toBe(true);
|
||||
});
|
||||
|
||||
describe("Option 1 - Enter", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = SafariZoneEncounter.options[0];
|
||||
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option.1.label`,
|
||||
buttonTooltip: `${namespace}:option.1.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.1.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should NOT be selectable if the player doesn't have enough money", async () => {
|
||||
game.scene.money = 0;
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.SAFARI_ZONE, defaultParty);
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
|
||||
const encounterPhase = scene.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase;
|
||||
vi.spyOn(mysteryEncounterPhase, "continueEncounter");
|
||||
vi.spyOn(mysteryEncounterPhase, "handleOptionSelect");
|
||||
vi.spyOn(scene.ui, "playError");
|
||||
|
||||
await runSelectMysteryEncounterOption(game, 1);
|
||||
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
|
||||
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
|
||||
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not spawn any Paradox Pokemon", async () => {
|
||||
const NUM_ROLLS = 2000; // As long as this is greater than total number of species, this should cover all possible RNG rolls
|
||||
let rngSweepProgress = 0; // Will simulate full range of RNG rolls by steadily increasing from 0 to 1
|
||||
|
||||
vi.spyOn(Phaser.Math.RND, "realInRange").mockImplementation((min: number, max: number) => {
|
||||
return rngSweepProgress * (max - min) + min;
|
||||
});
|
||||
vi.spyOn(Phaser.Math.RND, "shuffle").mockImplementation((arr: any[]) => arr);
|
||||
|
||||
for (let i = 0; i < NUM_ROLLS; i++) {
|
||||
rngSweepProgress = (2 * i + 1) / (2 * NUM_ROLLS);
|
||||
const simSpecies = getSafariSpeciesSpawn().speciesId;
|
||||
expect(NON_LEGEND_PARADOX_POKEMON).not.toContain(simSpecies);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Tests for player actions inside the Safari Zone (Pokeball, Mud, Bait, Flee)
|
||||
});
|
||||
|
||||
describe("Option 2 - Leave", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = SafariZoneEncounter.options[1];
|
||||
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option.2.label`,
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option.2.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.SAFARI_ZONE, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
@ -9,11 +9,12 @@ import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption } from "#test
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import { ThePokemonSalesmanEncounter } from "#app/data/mystery-encounters/encounters/the-pokemon-salesman-encounter";
|
||||
import { getSalesmanSpeciesOffer, ThePokemonSalesmanEncounter } from "#app/data/mystery-encounters/encounters/the-pokemon-salesman-encounter";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
|
||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||
import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups";
|
||||
|
||||
const namespace = "mysteryEncounters/thePokemonSalesman";
|
||||
const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ];
|
||||
@ -172,6 +173,22 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
||||
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not offer any Paradox Pokemon", async () => {
|
||||
const NUM_ROLLS = 2000; // As long as this is greater than total number of species, this should cover all possible RNG rolls
|
||||
let rngSweepProgress = 0; // Will simulate full range of RNG rolls by steadily increasing from 0 to 1
|
||||
|
||||
vi.spyOn(Phaser.Math.RND, "realInRange").mockImplementation((min: number, max: number) => {
|
||||
return rngSweepProgress * (max - min) + min;
|
||||
});
|
||||
vi.spyOn(Phaser.Math.RND, "shuffle").mockImplementation((arr: any[]) => arr);
|
||||
|
||||
for (let i = 0; i < NUM_ROLLS; i++) {
|
||||
rngSweepProgress = (2 * i + 1) / (2 * NUM_ROLLS);
|
||||
const simSpecies = getSalesmanSpeciesOffer().speciesId;
|
||||
expect(NON_LEGEND_PARADOX_POKEMON).not.toContain(simSpecies);
|
||||
}
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
scene.money = 20000;
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
@ -2,7 +2,7 @@ import BattleScene from "#app/battle-scene";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { getEncounterText, queueEncounterMessage, showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { getHighestLevelPlayerPokemon, getLowestLevelPlayerPokemon, getRandomPlayerPokemon, getRandomSpeciesByStarterTier, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getHighestLevelPlayerPokemon, getLowestLevelPlayerPokemon, getRandomPlayerPokemon, getRandomSpeciesByStarterCost, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Type } from "#enums/type";
|
||||
import { MessagePhase } from "#app/phases/message-phase";
|
||||
@ -204,9 +204,9 @@ describe("Mystery Encounter Utils", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRandomSpeciesByStarterTier", () => {
|
||||
describe("getRandomSpeciesByStarterCost", () => {
|
||||
it("gets species for a starter tier", () => {
|
||||
const result = getRandomSpeciesByStarterTier(5);
|
||||
const result = getRandomSpeciesByStarterCost(5);
|
||||
const pokeSpecies = getPokemonSpecies(result);
|
||||
|
||||
expect(pokeSpecies.speciesId).toBe(result);
|
||||
@ -214,7 +214,7 @@ describe("Mystery Encounter Utils", () => {
|
||||
});
|
||||
|
||||
it("gets species for a starter tier range", () => {
|
||||
const result = getRandomSpeciesByStarterTier([ 5, 8 ]);
|
||||
const result = getRandomSpeciesByStarterCost([ 5, 8 ]);
|
||||
const pokeSpecies = getPokemonSpecies(result);
|
||||
|
||||
expect(pokeSpecies.speciesId).toBe(result);
|
||||
@ -224,14 +224,14 @@ describe("Mystery Encounter Utils", () => {
|
||||
|
||||
it("excludes species from search", () => {
|
||||
// Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian
|
||||
const result = getRandomSpeciesByStarterTier(9, [ Species.KORAIDON, Species.MIRAIDON, Species.ARCEUS, Species.RAYQUAZA, Species.KYOGRE, Species.GROUDON ]);
|
||||
const result = getRandomSpeciesByStarterCost(9, [ Species.KORAIDON, Species.MIRAIDON, Species.ARCEUS, Species.RAYQUAZA, Species.KYOGRE, Species.GROUDON ]);
|
||||
const pokeSpecies = getPokemonSpecies(result);
|
||||
expect(pokeSpecies.speciesId).toBe(Species.ZACIAN);
|
||||
});
|
||||
|
||||
it("gets species of specified types", () => {
|
||||
// Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian
|
||||
const result = getRandomSpeciesByStarterTier(9, undefined, [ Type.GROUND ]);
|
||||
const result = getRandomSpeciesByStarterCost(9, undefined, [ Type.GROUND ]);
|
||||
const pokeSpecies = getPokemonSpecies(result);
|
||||
expect(pokeSpecies.speciesId).toBe(Species.GROUDON);
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { BerryPhase } from "#app/phases/berry-phase";
|
||||
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { DamagePhase } from "#app/phases/damage-phase";
|
||||
import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
|
||||
import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
|
||||
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||
import { EndEvolutionPhase } from "#app/phases/end-evolution-phase";
|
||||
@ -87,7 +87,7 @@ type PhaseClass =
|
||||
| typeof TurnStartPhase
|
||||
| typeof MovePhase
|
||||
| typeof MoveEffectPhase
|
||||
| typeof DamagePhase
|
||||
| typeof DamageAnimPhase
|
||||
| typeof FaintPhase
|
||||
| typeof BerryPhase
|
||||
| typeof TurnEndPhase
|
||||
@ -146,7 +146,7 @@ type PhaseString =
|
||||
| "TurnStartPhase"
|
||||
| "MovePhase"
|
||||
| "MoveEffectPhase"
|
||||
| "DamagePhase"
|
||||
| "DamageAnimPhase"
|
||||
| "FaintPhase"
|
||||
| "BerryPhase"
|
||||
| "TurnEndPhase"
|
||||
@ -229,7 +229,7 @@ export default class PhaseInterceptor {
|
||||
[ TurnStartPhase, this.startPhase ],
|
||||
[ MovePhase, this.startPhase ],
|
||||
[ MoveEffectPhase, this.startPhase ],
|
||||
[ DamagePhase, this.startPhase ],
|
||||
[ DamageAnimPhase, this.startPhase ],
|
||||
[ FaintPhase, this.startPhase ],
|
||||
[ BerryPhase, this.startPhase ],
|
||||
[ TurnEndPhase, this.startPhase ],
|
||||
|
@ -8,6 +8,7 @@ import { addTextObject, TextStyle } from "./text";
|
||||
import { addWindow } from "./ui-theme";
|
||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||
import JSZip from "jszip";
|
||||
|
||||
interface BuildInteractableImageOpts {
|
||||
scale?: number;
|
||||
@ -27,6 +28,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||
private googleImage: Phaser.GameObjects.Image;
|
||||
private discordImage: Phaser.GameObjects.Image;
|
||||
private usernameInfoImage: Phaser.GameObjects.Image;
|
||||
private saveDownloadImage: Phaser.GameObjects.Image;
|
||||
private externalPartyContainer: Phaser.GameObjects.Container;
|
||||
private infoContainer: Phaser.GameObjects.Container;
|
||||
private externalPartyBg: Phaser.GameObjects.NineSlice;
|
||||
@ -46,7 +48,13 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||
scale: 0.5
|
||||
});
|
||||
|
||||
this.saveDownloadImage = this.buildInteractableImage("saving_icon", "save-download-icon", {
|
||||
x: 0,
|
||||
scale: 0.75
|
||||
});
|
||||
|
||||
this.infoContainer.add(this.usernameInfoImage);
|
||||
this.infoContainer.add(this.saveDownloadImage);
|
||||
this.getUi().add(this.infoContainer);
|
||||
this.infoContainer.setVisible(false);
|
||||
this.infoContainer.disableInteractive();
|
||||
@ -160,7 +168,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||
this.infoContainer.setVisible(false);
|
||||
this.setMouseCursorStyle("default"); //reset cursor
|
||||
|
||||
[ this.discordImage, this.googleImage, this.usernameInfoImage ].forEach((img) => img.off("pointerdown"));
|
||||
[ this.discordImage, this.googleImage, this.usernameInfoImage, this.saveDownloadImage ].forEach((img) => img.off("pointerdown"));
|
||||
}
|
||||
|
||||
private processExternalProvider(config: ModalConfig): void {
|
||||
@ -178,6 +186,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||
this.infoContainer.setVisible(true);
|
||||
this.getUi().moveTo(this.infoContainer, this.getUi().length - 1);
|
||||
this.usernameInfoImage.setPositionRelative(this.infoContainer, 0, 0);
|
||||
this.saveDownloadImage.setPositionRelative(this.infoContainer, 20, 0);
|
||||
|
||||
this.discordImage.on("pointerdown", () => {
|
||||
const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/discord/callback`);
|
||||
@ -229,6 +238,34 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||
}
|
||||
});
|
||||
|
||||
this.saveDownloadImage.on("pointerdown", () => {
|
||||
// find all data_ and sessionData keys, put them in a .txt file and download everything in a single zip
|
||||
const localStorageKeys = Object.keys(localStorage); // this gets the keys for localStorage
|
||||
const keyToFind = "data_";
|
||||
const sessionKeyToFind = "sessionData";
|
||||
const dataKeys = localStorageKeys.filter(ls => ls.indexOf(keyToFind) >= 0);
|
||||
const sessionKeys = localStorageKeys.filter(ls => ls.indexOf(sessionKeyToFind) >= 0);
|
||||
if (dataKeys.length > 0 || sessionKeys.length > 0) {
|
||||
const zip = new JSZip();
|
||||
for (let i = 0; i < dataKeys.length; i++) {
|
||||
zip.file(dataKeys[i] + ".prsv", localStorage.getItem(dataKeys[i])!);
|
||||
}
|
||||
for (let i = 0; i < sessionKeys.length; i++) {
|
||||
zip.file(sessionKeys[i] + ".prsv", localStorage.getItem(sessionKeys[i])!);
|
||||
}
|
||||
zip.generateAsync({ type: "blob" }).then(content => {
|
||||
const url = URL.createObjectURL(content);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "pokerogue_saves.zip";
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
} else {
|
||||
return onFail(this.ERR_NO_SAVES);
|
||||
}
|
||||
});
|
||||
|
||||
this.externalPartyContainer.setAlpha(0);
|
||||
this.scene.tweens.add({
|
||||
targets: this.externalPartyContainer,
|
||||
|
@ -313,6 +313,11 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||
this.pokemonShinyNewIcon.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, this.scene.uiTheme));
|
||||
const newShinyOrVariant = ((newShiny & caughtAttr) === BigInt(0)) || ((newVariant & caughtAttr) === BigInt(0));
|
||||
this.pokemonShinyNewIcon.setVisible(!!newShinyOrVariant);
|
||||
} else if ((caughtAttr & DexAttr.NON_SHINY) === BigInt(0) && ((caughtAttr & DexAttr.SHINY) === DexAttr.SHINY)) { //If the player has *only* caught any shiny variant of this species, not a non-shiny
|
||||
this.pokemonShinyNewIcon.setVisible(true);
|
||||
this.pokemonShinyNewIcon.setText("(+)");
|
||||
this.pokemonShinyNewIcon.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme));
|
||||
this.pokemonShinyNewIcon.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, this.scene.uiTheme));
|
||||
} else {
|
||||
this.pokemonShinyNewIcon.setVisible(false);
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { hasTouchscreen, isMobile } from "#app/touch-controls";
|
||||
import { TextStyle, addTextObject } from "#app/ui/text";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import UiHandler from "#app/ui/ui-handler";
|
||||
import MessageUiHandler from "#app/ui/message-ui-handler";
|
||||
import { addWindow } from "#app/ui/ui-theme";
|
||||
import { ScrollBar } from "#app/ui/scroll-bar";
|
||||
import { Button } from "#enums/buttons";
|
||||
@ -15,9 +14,10 @@ import i18next from "i18next";
|
||||
/**
|
||||
* Abstract class for handling UI elements related to settings.
|
||||
*/
|
||||
export default class AbstractSettingsUiHandler extends UiHandler {
|
||||
export default class AbstractSettingsUiHandler extends MessageUiHandler {
|
||||
private settingsContainer: Phaser.GameObjects.Container;
|
||||
private optionsContainer: Phaser.GameObjects.Container;
|
||||
private messageBoxContainer: Phaser.GameObjects.Container;
|
||||
private navigationContainer: NavigationMenu;
|
||||
|
||||
private scrollCursor: number;
|
||||
@ -135,6 +135,23 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
||||
this.scrollBar = new ScrollBar(this.scene, this.optionsBg.width - 9, this.optionsBg.y + 5, 4, this.optionsBg.height - 11, this.rowsToDisplay);
|
||||
this.scrollBar.setTotalRows(this.settings.length);
|
||||
|
||||
// Two-lines message box
|
||||
this.messageBoxContainer = this.scene.add.container(0, this.scene.scaledCanvas.height);
|
||||
this.messageBoxContainer.setName("settings-message-box");
|
||||
this.messageBoxContainer.setVisible(false);
|
||||
|
||||
const settingsMessageBox = addWindow(this.scene, 0, -1, this.scene.scaledCanvas.width - 2, 48);
|
||||
settingsMessageBox.setOrigin(0, 1);
|
||||
this.messageBoxContainer.add(settingsMessageBox);
|
||||
|
||||
const messageText = addTextObject(this.scene, 8, -40, "", TextStyle.WINDOW, { maxLines: 2 });
|
||||
messageText.setWordWrapWidth(this.scene.game.canvas.width - 60);
|
||||
messageText.setName("settings-message");
|
||||
messageText.setOrigin(0, 0);
|
||||
|
||||
this.messageBoxContainer.add(messageText);
|
||||
this.message = messageText;
|
||||
|
||||
this.settingsContainer.add(this.optionsBg);
|
||||
this.settingsContainer.add(this.scrollBar);
|
||||
this.settingsContainer.add(this.navigationContainer);
|
||||
@ -144,6 +161,7 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
||||
this.settingsContainer.add(iconCancel);
|
||||
this.settingsContainer.add(actionText);
|
||||
this.settingsContainer.add(cancelText);
|
||||
this.settingsContainer.add(this.messageBoxContainer);
|
||||
|
||||
ui.add(this.settingsContainer);
|
||||
|
||||
@ -326,18 +344,16 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
||||
/**
|
||||
* Set the option cursor to the specified position.
|
||||
*
|
||||
* @param settingIndex - The index of the setting.
|
||||
* @param settingIndex - The index of the setting or -1 to change the current setting
|
||||
* @param cursor - The cursor position to set.
|
||||
* @param save - Whether to save the setting to local storage.
|
||||
* @returns `true` if the option cursor was set successfully.
|
||||
*/
|
||||
setOptionCursor(settingIndex: number, cursor: number, save?: boolean): boolean {
|
||||
const setting = this.settings[settingIndex];
|
||||
|
||||
if (setting.key === SettingKeys.Touch_Controls && cursor && hasTouchscreen() && isMobile()) {
|
||||
this.getUi().playError();
|
||||
return false;
|
||||
if (settingIndex === -1) {
|
||||
settingIndex = this.cursor + this.scrollCursor;
|
||||
}
|
||||
const setting = this.settings[settingIndex];
|
||||
|
||||
const lastCursor = this.optionCursors[settingIndex];
|
||||
|
||||
@ -352,10 +368,34 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
||||
newValueLabel.setShadowColor(this.getTextColor(TextStyle.SETTINGS_SELECTED, true));
|
||||
|
||||
if (save) {
|
||||
const saveSetting = () => {
|
||||
this.scene.gameData.saveSetting(setting.key, cursor);
|
||||
if (this.reloadSettings.includes(setting)) {
|
||||
if (setting.requireReload) {
|
||||
this.reloadRequired = true;
|
||||
}
|
||||
};
|
||||
|
||||
// For settings that ask for confirmation, display confirmation message and a Yes/No prompt before saving the setting
|
||||
if (setting.options[cursor].needConfirmation) {
|
||||
const confirmUpdateSetting = () => {
|
||||
this.scene.ui.revertMode();
|
||||
this.showText("");
|
||||
saveSetting();
|
||||
};
|
||||
const cancelUpdateSetting = () => {
|
||||
this.scene.ui.revertMode();
|
||||
this.showText("");
|
||||
// Put the cursor back to its previous position without saving or asking for confirmation again
|
||||
this.setOptionCursor(settingIndex, lastCursor, false);
|
||||
};
|
||||
|
||||
const confirmationMessage = setting.options[cursor].confirmationMessage ?? i18next.t("settings:defaultConfirmMessage");
|
||||
this.scene.ui.showText(confirmationMessage, null, () => {
|
||||
this.scene.ui.setOverlayMode(Mode.CONFIRM, confirmUpdateSetting, cancelUpdateSetting, null, null, 1, 750);
|
||||
});
|
||||
} else {
|
||||
saveSetting();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -421,4 +461,9 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
||||
}
|
||||
this.cursorObj = null;
|
||||
}
|
||||
|
||||
override showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
|
||||
this.messageBoxContainer.setVisible(!!text?.length);
|
||||
super.showText(text, delay, callback, callbackDelay, prompt, promptDelay);
|
||||
}
|
||||
}
|
||||
|