Release 1.5.0

Merge pull request #5177 from pagefaultgames/beta
This commit is contained in:
damocleas 2025-01-27 19:03:43 -05:00 committed by GitHub
commit 7ee573937e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
470 changed files with 13824 additions and 19444 deletions

View File

@ -46,6 +46,7 @@ export default [
"computed-property-spacing": ["error", "never" ], // Enforces consistent spacing inside computed property brackets "computed-property-spacing": ["error", "never" ], // Enforces consistent spacing inside computed property brackets
"space-infix-ops": ["error", { "int32Hint": false }], // Enforces spacing around infix operators "space-infix-ops": ["error", { "int32Hint": false }], // Enforces spacing around infix operators
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], // Disallows multiple empty lines "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], // Disallows multiple empty lines
"@typescript-eslint/consistent-type-imports": "error", // Enforces type-only imports wherever possible
} }
} }
] ]

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.4.3", "version": "1.5.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.4.3", "version": "1.5.0",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@material/material-color-utilities": "^0.2.7", "@material/material-color-utilities": "^0.2.7",

View File

@ -1,7 +1,7 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"private": true, "private": true,
"version": "1.4.3", "version": "1.5.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "vite", "start": "vite",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 B

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -4,6 +4,8 @@
"ffee52": "37d6de", "ffee52": "37d6de",
"debd29": "078a8f", "debd29": "078a8f",
"833100": "002112", "833100": "002112",
"830009": "23033b",
"189d87": "c2247b",
"ff7b73": "712f8f", "ff7b73": "712f8f",
"de4141": "3f1375", "de4141": "3f1375",
"ffbdbd": "a266b0", "ffbdbd": "a266b0",
@ -11,6 +13,7 @@
"107b6a": "9e1976", "107b6a": "9e1976",
"105241": "4f2800", "105241": "4f2800",
"83de7b": "a37707", "83de7b": "a37707",
"2e5529": "38001c",
"5a9c39": "705207", "5a9c39": "705207",
"20b49c": "de3592", "20b49c": "de3592",
"fdfdfd": "fdfdfd", "fdfdfd": "fdfdfd",
@ -21,14 +24,17 @@
"ffee52": "f75ea8", "ffee52": "f75ea8",
"debd29": "a30a66", "debd29": "a30a66",
"833100": "0b2e01", "833100": "0b2e01",
"830009": "154205",
"189d87": "f17f05",
"ff7b73": "9db042", "ff7b73": "9db042",
"de4141": "3c8227", "de4141": "3c8227",
"ffbdbd": "e7e385", "ffbdbd": "e7e385",
"101010": "101010", "101010": "101010",
"107b6a": "d44300", "107b6a": "d44300",
"105241": "030129", "105241": "381601",
"83de7b": "433d99", "83de7b": "80ced9",
"5a9c39": "19164f", "2e5519": "011c38",
"5a9c39": "446b94",
"20b49c": "fa8405", "20b49c": "fa8405",
"fdfdfd": "fdfdfd", "fdfdfd": "fdfdfd",
"5ad5c5": "faa405" "5ad5c5": "faa405"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,6 +1,7 @@
{ {
"1": { "1": {
"843100": "033b22", "843100": "033b22",
"830009": "23033b",
"ff7b73": "712f8f", "ff7b73": "712f8f",
"ffbdbd": "a266b0", "ffbdbd": "a266b0",
"debd29": "078a8f", "debd29": "078a8f",
@ -13,11 +14,13 @@
"5a9c3a": "b34952", "5a9c3a": "b34952",
"84de7b": "ff745e", "84de7b": "ff745e",
"5ad6c5": "f062a4", "5ad6c5": "f062a4",
"2e5519": "38001c",
"21b59c": "de3592", "21b59c": "de3592",
"ffffff": "ffffff" "ffffff": "ffffff"
}, },
"2": { "2": {
"843100": "420514", "843100": "420514",
"830009": "154205",
"ff7b73": "9db042", "ff7b73": "9db042",
"ffbdbd": "e7e385", "ffbdbd": "e7e385",
"debd29": "a30a66", "debd29": "a30a66",
@ -30,6 +33,7 @@
"5a9c3a": "446b94", "5a9c3a": "446b94",
"84de7b": "80ced9", "84de7b": "80ced9",
"5ad6c5": "faa405", "5ad6c5": "faa405",
"2e5519": "011c38",
"21b59c": "fa8405", "21b59c": "fa8405",
"ffffff": "ffffff" "ffffff": "ffffff"
} }

View File

@ -4,6 +4,7 @@
"3f4447": "466698", "3f4447": "466698",
"de3431": "3fca9f", "de3431": "3fca9f",
"f8f8f8": "a1e9f0", "f8f8f8": "a1e9f0",
"f4f4f4": "d7eff4",
"7b282e": "0e3e81", "7b282e": "0e3e81",
"6b1d1d": "206d74", "6b1d1d": "206d74",
"4ebdd9": "41a7b0", "4ebdd9": "41a7b0",
@ -11,7 +12,7 @@
"bfbfbf": "8cc7d4", "bfbfbf": "8cc7d4",
"ffb2bf": "b7e9ff", "ffb2bf": "b7e9ff",
"bf4c60": "4386df", "bf4c60": "4386df",
"fff0a6": "271f4c", "fff0a6": "208698",
"3e7acc": "6b4592", "3e7acc": "6b4592",
"18335c": "170738", "18335c": "170738",
"f2798d": "8dcfff", "f2798d": "8dcfff",
@ -25,6 +26,7 @@
"3f4447": "466698", "3f4447": "466698",
"de3431": "9ceec6", "de3431": "9ceec6",
"f8f8f8": "89d2b8", "f8f8f8": "89d2b8",
"f4f4f4": "d7eff4",
"7b282e": "152a5c", "7b282e": "152a5c",
"6b1d1d": "356e8d", "6b1d1d": "356e8d",
"4ebdd9": "2f6e74", "4ebdd9": "2f6e74",

View File

@ -835,7 +835,7 @@
"6713": [0, 1, 1], "6713": [0, 1, 1],
"8901": [1, 1, 1], "8901": [1, 1, 1],
"female": { "female": {
"3": [0, 2, 1], "3": [0, 1, 1],
"19": [0, 1, 1], "19": [0, 1, 1],
"20": [0, 1, 1], "20": [0, 1, 1],
"25": [0, 1, 1], "25": [0, 1, 1],
@ -869,6 +869,7 @@
"198": [0, 1, 1], "198": [0, 1, 1],
"203": [0, 1, 1], "203": [0, 1, 1],
"207": [0, 1, 1], "207": [0, 1, 1],
"212": [1, 1, 1],
"215": [0, 1, 1], "215": [0, 1, 1],
"217": [1, 1, 1], "217": [1, 1, 1],
"229": [0, 1, 1], "229": [0, 1, 1],
@ -1778,6 +1779,7 @@
"198": [0, 1, 1], "198": [0, 1, 1],
"203": [0, 1, 1], "203": [0, 1, 1],
"207": [0, 1, 1], "207": [0, 1, 1],
"212": [1, 1, 1],
"215": [0, 1, 1], "215": [0, 1, 1],
"217": [1, 1, 1], "217": [1, 1, 1],
"229": [0, 1, 1], "229": [0, 1, 1],

View File

@ -1,6 +1,7 @@
{ {
"1": { "1": {
"833100": "180136", "833100": "180136",
"830009": "23033b",
"bd6a31": "012729", "bd6a31": "012729",
"ffee52": "37d6de", "ffee52": "37d6de",
"debd29": "078a8f", "debd29": "078a8f",
@ -8,15 +9,18 @@
"de4141": "3f1375", "de4141": "3f1375",
"ff7b73": "712f8f", "ff7b73": "712f8f",
"ffbdbd": "a266b0", "ffbdbd": "a266b0",
"5a9c39": "705207", "e8a3a3": "91579e",
"105241": "4f2800", "5a9c39": "b34952",
"83de7b": "a37707", "105241": "190038",
"2e5519": "38001c",
"83de7b": "ff745e",
"107b6a": "b80479", "107b6a": "b80479",
"20b49c": "de3592", "20b49c": "de3592",
"fdfdfd": "fdfdfd" "fdfdfd": "fdfdfd"
}, },
"2": { "2": {
"833100": "0b2e01", "833100": "0b2e01",
"830009": "154205",
"bd6a31": "420514", "bd6a31": "420514",
"ffee52": "f75ea8", "ffee52": "f75ea8",
"debd29": "a30a66", "debd29": "a30a66",
@ -24,9 +28,11 @@
"de4141": "3c8227", "de4141": "3c8227",
"ff7b73": "9db042", "ff7b73": "9db042",
"ffbdbd": "e7e385", "ffbdbd": "e7e385",
"5a9c39": "19164f", "e8a3a3": "ced76f",
"105241": "030129", "5a9c39": "446b94",
"83de7b": "433d99", "105241": "381601",
"2e5519": "011c38",
"83de7b": "80ced9",
"107b6a": "d15d04", "107b6a": "d15d04",
"20b49c": "fa8405", "20b49c": "fa8405",
"fdfdfd": "fdfdfd" "fdfdfd": "fdfdfd"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,6 +1,7 @@
{ {
"1": { "1": {
"843100": "033b22", "843100": "033b22",
"830009": "23033b",
"ffbdbd": "a266b0", "ffbdbd": "a266b0",
"ff7b73": "712f8f", "ff7b73": "712f8f",
"debd29": "078a8f", "debd29": "078a8f",
@ -11,6 +12,7 @@
"105242": "190038", "105242": "190038",
"107b6b": "c21f7e", "107b6b": "c21f7e",
"5a9c3a": "b34952", "5a9c3a": "b34952",
"2e5519": "38001c",
"5ad6c5": "f062a4", "5ad6c5": "f062a4",
"21b59c": "de3592", "21b59c": "de3592",
"84de7b": "ff745e", "84de7b": "ff745e",
@ -18,6 +20,7 @@
}, },
"2": { "2": {
"843100": "420514", "843100": "420514",
"830009": "154205",
"ffbdbd": "e7e385", "ffbdbd": "e7e385",
"ff7b73": "9db042", "ff7b73": "9db042",
"debd29": "a30a66", "debd29": "a30a66",
@ -25,7 +28,8 @@
"de4242": "3c8227", "de4242": "3c8227",
"101010": "101010", "101010": "101010",
"ffef52": "f75ea8", "ffef52": "f75ea8",
"105242": "001a33", "105242": "381601",
"2e5519": "011c38",
"107b6b": "d15d04", "107b6b": "d15d04",
"5a9c3a": "446b94", "5a9c3a": "446b94",
"5ad6c5": "faa405", "5ad6c5": "faa405",

View File

@ -9,7 +9,7 @@
"de62a4": "ffc668", "de62a4": "ffc668",
"4a83a4": "387fa7", "4a83a4": "387fa7",
"314a62": "244260", "314a62": "244260",
"70bbb4": "f8d371", "548e88": "2d60bb",
"a4295a": "cc762f" "a4295a": "cc762f"
}, },
"1": { "1": {
@ -22,7 +22,7 @@
"de62a4": "ffdf90", "de62a4": "ffdf90",
"4a83a4": "a1c8db", "4a83a4": "a1c8db",
"314a62": "7396b4", "314a62": "7396b4",
"70bbb4": "70bbb4", "548e88": "a9c0c6",
"a4295a": "e28c27" "a4295a": "e28c27"
}, },
"2": { "2": {
@ -35,7 +35,7 @@
"de62a4": "e25038", "de62a4": "e25038",
"4a83a4": "e6aa47", "4a83a4": "e6aa47",
"314a62": "b56f2a", "314a62": "b56f2a",
"70bbb4": "f8d371", "548e88": "e0b544",
"a4295a": "a62a21" "a4295a": "a62a21"
} }
} }

View File

@ -0,0 +1,41 @@
{
"0": {
"632929": "215a2d",
"f76b6b": "8cce73",
"a52929": "2f794e",
"101010": "101010",
"d63a3a": "4a9c53",
"9494a5": "9494a5",
"ffffff": "ffffff",
"b5b5ce": "b5b5ce",
"3a3a4a": "3a3a4a",
"9c6b21": "9c6b21",
"dec510": "dec510"
},
"1": {
"632929": "2f2962",
"f76b6b": "639cf7",
"a52929": "29429c",
"101010": "101010",
"d63a3a": "4263ef",
"9494a5": "6262a4",
"ffffff": "ffffff",
"b5b5ce": "b5b5ce",
"3a3a4a": "3c3c50",
"9c6b21": "131387",
"dec510": "10bdde"
},
"2": {
"632929": "645117",
"f76b6b": "c59f29",
"a52929": "b88619",
"101010": "101010",
"d63a3a": "ffca2a",
"9494a5": "3c4543",
"ffffff": "ffffff",
"b5b5ce": "b5b5ce",
"3a3a4a": "282d2c",
"9c6b21": "9c6b21",
"dec510": "dec510"
}
}

View File

@ -1,6 +1,7 @@
{ {
"1": { "1": {
"843100": "033b22", "843100": "033b22",
"830009": "23033b",
"ffbdbd": "a266b0", "ffbdbd": "a266b0",
"ffef52": "37d6de", "ffef52": "37d6de",
"debd29": "078a8f", "debd29": "078a8f",
@ -10,6 +11,7 @@
"101010": "101010", "101010": "101010",
"105242": "190038", "105242": "190038",
"107b6b": "9e1976", "107b6b": "9e1976",
"2e5519": "38001c",
"5a9c3a": "b34952", "5a9c3a": "b34952",
"5ad6c5": "f062a4", "5ad6c5": "f062a4",
"21b59c": "de3592", "21b59c": "de3592",
@ -18,6 +20,7 @@
}, },
"2": { "2": {
"843100": "420514", "843100": "420514",
"830009": "154205",
"ffbdbd": "e7e385", "ffbdbd": "e7e385",
"ffef52": "f75ea8", "ffef52": "f75ea8",
"debd29": "a30a66", "debd29": "a30a66",
@ -27,6 +30,7 @@
"101010": "101010", "101010": "101010",
"105242": "381601", "105242": "381601",
"107b6b": "d15d04", "107b6b": "d15d04",
"2e5519": "011c38",
"5a9c3a": "446b94", "5a9c3a": "446b94",
"5ad6c5": "faa405", "5ad6c5": "faa405",
"21b59c": "fa8405", "21b59c": "fa8405",

View File

@ -4,6 +4,7 @@
"3f4447": "466698", "3f4447": "466698",
"de3431": "3fca9f", "de3431": "3fca9f",
"f8f8f8": "a1e9f0", "f8f8f8": "a1e9f0",
"f4f4f4": "d7effa",
"7b282e": "0e3e81", "7b282e": "0e3e81",
"6b1d1d": "206d74", "6b1d1d": "206d74",
"4ebdd9": "41a7b0", "4ebdd9": "41a7b0",
@ -25,6 +26,7 @@
"3f4447": "466698", "3f4447": "466698",
"de3431": "9ceec6", "de3431": "9ceec6",
"f8f8f8": "89d2b8", "f8f8f8": "89d2b8",
"f4f4f4": "d7effa",
"7b282e": "152a5c", "7b282e": "152a5c",
"6b1d1d": "356e8d", "6b1d1d": "356e8d",
"4ebdd9": "2f6e74", "4ebdd9": "2f6e74",

View File

@ -8,7 +8,7 @@
"0f0f0f": "0f0f0f", "0f0f0f": "0f0f0f",
"314a62": "244260", "314a62": "244260",
"621841": "71370f", "621841": "71370f",
"70bbb4": "f8d371", "548e88": "2d60bb",
"de62a4": "ffc668", "de62a4": "ffc668",
"a4295a": "cc762f" "a4295a": "cc762f"
}, },
@ -21,7 +21,7 @@
"0f0f0f": "0f0f0f", "0f0f0f": "0f0f0f",
"314a62": "7396b4", "314a62": "7396b4",
"621841": "7b3c08", "621841": "7b3c08",
"70bbb4": "70bbb4", "548e88": "a9c0c6",
"de62a4": "ffdf90", "de62a4": "ffdf90",
"a4295a": "e28c27" "a4295a": "e28c27"
}, },
@ -34,7 +34,7 @@
"0f0f0f": "0f0f0f", "0f0f0f": "0f0f0f",
"314a62": "b56f2a", "314a62": "b56f2a",
"621841": "5a0a05", "621841": "5a0a05",
"70bbb4": "f8d371", "548e88": "e0b544",
"de62a4": "e25038", "de62a4": "e25038",
"a4295a": "a62a21" "a4295a": "a62a21"
} }

View File

@ -0,0 +1,41 @@
{
"0": {
"632929": "215a2d",
"f76b6b": "8cce73",
"101010": "101010",
"3a3a4a": "3a3a4a",
"ffffff": "ffffff",
"d63a3a": "4a9c53",
"b5b5ce": "b5b5ce",
"9494a5": "9494a5",
"a52929": "2f794e",
"dec510": "dec510",
"9c6b21": "9c6b21"
},
"1": {
"632929": "2f2962",
"f76b6b": "639cf7",
"101010": "101010",
"3a3a4a": "3c3c50",
"ffffff": "ffffff",
"d63a3a": "4263ef",
"b5b5ce": "b5b5ce",
"9494a5": "6262a4",
"a52929": "29429c",
"dec510": "10bdde",
"9c6b21": "131387"
},
"2": {
"632929": "645117",
"f76b6b": "c59f29",
"101010": "101010",
"3a3a4a": "282d2c",
"ffffff": "ffffff",
"d63a3a": "ffca2a",
"b5b5ce": "b5b5ce",
"9494a5": "3c4543",
"a52929": "b88619",
"dec510": "dec510",
"9c6b21": "9c6b21"
}
}

View File

@ -1,19 +1,41 @@
{ {
"1": {
"843100": "033b22",
"830009": "23033b",
"ffbdbd": "a266b0",
"ffef52": "37d6de",
"debd29": "078a8f",
"ff7b73": "712f8f",
"bd6b31": "168a69",
"de4242": "3f1375",
"101010": "101010",
"105242": "190038",
"107b6b": "9e1976",
"2e5519": "38001c",
"5a9c3a": "b34952",
"5ad6c5": "f062a4",
"21b59c": "de3592",
"84de7b": "ff745e",
"ffffff": "ffffff"
},
"2": { "2": {
"843100": "420514", "843100": "420514",
"ff7b73": "9db042", "830009": "154205",
"ffbdbd": "e7e385", "ffbdbd": "e7e385",
"ffef52": "f75ea8", "ffef52": "f75ea8",
"debd29": "a30a66", "debd29": "a30a66",
"ff7b73": "9db042",
"bd6b31": "852a41", "bd6b31": "852a41",
"de4242": "3c8227", "de4242": "3c8227",
"101010": "101010", "101010": "101010",
"105242": "381601", "105242": "381601",
"107b6b": "d44300", "107b6b": "d15d04",
"2e5519": "011c38",
"5a9c3a": "446b94", "5a9c3a": "446b94",
"84de7b": "80ced9",
"5ad6c5": "faa405", "5ad6c5": "faa405",
"21b59c": "fa8405", "21b59c": "fa8405",
"ffffff": "ffffff" "84de7b": "80ced9",
"ffffff": "ffffff",
"2f561a": "011b34"
} }
} }

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 B

After

Width:  |  Height:  |  Size: 985 B

@ -1 +1 @@
Subproject commit 2e03bc8f2736269bfa365faad587c3ec54a37621 Subproject commit 5ef993b95fa8248adc0fb7d9489baccf546bf8e3

View File

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

View File

@ -1,4 +1,4 @@
import { TOptions } from "i18next"; import type { TOptions } from "i18next";
// Module declared to make referencing keys in the localization files type-safe. // Module declared to make referencing keys in the localization files type-safe.
declare module "i18next" { declare module "i18next" {

View File

@ -1,13 +1,18 @@
import Phaser from "phaser"; import Phaser from "phaser";
import UI from "#app/ui/ui"; import UI from "#app/ui/ui";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils"; import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
import type PokemonSpecies from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import type { Constructor } from "#app/utils";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier"; import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "./modifier/modifier";
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, ModifierBar, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, RememberMoveModifier, TerastallizeModifier } from "./modifier/modifier";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims";
import { Phase } from "#app/phase"; import type { Phase } from "#app/phase";
import { initGameSpeed } from "#app/system/game-speed"; import { initGameSpeed } from "#app/system/game-speed";
import { Arena, ArenaBase } from "#app/field/arena"; import { Arena, ArenaBase } from "#app/field/arena";
import { GameData } from "#app/system/game-data"; import { GameData } from "#app/system/game-data";
@ -17,26 +22,32 @@ import { MusicPreference } from "#app/system/settings/settings";
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import AbilityBar from "#app/ui/ability-bar"; import AbilityBar from "#app/ui/ability-bar";
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr, PostItemLostAbAttr } from "#app/data/ability"; import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr, PostItemLostAbAttr } from "#app/data/ability";
import Battle, { BattleType, FixedBattleConfig } from "#app/battle"; import type { FixedBattleConfig } from "#app/battle";
import { GameMode, GameModes, getGameMode } from "#app/game-mode"; import Battle, { BattleType } from "#app/battle";
import type { GameMode } from "#app/game-mode";
import { GameModes, getGameMode } from "#app/game-mode";
import FieldSpritePipeline from "#app/pipelines/field-sprite"; import FieldSpritePipeline from "#app/pipelines/field-sprite";
import SpritePipeline from "#app/pipelines/sprite"; import SpritePipeline from "#app/pipelines/sprite";
import PartyExpBar from "#app/ui/party-exp-bar"; import PartyExpBar from "#app/ui/party-exp-bar";
import { trainerConfigs, TrainerSlot } from "#app/data/trainer-config"; import type { TrainerSlot } from "#app/data/trainer-config";
import { trainerConfigs } from "#app/data/trainer-config";
import Trainer, { TrainerVariant } from "#app/field/trainer"; import Trainer, { TrainerVariant } from "#app/field/trainer";
import TrainerData from "#app/system/trainer-data"; import type TrainerData from "#app/system/trainer-data";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import PokeballTray from "#app/ui/pokeball-tray"; import PokeballTray from "#app/ui/pokeball-tray";
import InvertPostFX from "#app/pipelines/invert"; import InvertPostFX from "#app/pipelines/invert";
import { Achv, achvs, ModifierAchv, MoneyAchv } from "#app/system/achv"; import type { Achv } from "#app/system/achv";
import { Voucher, vouchers } from "#app/system/voucher"; import { achvs, ModifierAchv, MoneyAchv } from "#app/system/achv";
import type { Voucher } from "#app/system/voucher";
import { vouchers } from "#app/system/voucher";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import type UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
import { addUiThemeOverrides } from "#app/ui/ui-theme"; import { addUiThemeOverrides } from "#app/ui/ui-theme";
import PokemonData from "#app/system/pokemon-data"; import type PokemonData from "#app/system/pokemon-data";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms"; import type { SpeciesFormChange, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger } from "#app/data/pokemon-forms";
import { FormChangePhase } from "#app/phases/form-change-phase"; import { FormChangePhase } from "#app/phases/form-change-phase";
import { getTypeRgb } from "#app/data/type"; import { getTypeRgb } from "#app/data/type";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
@ -47,8 +58,9 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { biomeDepths, getBiomeName } from "#app/data/balance/biomes"; import { biomeDepths, getBiomeName } from "#app/data/balance/biomes";
import { SceneBase } from "#app/scene-base"; import { SceneBase } from "#app/scene-base";
import CandyBar from "#app/ui/candy-bar"; import CandyBar from "#app/ui/candy-bar";
import { Variant, variantColorCache, variantData, VariantSet } from "#app/data/variant"; import type { Variant, VariantSet } from "#app/data/variant";
import { Localizable } from "#app/interfaces/locales"; import { variantColorCache, variantData } from "#app/data/variant";
import type { Localizable } from "#app/interfaces/locales";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { InputsController } from "#app/inputs-controller"; import { InputsController } from "#app/inputs-controller";
import { UiInputs } from "#app/ui-inputs"; import { UiInputs } from "#app/ui-inputs";
@ -58,14 +70,14 @@ import { EaseType } from "#enums/ease-type";
import { BattleSpec } from "#enums/battle-spec"; import { BattleSpec } from "#enums/battle-spec";
import { BattleStyle } from "#enums/battle-style"; import { BattleStyle } from "#enums/battle-style";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { ExpNotification } from "#enums/exp-notification"; import type { ExpNotification } from "#enums/exp-notification";
import { MoneyFormat } from "#enums/money-format"; import { MoneyFormat } from "#enums/money-format";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { UiTheme } from "#enums/ui-theme"; import { UiTheme } from "#enums/ui-theme";
import { TimedEventManager } from "#app/timed-event-manager"; import { TimedEventManager } from "#app/timed-event-manager";
import { PokemonAnimType } from "#enums/pokemon-anim-type"; import type { PokemonAnimType } from "#enums/pokemon-anim-type";
import i18next from "i18next"; import i18next from "i18next";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { battleSpecDialogue } from "#app/data/dialogue"; import { battleSpecDialogue } from "#app/data/dialogue";
@ -92,7 +104,7 @@ import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { ExpPhase } from "#app/phases/exp-phase"; import { ExpPhase } from "#app/phases/exp-phase";
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
@ -100,6 +112,7 @@ import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters"; import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { initGlobalScene } from "#app/global-scene";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -330,6 +343,7 @@ export default class BattleScene extends SceneBase {
this.nextCommandPhaseQueue = []; this.nextCommandPhaseQueue = [];
this.eventManager = new TimedEventManager(); this.eventManager = new TimedEventManager();
this.updateGameInfo(); this.updateGameInfo();
initGlobalScene(this);
} }
loadPokemonAtlas(key: string, atlasPath: string, experimental?: boolean) { loadPokemonAtlas(key: string, atlasPath: string, experimental?: boolean) {
@ -349,40 +363,41 @@ export default class BattleScene extends SceneBase {
/** /**
* Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache} * Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache}
*/ */
loadPokemonVariantAssets(spriteKey: string, fileRoot: string, variant?: Variant) { public async loadPokemonVariantAssets(spriteKey: string, fileRoot: string, variant?: Variant): Promise<void> {
const useExpSprite = this.experimentalSprites && this.hasExpSprite(spriteKey); const useExpSprite = this.experimentalSprites && this.hasExpSprite(spriteKey);
if (useExpSprite) { if (useExpSprite) {
fileRoot = `exp/${fileRoot}`; fileRoot = `exp/${fileRoot}`;
} }
let variantConfig = variantData; let variantConfig = variantData;
fileRoot.split("/").map(p => variantConfig ? variantConfig = variantConfig[p] : null); fileRoot.split("/").map((p) => (variantConfig ? (variantConfig = variantConfig[p]) : null));
const variantSet = variantConfig as VariantSet; const variantSet = variantConfig as VariantSet;
if (variantSet && (variant !== undefined && variantSet[variant] === 1)) {
const populateVariantColors = (key: string): Promise<void> => { return new Promise<void>((resolve) => {
return new Promise(resolve => { if (variantSet && variant !== undefined && variantSet[variant] === 1) {
if (variantColorCache.hasOwnProperty(key)) { if (variantColorCache.hasOwnProperty(spriteKey)) {
return resolve(); return resolve();
} }
this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`).then(res => res.json()).then(c => { this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`)
variantColorCache[key] = c; .then((res) => res.json())
.then((c) => {
variantColorCache[spriteKey] = c;
resolve(); resolve();
}); });
}); } else {
}; resolve();
populateVariantColors(spriteKey); }
} });
} }
async preload() { async preload() {
if (DEBUG_RNG) { if (DEBUG_RNG) {
const scene = this;
const originalRealInRange = Phaser.Math.RND.realInRange; const originalRealInRange = Phaser.Math.RND.realInRange;
Phaser.Math.RND.realInRange = function (min: number, max: number): number { Phaser.Math.RND.realInRange = function (min: number, max: number): number {
const ret = originalRealInRange.apply(this, [ min, max ]); const ret = originalRealInRange.apply(this, [ min, max ]);
const args = [ "RNG", ++scene.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ]; const args = [ "RNG", ++this.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ];
args.push(`seed: ${scene.rngSeedOverride || scene.waveSeed || scene.seed}`); args.push(`seed: ${this.rngSeedOverride || this.waveSeed || this.seed}`);
if (scene.rngOffset) { if (this.rngOffset) {
args.push(`offset: ${scene.rngOffset}`); args.push(`offset: ${this.rngOffset}`);
} }
console.log(...args); console.log(...args);
return ret; return ret;
@ -397,12 +412,12 @@ export default class BattleScene extends SceneBase {
create() { create() {
this.scene.remove(LoadingScene.KEY); this.scene.remove(LoadingScene.KEY);
initGameSpeed.apply(this); initGameSpeed.apply(this);
this.inputController = new InputsController(this); this.inputController = new InputsController();
this.uiInputs = new UiInputs(this, this.inputController); this.uiInputs = new UiInputs(this.inputController);
this.gameData = new GameData(this); this.gameData = new GameData();
addUiThemeOverrides(this); addUiThemeOverrides();
this.load.setBaseURL(); this.load.setBaseURL();
@ -489,76 +504,76 @@ export default class BattleScene extends SceneBase {
this.modifiers = []; this.modifiers = [];
this.enemyModifiers = []; this.enemyModifiers = [];
this.modifierBar = new ModifierBar(this); this.modifierBar = new ModifierBar();
this.modifierBar.setName("modifier-bar"); this.modifierBar.setName("modifier-bar");
this.add.existing(this.modifierBar); this.add.existing(this.modifierBar);
uiContainer.add(this.modifierBar); uiContainer.add(this.modifierBar);
this.enemyModifierBar = new ModifierBar(this, true); this.enemyModifierBar = new ModifierBar(true);
this.enemyModifierBar.setName("enemy-modifier-bar"); this.enemyModifierBar.setName("enemy-modifier-bar");
this.add.existing(this.enemyModifierBar); this.add.existing(this.enemyModifierBar);
uiContainer.add(this.enemyModifierBar); uiContainer.add(this.enemyModifierBar);
this.charSprite = new CharSprite(this); this.charSprite = new CharSprite();
this.charSprite.setName("sprite-char"); this.charSprite.setName("sprite-char");
this.charSprite.setup(); this.charSprite.setup();
this.fieldUI.add(this.charSprite); this.fieldUI.add(this.charSprite);
this.pbTray = new PokeballTray(this, true); this.pbTray = new PokeballTray(true);
this.pbTray.setName("pb-tray"); this.pbTray.setName("pb-tray");
this.pbTray.setup(); this.pbTray.setup();
this.pbTrayEnemy = new PokeballTray(this, false); this.pbTrayEnemy = new PokeballTray(false);
this.pbTrayEnemy.setName("enemy-pb-tray"); this.pbTrayEnemy.setName("enemy-pb-tray");
this.pbTrayEnemy.setup(); this.pbTrayEnemy.setup();
this.fieldUI.add(this.pbTray); this.fieldUI.add(this.pbTray);
this.fieldUI.add(this.pbTrayEnemy); this.fieldUI.add(this.pbTrayEnemy);
this.abilityBar = new AbilityBar(this); this.abilityBar = new AbilityBar();
this.abilityBar.setName("ability-bar"); this.abilityBar.setName("ability-bar");
this.abilityBar.setup(); this.abilityBar.setup();
this.fieldUI.add(this.abilityBar); this.fieldUI.add(this.abilityBar);
this.partyExpBar = new PartyExpBar(this); this.partyExpBar = new PartyExpBar();
this.partyExpBar.setName("party-exp-bar"); this.partyExpBar.setName("party-exp-bar");
this.partyExpBar.setup(); this.partyExpBar.setup();
this.fieldUI.add(this.partyExpBar); this.fieldUI.add(this.partyExpBar);
this.candyBar = new CandyBar(this); this.candyBar = new CandyBar();
this.candyBar.setName("candy-bar"); this.candyBar.setName("candy-bar");
this.candyBar.setup(); this.candyBar.setup();
this.fieldUI.add(this.candyBar); this.fieldUI.add(this.candyBar);
this.biomeWaveText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO); this.biomeWaveText = addTextObject((this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO);
this.biomeWaveText.setName("text-biome-wave"); this.biomeWaveText.setName("text-biome-wave");
this.biomeWaveText.setOrigin(1, 0.5); this.biomeWaveText.setOrigin(1, 0.5);
this.fieldUI.add(this.biomeWaveText); this.fieldUI.add(this.biomeWaveText);
this.moneyText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.MONEY); this.moneyText = addTextObject((this.game.canvas.width / 6) - 2, 0, "", TextStyle.MONEY);
this.moneyText.setName("text-money"); this.moneyText.setName("text-money");
this.moneyText.setOrigin(1, 0.5); this.moneyText.setOrigin(1, 0.5);
this.fieldUI.add(this.moneyText); this.fieldUI.add(this.moneyText);
this.scoreText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" }); this.scoreText = addTextObject((this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" });
this.scoreText.setName("text-score"); this.scoreText.setName("text-score");
this.scoreText.setOrigin(1, 0.5); this.scoreText.setOrigin(1, 0.5);
this.fieldUI.add(this.scoreText); this.fieldUI.add(this.scoreText);
this.luckText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" }); this.luckText = addTextObject((this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" });
this.luckText.setName("text-luck"); this.luckText.setName("text-luck");
this.luckText.setOrigin(1, 0.5); this.luckText.setOrigin(1, 0.5);
this.luckText.setVisible(false); this.luckText.setVisible(false);
this.fieldUI.add(this.luckText); this.fieldUI.add(this.luckText);
this.luckLabelText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, i18next.t("common:luckIndicator"), TextStyle.PARTY, { fontSize: "54px" }); this.luckLabelText = addTextObject((this.game.canvas.width / 6) - 2, 0, i18next.t("common:luckIndicator"), TextStyle.PARTY, { fontSize: "54px" });
this.luckLabelText.setName("text-luck-label"); this.luckLabelText.setName("text-luck-label");
this.luckLabelText.setOrigin(1, 0.5); this.luckLabelText.setOrigin(1, 0.5);
this.luckLabelText.setVisible(false); this.luckLabelText.setVisible(false);
this.fieldUI.add(this.luckLabelText); this.fieldUI.add(this.luckLabelText);
this.arenaFlyout = new ArenaFlyout(this); this.arenaFlyout = new ArenaFlyout();
this.fieldUI.add(this.arenaFlyout); this.fieldUI.add(this.arenaFlyout);
this.fieldUI.moveBelow<Phaser.GameObjects.GameObject>(this.arenaFlyout, this.fieldOverlay); this.fieldUI.moveBelow<Phaser.GameObjects.GameObject>(this.arenaFlyout, this.fieldOverlay);
@ -567,9 +582,9 @@ export default class BattleScene extends SceneBase {
this.damageNumberHandler = new DamageNumberHandler(); this.damageNumberHandler = new DamageNumberHandler();
this.spriteSparkleHandler = new PokemonSpriteSparkleHandler(); this.spriteSparkleHandler = new PokemonSpriteSparkleHandler();
this.spriteSparkleHandler.setup(this); this.spriteSparkleHandler.setup();
this.pokemonInfoContainer = new PokemonInfoContainer(this, (this.game.canvas.width / 6) + 52, -(this.game.canvas.height / 6) + 66); this.pokemonInfoContainer = new PokemonInfoContainer((this.game.canvas.width / 6) + 52, -(this.game.canvas.height / 6) + 66);
this.pokemonInfoContainer.setup(); this.pokemonInfoContainer.setup();
this.fieldUI.add(this.pokemonInfoContainer); this.fieldUI.add(this.pokemonInfoContainer);
@ -578,13 +593,13 @@ export default class BattleScene extends SceneBase {
const loadPokemonAssets = []; const loadPokemonAssets = [];
this.arenaPlayer = new ArenaBase(this, true); this.arenaPlayer = new ArenaBase(true);
this.arenaPlayer.setName("arena-player"); this.arenaPlayer.setName("arena-player");
this.arenaPlayerTransition = new ArenaBase(this, true); this.arenaPlayerTransition = new ArenaBase(true);
this.arenaPlayerTransition.setName("arena-player-transition"); this.arenaPlayerTransition.setName("arena-player-transition");
this.arenaEnemy = new ArenaBase(this, false); this.arenaEnemy = new ArenaBase(false);
this.arenaEnemy.setName("arena-enemy"); this.arenaEnemy.setName("arena-enemy");
this.arenaNextEnemy = new ArenaBase(this, false); this.arenaNextEnemy = new ArenaBase(false);
this.arenaNextEnemy.setName("arena-next-enemy"); this.arenaNextEnemy.setName("arena-next-enemy");
this.arenaBgTransition.setVisible(false); this.arenaBgTransition.setVisible(false);
@ -625,7 +640,7 @@ export default class BattleScene extends SceneBase {
this.reset(false, false, true); this.reset(false, false, true);
const ui = new UI(this); const ui = new UI();
this.uiContainer.add(ui); this.uiContainer.add(ui);
this.ui = ui; this.ui = ui;
@ -636,12 +651,12 @@ export default class BattleScene extends SceneBase {
Promise.all([ Promise.all([
Promise.all(loadPokemonAssets), Promise.all(loadPokemonAssets),
initCommonAnims(this).then(() => loadCommonAnimAssets(this, true)), initCommonAnims().then(() => loadCommonAnimAssets(true)),
Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(this, m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)), Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(defaultMoves, true)),
this.initStarterColors() this.initStarterColors()
]).then(() => { ]).then(() => {
this.pushPhase(new LoginPhase(this)); this.pushPhase(new LoginPhase());
this.pushPhase(new TitlePhase(this)); this.pushPhase(new TitlePhase());
this.shiftPhase(); this.shiftPhase();
}); });
@ -911,7 +926,7 @@ export default class BattleScene extends SceneBase {
} }
addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon { addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon {
const pokemon = new PlayerPokemon(this, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource); const pokemon = new PlayerPokemon(species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource);
if (postProcess) { if (postProcess) {
postProcess(pokemon); postProcess(pokemon);
} }
@ -929,7 +944,7 @@ export default class BattleScene extends SceneBase {
boss = this.getEncounterBossSegments(this.currentBattle.waveIndex, level, species) > 1; boss = this.getEncounterBossSegments(this.currentBattle.waveIndex, level, species) > 1;
} }
const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, shinyLock, dataSource); const pokemon = new EnemyPokemon(species, level, trainerSlot, boss, shinyLock, dataSource);
if (Overrides.OPP_FUSION_OVERRIDE) { if (Overrides.OPP_FUSION_OVERRIDE) {
pokemon.generateFusionSpecies(); pokemon.generateFusionSpecies();
} }
@ -1064,7 +1079,7 @@ export default class BattleScene extends SceneBase {
/** /**
* Generates a random number using the current battle's seed * Generates a random number using the current battle's seed
* *
* This calls {@linkcode Battle.randSeedInt}(`scene`, {@linkcode range}, {@linkcode min}) in `src/battle.ts` * This calls {@linkcode Battle.randSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle.ts`
* which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts` * which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`
* *
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min} * @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
@ -1072,12 +1087,12 @@ export default class BattleScene extends SceneBase {
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1) * @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
*/ */
randBattleSeedInt(range: integer, min: integer = 0): integer { randBattleSeedInt(range: integer, min: integer = 0): integer {
return this.currentBattle?.randSeedInt(this, range, min); return this.currentBattle?.randSeedInt(range, min);
} }
reset(clearScene: boolean = false, clearData: boolean = false, reloadI18n: boolean = false): void { reset(clearScene: boolean = false, clearData: boolean = false, reloadI18n: boolean = false): void {
if (clearData) { if (clearData) {
this.gameData = new GameData(this); this.gameData = new GameData();
} }
this.gameMode = getGameMode(GameModes.CLASSIC); this.gameMode = getGameMode(GameModes.CLASSIC);
@ -1178,6 +1193,9 @@ export default class BattleScene extends SceneBase {
onComplete: () => { onComplete: () => {
this.clearPhaseQueue(); this.clearPhaseQueue();
this.ui.freeUIData();
this.uiContainer.remove(this.ui, true);
this.uiContainer.destroy();
this.children.removeAll(true); this.children.removeAll(true);
this.game.domContainer.innerHTML = ""; this.game.domContainer.innerHTML = "";
this.launchBattle(); this.launchBattle();
@ -1210,7 +1228,7 @@ export default class BattleScene extends SceneBase {
battleConfig = this.gameMode.getFixedBattle(newWaveIndex); battleConfig = this.gameMode.getFixedBattle(newWaveIndex);
newDouble = battleConfig.double; newDouble = battleConfig.double;
newBattleType = battleConfig.battleType; newBattleType = battleConfig.battleType;
this.executeWithSeedOffset(() => newTrainer = battleConfig?.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8); this.executeWithSeedOffset(() => newTrainer = battleConfig?.getTrainer(), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8);
if (newTrainer) { if (newTrainer) {
this.field.add(newTrainer); this.field.add(newTrainer);
} }
@ -1236,7 +1254,7 @@ export default class BattleScene extends SceneBase {
} }
} }
const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT); const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant); newTrainer = trainerData !== undefined ? trainerData.toTrainer() : new Trainer(trainerType, variant);
this.field.add(newTrainer); this.field.add(newTrainer);
} }
@ -1314,7 +1332,7 @@ export default class BattleScene extends SceneBase {
this.executeWithSeedOffset(() => { this.executeWithSeedOffset(() => {
this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble); this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble);
}, newWaveIndex << 3, this.waveSeed); }, newWaveIndex << 3, this.waveSeed);
this.currentBattle.incrementTurn(this); this.currentBattle.incrementTurn();
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) { if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
// Will generate the actual Mystery Encounter during NextEncounterPhase, to ensure it uses proper biome // Will generate the actual Mystery Encounter during NextEncounterPhase, to ensure it uses proper biome
@ -1342,7 +1360,7 @@ export default class BattleScene extends SceneBase {
playerField.forEach((pokemon, p) => { playerField.forEach((pokemon, p) => {
if (pokemon.isOnField()) { if (pokemon.isOnField()) {
this.pushPhase(new ReturnPhase(this, p)); this.pushPhase(new ReturnPhase(p));
} }
}); });
@ -1352,7 +1370,7 @@ export default class BattleScene extends SceneBase {
} }
if (!this.trainer.visible) { if (!this.trainer.visible) {
this.pushPhase(new ShowTrainerPhase(this)); this.pushPhase(new ShowTrainerPhase());
} }
} }
@ -1361,14 +1379,14 @@ export default class BattleScene extends SceneBase {
} }
if (!this.gameMode.hasRandomBiomes && !isNewBiome) { if (!this.gameMode.hasRandomBiomes && !isNewBiome) {
this.pushPhase(new NextEncounterPhase(this)); this.pushPhase(new NextEncounterPhase());
} else { } else {
this.pushPhase(new SelectBiomePhase(this)); this.pushPhase(new SelectBiomePhase());
this.pushPhase(new NewBiomeEncounterPhase(this)); this.pushPhase(new NewBiomeEncounterPhase());
const newMaxExpLevel = this.getMaxExpLevel(); const newMaxExpLevel = this.getMaxExpLevel();
if (newMaxExpLevel > maxExpLevel) { if (newMaxExpLevel > maxExpLevel) {
this.pushPhase(new LevelCapPhase(this)); this.pushPhase(new LevelCapPhase());
} }
} }
} }
@ -1377,7 +1395,7 @@ export default class BattleScene extends SceneBase {
} }
newArena(biome: Biome): Arena { newArena(biome: Biome): Arena {
this.arena = new Arena(this, biome, Biome[biome].toLowerCase()); this.arena = new Arena(biome, Biome[biome].toLowerCase());
this.eventTarget.dispatchEvent(new NewArenaEvent()); this.eventTarget.dispatchEvent(new NewArenaEvent());
this.arenaBg.pipelineData = { terrainColorRatio: this.arena.getBgTerrainColorRatioForBiome() }; this.arenaBg.pipelineData = { terrainColorRatio: this.arena.getBgTerrainColorRatioForBiome() };
@ -1424,6 +1442,8 @@ export default class BattleScene extends SceneBase {
return 0; return 0;
} }
const isEggPhase: boolean = [ "EggLapsePhase", "EggHatchPhase" ].includes(this.getCurrentPhase()?.constructor.name ?? "");
switch (species.speciesId) { switch (species.speciesId) {
case Species.UNOWN: case Species.UNOWN:
case Species.SHELLOS: case Species.SHELLOS:
@ -1455,7 +1475,7 @@ export default class BattleScene extends SceneBase {
} }
return Utils.randSeedInt(8); return Utils.randSeedInt(8);
case Species.EEVEE: case Species.EEVEE:
if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) { if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30 && !isEggPhase) {
return 0; // No Partner Eevee for Wave 12 Preschoolers return 0; // No Partner Eevee for Wave 12 Preschoolers
} }
return Utils.randSeedInt(2); return Utils.randSeedInt(2);
@ -1483,7 +1503,7 @@ export default class BattleScene extends SceneBase {
return 0; return 0;
case Species.GIMMIGHOUL: case Species.GIMMIGHOUL:
// Chest form can only be found in Mysterious Chest Encounter, if this is a game mode with MEs // Chest form can only be found in Mysterious Chest Encounter, if this is a game mode with MEs
if (this.gameMode.hasMysteryEncounters) { if (this.gameMode.hasMysteryEncounters && !isEggPhase) {
return 1; // Wandering form return 1; // Wandering form
} else { } else {
return Utils.randSeedInt(species.forms.length); return Utils.randSeedInt(species.forms.length);
@ -1791,7 +1811,7 @@ export default class BattleScene extends SceneBase {
} }
updateUIPositions(): void { updateUIPositions(): void {
const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible(this)).length; const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible()).length;
const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y; const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y;
this.biomeWaveText.setY( this.biomeWaveText.setY(
-(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0) + (biomeWaveTextHeight / 2) -(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0) + (biomeWaveTextHeight / 2)
@ -1823,8 +1843,10 @@ export default class BattleScene extends SceneBase {
this.currentBattle.battleScore += Math.ceil(scoreIncrease); this.currentBattle.battleScore += Math.ceil(scoreIncrease);
} }
getMaxExpLevel(ignoreLevelCap?: boolean): integer { getMaxExpLevel(ignoreLevelCap: boolean = false): integer {
if (ignoreLevelCap) { if (Overrides.LEVEL_CAP_OVERRIDE > 0) {
return Overrides.LEVEL_CAP_OVERRIDE;
} else if (ignoreLevelCap || Overrides.LEVEL_CAP_OVERRIDE < 0) {
return Number.MAX_SAFE_INTEGER; return Number.MAX_SAFE_INTEGER;
} }
const waveIndex = Math.ceil((this.currentBattle?.waveIndex || 1) / 10) * 10; const waveIndex = Math.ceil((this.currentBattle?.waveIndex || 1) / 10) * 10;
@ -1850,7 +1872,7 @@ export default class BattleScene extends SceneBase {
generateRandomBiome(waveIndex: integer): Biome { generateRandomBiome(waveIndex: integer): Biome {
const relWave = waveIndex % 250; const relWave = waveIndex % 250;
const biomes = Utils.getEnumValues(Biome).slice(1, Utils.getEnumValues(Biome).filter(b => b >= 40).length * -1); const biomes = Utils.getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
const maxDepth = biomeDepths[Biome.END][0] - 2; const maxDepth = biomeDepths[Biome.END][0] - 2;
const depthWeights = new Array(maxDepth + 1).fill(null) const depthWeights = new Array(maxDepth + 1).fill(null)
.map((_, i: integer) => ((1 - Math.min(Math.abs((i / (maxDepth - 1)) - (relWave / 250)) + 0.25, 1)) / 0.75) * 250); .map((_, i: integer) => ((1 - Math.min(Math.abs((i / (maxDepth - 1)) - (relWave / 250)) + 0.25, 1)) / 0.75) * 250);
@ -1863,9 +1885,9 @@ export default class BattleScene extends SceneBase {
const randInt = Utils.randSeedInt(totalWeight); const randInt = Utils.randSeedInt(totalWeight);
for (const biome of biomes) { for (let i = 0; i < biomes.length; i++) {
if (randInt < biomeThresholds[biome]) { if (randInt < biomeThresholds[i]) {
return biome; return biomes[i];
} }
} }
@ -1878,7 +1900,7 @@ export default class BattleScene extends SceneBase {
playBgm(bgmName?: string, fadeOut?: boolean): void { playBgm(bgmName?: string, fadeOut?: boolean): void {
if (bgmName === undefined) { if (bgmName === undefined) {
bgmName = this.currentBattle?.getBgmOverride(this) || this.arena?.bgm; bgmName = this.currentBattle?.getBgmOverride() || this.arena?.bgm;
} }
if (this.bgm && bgmName === this.bgm.key) { if (this.bgm && bgmName === this.bgm.key) {
if (!this.bgm.isPlaying) { if (!this.bgm.isPlaying) {
@ -2497,7 +2519,7 @@ export default class BattleScene extends SceneBase {
* @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue * @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue
*/ */
queueMessage(message: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null, defer?: boolean | null) { queueMessage(message: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null, defer?: boolean | null) {
const phase = new MessagePhase(this, message, callbackDelay, prompt, promptDelay); const phase = new MessagePhase(message, callbackDelay, prompt, promptDelay);
if (!defer) { if (!defer) {
// adds to the end of PhaseQueuePrepend // adds to the end of PhaseQueuePrepend
this.unshiftPhase(phase); this.unshiftPhase(phase);
@ -2515,7 +2537,7 @@ export default class BattleScene extends SceneBase {
this.phaseQueue.push(...this.nextCommandPhaseQueue); this.phaseQueue.push(...this.nextCommandPhaseQueue);
this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length); this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length);
} }
this.phaseQueue.push(new TurnInitPhase(this)); this.phaseQueue.push(new TurnInitPhase());
} }
addMoney(amount: integer): void { addMoney(amount: integer): void {
@ -2546,7 +2568,7 @@ export default class BattleScene extends SceneBase {
if (modifier instanceof TerastallizeModifier) { if (modifier instanceof TerastallizeModifier) {
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId))); modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId)));
} }
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) { if ((modifier as PersistentModifier).add(this.modifiers, !!virtual)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) { if (pokemon) {
@ -2627,7 +2649,7 @@ export default class BattleScene extends SceneBase {
if (modifier instanceof TerastallizeModifier) { if (modifier instanceof TerastallizeModifier) {
modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId, false))); modifiersToRemove.push(...(this.findModifiers(m => m instanceof TerastallizeModifier && m.pokemonId === modifier.pokemonId, false)));
} }
if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) { if ((modifier as PersistentModifier).add(this.enemyModifiers, false)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
const pokemon = this.getPokemonById(modifier.pokemonId); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) { if (pokemon) {
@ -2662,7 +2684,7 @@ export default class BattleScene extends SceneBase {
*/ */
tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, playSound: boolean, transferQuantity: number = 1, instant?: boolean, ignoreUpdate?: boolean, itemLost: boolean = true): Promise<boolean> { tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, playSound: boolean, transferQuantity: number = 1, instant?: boolean, ignoreUpdate?: boolean, itemLost: boolean = true): Promise<boolean> {
return new Promise(resolve => { return new Promise(resolve => {
const source = itemModifier.pokemonId ? itemModifier.getPokemon(target.scene) : null; const source = itemModifier.pokemonId ? itemModifier.getPokemon() : null;
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
Utils.executeIf(!!source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source! /* checked in condition*/, cancelled)).then(() => { Utils.executeIf(!!source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source! /* checked in condition*/, cancelled)).then(() => {
if (cancelled.value) { if (cancelled.value) {
@ -2670,11 +2692,11 @@ export default class BattleScene extends SceneBase {
} }
const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier; const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier;
newItemModifier.pokemonId = target.id; newItemModifier.pokemonId = target.id;
const matchingModifier = target.scene.findModifier(m => m instanceof PokemonHeldItemModifier const matchingModifier = this.findModifier(m => m instanceof PokemonHeldItemModifier
&& (m as PokemonHeldItemModifier).matchType(itemModifier) && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier; && (m as PokemonHeldItemModifier).matchType(itemModifier) && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier;
let removeOld = true; let removeOld = true;
if (matchingModifier) { if (matchingModifier) {
const maxStackCount = matchingModifier.getMaxStackCount(target.scene); const maxStackCount = matchingModifier.getMaxStackCount();
if (matchingModifier.stackCount >= maxStackCount) { if (matchingModifier.stackCount >= maxStackCount) {
return resolve(false); return resolve(false);
} }
@ -2791,7 +2813,7 @@ export default class BattleScene extends SceneBase {
count = Math.max(count, Math.floor(chances / 2)); count = Math.max(count, Math.floor(chances / 2));
} }
getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance) getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance)
.map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this)); .map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false));
} }
return true; return true;
}); });
@ -2815,7 +2837,7 @@ export default class BattleScene extends SceneBase {
* @param pokemon - If specified, only removes held items from that {@linkcode Pokemon} * @param pokemon - If specified, only removes held items from that {@linkcode Pokemon}
*/ */
clearEnemyHeldItemModifiers(pokemon?: Pokemon): void { clearEnemyHeldItemModifiers(pokemon?: Pokemon): void {
const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier && (!pokemon || m.getPokemon(this) === pokemon)); const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier && (!pokemon || m.getPokemon() === pokemon));
for (const m of modifiersToRemove) { for (const m of modifiersToRemove) {
this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1); this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1);
} }
@ -2864,9 +2886,7 @@ export default class BattleScene extends SceneBase {
updatePartyForModifiers(party: Pokemon[], instant?: boolean): Promise<void> { updatePartyForModifiers(party: Pokemon[], instant?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
Promise.allSettled(party.map(p => { Promise.allSettled(party.map(p => {
if (p.scene) { p.calculateStats();
p.calculateStats();
}
return p.updateInfo(instant); return p.updateInfo(instant);
})).then(() => resolve()); })).then(() => resolve());
}); });
@ -2929,15 +2949,14 @@ export default class BattleScene extends SceneBase {
/** /**
* Apply all modifiers that match `modifierType` in a random order * Apply all modifiers that match `modifierType` in a random order
* @param scene {@linkcode BattleScene} used to randomize the order of modifiers
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier} * @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true` * @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @param ...args The list of arguments needed to invoke `modifierType.apply` * @param ...args The list of arguments needed to invoke `modifierType.apply`
* @returns the list of all modifiers that matched `modifierType` and were applied. * @returns the list of all modifiers that matched `modifierType` and were applied.
*/ */
applyShuffledModifiers<T extends PersistentModifier>(scene: BattleScene, modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] { applyShuffledModifiers<T extends PersistentModifier>(modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] {
let modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args)); let modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
scene.executeWithSeedOffset(() => { this.executeWithSeedOffset(() => {
const shuffleModifiers = mods => { const shuffleModifiers = mods => {
if (mods.length < 1) { if (mods.length < 1) {
return mods; return mods;
@ -2946,7 +2965,7 @@ export default class BattleScene extends SceneBase {
return [ mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand)) ]; return [ mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand)) ];
}; };
modifiers = shuffleModifiers(modifiers); modifiers = shuffleModifiers(modifiers);
}, scene.currentBattle.turn << 4, scene.waveSeed); }, this.currentBattle.turn << 4, this.waveSeed);
return this.applyModifiersInternal(modifiers, player, args); return this.applyModifiersInternal(modifiers, player, args);
} }
@ -3016,9 +3035,9 @@ export default class BattleScene extends SceneBase {
if (matchingFormChange) { if (matchingFormChange) {
let phase: Phase; let phase: Phase;
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) { if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) {
phase = new FormChangePhase(this, pokemon, matchingFormChange, modal); phase = new FormChangePhase(pokemon, matchingFormChange, modal);
} else { } else {
phase = new QuietFormChangePhase(this, pokemon, matchingFormChange); phase = new QuietFormChangePhase(pokemon, matchingFormChange);
} }
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) { if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) {
this.overridePhase(phase); this.overridePhase(phase);
@ -3035,7 +3054,7 @@ export default class BattleScene extends SceneBase {
} }
triggerPokemonBattleAnim(pokemon: Pokemon, battleAnimType: PokemonAnimType, fieldAssets?: Phaser.GameObjects.Sprite[], delayed: boolean = false): boolean { triggerPokemonBattleAnim(pokemon: Pokemon, battleAnimType: PokemonAnimType, fieldAssets?: Phaser.GameObjects.Sprite[], delayed: boolean = false): boolean {
const phase: Phase = new PokemonAnimPhase(this, battleAnimType, pokemon, fieldAssets); const phase: Phase = new PokemonAnimPhase(battleAnimType, pokemon, fieldAssets);
if (delayed) { if (delayed) {
this.pushPhase(phase); this.pushPhase(phase);
} else { } else {
@ -3053,7 +3072,7 @@ export default class BattleScene extends SceneBase {
validateAchv(achv: Achv, args?: unknown[]): boolean { validateAchv(achv: Achv, args?: unknown[]): boolean {
if ((!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE) if ((!this.gameData.achvUnlocks.hasOwnProperty(achv.id) || Overrides.ACHIEVEMENTS_REUNLOCK_OVERRIDE)
&& achv.validate(this, args)) { && achv.validate(args)) {
this.gameData.achvUnlocks[achv.id] = new Date().getTime(); this.gameData.achvUnlocks[achv.id] = new Date().getTime();
this.ui.achvBar.showAchv(achv); this.ui.achvBar.showAchv(achv);
if (vouchers.hasOwnProperty(achv.id)) { if (vouchers.hasOwnProperty(achv.id)) {
@ -3066,7 +3085,7 @@ export default class BattleScene extends SceneBase {
} }
validateVoucher(voucher: Voucher, args?: unknown[]): boolean { validateVoucher(voucher: Voucher, args?: unknown[]): boolean {
if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(this, args)) { if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(args)) {
this.gameData.voucherUnlocks[voucher.id] = new Date().getTime(); this.gameData.voucherUnlocks[voucher.id] = new Date().getTime();
this.ui.achvBar.showAchv(voucher); this.ui.achvBar.showAchv(voucher);
this.gameData.voucherCounts[voucher.voucherType]++; this.gameData.voucherCounts[voucher.voucherType]++;
@ -3139,9 +3158,9 @@ export default class BattleScene extends SceneBase {
this.currentBattle.double = true; this.currentBattle.double = true;
const availablePartyMembers = this.getPlayerParty().filter((p) => p.isAllowedInBattle()); const availablePartyMembers = this.getPlayerParty().filter((p) => p.isAllowedInBattle());
if (availablePartyMembers.length > 1) { if (availablePartyMembers.length > 1) {
this.pushPhase(new ToggleDoublePositionPhase(this, true)); this.pushPhase(new ToggleDoublePositionPhase(true));
if (!availablePartyMembers[1].isOnField()) { if (!availablePartyMembers[1].isOnField()) {
this.pushPhase(new SummonPhase(this, 1)); this.pushPhase(new SummonPhase(1));
} }
} }
@ -3186,7 +3205,7 @@ export default class BattleScene extends SceneBase {
if (participated && pokemonDefeated) { if (participated && pokemonDefeated) {
partyMember.addFriendship(FRIENDSHIP_GAIN_FROM_BATTLE); partyMember.addFriendship(FRIENDSHIP_GAIN_FROM_BATTLE);
const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier); const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier);
if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this)) { if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount()) {
machoBraceModifier.stackCount++; machoBraceModifier.stackCount++;
this.updateModifiers(true, true); this.updateModifiers(true, true);
partyMember.updateInfo(); partyMember.updateInfo();
@ -3248,7 +3267,7 @@ export default class BattleScene extends SceneBase {
if (exp) { if (exp) {
const partyMemberIndex = party.indexOf(expPartyMembers[pm]); const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
this.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(this, partyMemberIndex, exp) : new ShowPartyExpBarPhase(this, partyMemberIndex, exp)); this.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(partyMemberIndex, exp) : new ShowPartyExpBarPhase(partyMemberIndex, exp));
} }
} }
} }
@ -3340,7 +3359,7 @@ export default class BattleScene extends SceneBase {
if (encounter) { if (encounter) {
encounter = new MysteryEncounter(encounter); encounter = new MysteryEncounter(encounter);
encounter.populateDialogueTokensFromRequirements(this); encounter.populateDialogueTokensFromRequirements();
return encounter; return encounter;
} }
@ -3368,9 +3387,11 @@ export default class BattleScene extends SceneBase {
} }
let availableEncounters: MysteryEncounter[] = []; let availableEncounters: MysteryEncounter[] = [];
// New encounter should never be the same as the most recent encounter const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.length > 0 ?
const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.length > 0 ? this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1].type : null; this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1].type
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? []; : null;
const disabledEncounters = this.eventManager.getEventMysteryEncountersDisabled();
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType)?.filter(enc => !disabledEncounters.includes(enc)) ?? [];
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available // If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
while (availableEncounters.length === 0 && tier !== null) { while (availableEncounters.length === 0 && tier !== null) {
availableEncounters = biomeMysteryEncounters availableEncounters = biomeMysteryEncounters
@ -3379,27 +3400,27 @@ export default class BattleScene extends SceneBase {
if (!encounterCandidate) { if (!encounterCandidate) {
return false; return false;
} }
if (encounterCandidate.encounterTier !== tier) { // Encounter is in tier if (this.eventManager.getMysteryEncounterTierForEvent(encounterType, encounterCandidate.encounterTier) !== tier) {
return false; return false;
} }
const disallowedGameModes = encounterCandidate.disallowedGameModes; const disallowedGameModes = encounterCandidate.disallowedGameModes;
if (disallowedGameModes && disallowedGameModes.length > 0 if (disallowedGameModes && disallowedGameModes.length > 0
&& disallowedGameModes.includes(this.gameMode.modeId)) { // Encounter is enabled for game mode && disallowedGameModes.includes(this.gameMode.modeId)) {
return false; return false;
} }
if (this.gameMode.modeId === GameModes.CHALLENGE) { // Encounter is enabled for challenges if (this.gameMode.modeId === GameModes.CHALLENGE) {
const disallowedChallenges = encounterCandidate.disallowedChallenges; const disallowedChallenges = encounterCandidate.disallowedChallenges;
if (disallowedChallenges && disallowedChallenges.length > 0 && this.gameMode.challenges.some(challenge => disallowedChallenges.includes(challenge.id))) { if (disallowedChallenges && disallowedChallenges.length > 0 && this.gameMode.challenges.some(challenge => disallowedChallenges.includes(challenge.id))) {
return false; return false;
} }
} }
if (!encounterCandidate.meetsRequirements(this)) { // Meets encounter requirements if (!encounterCandidate.meetsRequirements()) {
return false; return false;
} }
if (previousEncounter !== null && encounterType === previousEncounter) { // Previous encounter was not this one if (previousEncounter !== null && encounterType === previousEncounter) {
return false; return false;
} }
if (this.mysteryEncounterSaveData.encounteredEvents.length > 0 && // Encounter has not exceeded max allowed encounters if (this.mysteryEncounterSaveData.encounteredEvents.length > 0 &&
(encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0) (encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0)
&& this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) { && this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) {
return false; return false;
@ -3427,7 +3448,7 @@ export default class BattleScene extends SceneBase {
encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)]; encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)];
// New encounter object to not dirty flags // New encounter object to not dirty flags
encounter = new MysteryEncounter(encounter); encounter = new MysteryEncounter(encounter);
encounter.populateDialogueTokensFromRequirements(this); encounter.populateDialogueTokensFromRequirements();
return encounter; return encounter;
} }
} }

View File

@ -1,31 +1,49 @@
import BattleScene from "./battle-scene"; import { globalScene } from "#app/global-scene";
import { Command } from "./ui/command-ui-handler"; import type { Command } from "./ui/command-ui-handler";
import * as Utils from "./utils"; import * as Utils from "./utils";
import Trainer, { TrainerVariant } from "./field/trainer"; import Trainer, { TrainerVariant } from "./field/trainer";
import { GameMode } from "./game-mode"; import type { GameMode } from "./game-mode";
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier"; import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
import { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
import { trainerConfigs } from "#app/data/trainer-config"; import { trainerConfigs } from "#app/data/trainer-config";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
import Pokemon, { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon"; import type { EnemyPokemon, PlayerPokemon, TurnMove } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagType } from "#enums/arena-tag-type";
import { BattleSpec } from "#enums/battle-spec"; import { BattleSpec } from "#enums/battle-spec";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { MusicPreference } from "#app/system/settings/settings"; import { MusicPreference } from "#app/system/settings/settings";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { CustomModifierSettings } from "#app/modifier/modifier-type"; import type { CustomModifierSettings } from "#app/modifier/modifier-type";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
export enum ClassicFixedBossWaves { export enum ClassicFixedBossWaves {
// TODO: other fixed wave battles should be added here TOWN_YOUNGSTER = 5,
RIVAL_1 = 8,
RIVAL_2 = 25,
EVIL_GRUNT_1 = 35,
RIVAL_3 = 55,
EVIL_GRUNT_2 = 62,
EVIL_GRUNT_3 = 64,
EVIL_ADMIN_1 = 66,
RIVAL_4 = 95,
EVIL_GRUNT_4 = 112,
EVIL_ADMIN_2 = 114,
EVIL_BOSS_1 = 115, EVIL_BOSS_1 = 115,
RIVAL_5 = 145,
EVIL_BOSS_2 = 165, EVIL_BOSS_2 = 165,
ELITE_FOUR_1 = 182,
ELITE_FOUR_2 = 184,
ELITE_FOUR_3 = 186,
ELITE_FOUR_4 = 188,
CHAMPION = 190,
RIVAL_6 = 195,
} }
export enum BattleType { export enum BattleType {
@ -44,12 +62,12 @@ export enum BattlerIndex {
} }
export interface TurnCommand { export interface TurnCommand {
command: Command; command: Command;
cursor?: number; cursor?: number;
move?: QueuedMove; move?: TurnMove;
targets?: BattlerIndex[]; targets?: BattlerIndex[];
skip?: boolean; skip?: boolean;
args?: any[]; args?: any[];
} }
export interface FaintLogEntry { export interface FaintLogEntry {
@ -154,7 +172,7 @@ export default class Battle {
return this.double ? 2 : 1; return this.double ? 2 : 1;
} }
incrementTurn(scene: BattleScene): void { incrementTurn(): void {
this.turn++; this.turn++;
this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ])); this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ]));
this.battleSeedState = null; this.battleSeedState = null;
@ -169,7 +187,7 @@ export default class Battle {
} }
addPostBattleLoot(enemyPokemon: EnemyPokemon): void { addPostBattleLoot(enemyPokemon: EnemyPokemon): void {
this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable, false).map(i => { this.postBattleLoot.push(...globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable, false).map(i => {
const ret = i as PokemonHeldItemModifier; const ret = i as PokemonHeldItemModifier;
//@ts-ignore - this is awful to fix/change //@ts-ignore - this is awful to fix/change
ret.pokemonId = null; ret.pokemonId = null;
@ -177,43 +195,43 @@ export default class Battle {
})); }));
} }
pickUpScatteredMoney(scene: BattleScene): void { pickUpScatteredMoney(): void {
const moneyAmount = new Utils.IntegerHolder(scene.currentBattle.moneyScattered); const moneyAmount = new Utils.IntegerHolder(globalScene.currentBattle.moneyScattered);
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
if (scene.arena.getTag(ArenaTagType.HAPPY_HOUR)) { if (globalScene.arena.getTag(ArenaTagType.HAPPY_HOUR)) {
moneyAmount.value *= 2; moneyAmount.value *= 2;
} }
scene.addMoney(moneyAmount.value); globalScene.addMoney(moneyAmount.value);
const userLocale = navigator.language || "en-US"; const userLocale = navigator.language || "en-US";
const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale); const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale);
const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount }); const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount });
scene.queueMessage(message, undefined, true); globalScene.queueMessage(message, undefined, true);
scene.currentBattle.moneyScattered = 0; globalScene.currentBattle.moneyScattered = 0;
} }
addBattleScore(scene: BattleScene): void { addBattleScore(): void {
let partyMemberTurnMultiplier = scene.getEnemyParty().length / 2 + 0.5; let partyMemberTurnMultiplier = globalScene.getEnemyParty().length / 2 + 0.5;
if (this.double) { if (this.double) {
partyMemberTurnMultiplier /= 1.5; partyMemberTurnMultiplier /= 1.5;
} }
for (const p of scene.getEnemyParty()) { for (const p of globalScene.getEnemyParty()) {
if (p.isBoss()) { if (p.isBoss()) {
partyMemberTurnMultiplier *= (p.bossSegments / 1.5) / scene.getEnemyParty().length; partyMemberTurnMultiplier *= (p.bossSegments / 1.5) / globalScene.getEnemyParty().length;
} }
} }
const turnMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(1 - Math.min(this.turn - 2, 10 * partyMemberTurnMultiplier) / (10 * partyMemberTurnMultiplier)); const turnMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(1 - Math.min(this.turn - 2, 10 * partyMemberTurnMultiplier) / (10 * partyMemberTurnMultiplier));
const finalBattleScore = Math.ceil(this.battleScore * turnMultiplier); const finalBattleScore = Math.ceil(this.battleScore * turnMultiplier);
scene.score += finalBattleScore; globalScene.score += finalBattleScore;
console.log(`Battle Score: ${finalBattleScore} (${this.turn - 1} Turns x${Math.floor(turnMultiplier * 100) / 100})`); console.log(`Battle Score: ${finalBattleScore} (${this.turn - 1} Turns x${Math.floor(turnMultiplier * 100) / 100})`);
console.log(`Total Score: ${scene.score}`); console.log(`Total Score: ${globalScene.score}`);
scene.updateScoreText(); globalScene.updateScoreText();
} }
getBgmOverride(scene: BattleScene): string | null { getBgmOverride(): string | null {
if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) { if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
// Music is overridden for MEs during ME onInit() // Music is overridden for MEs during ME onInit()
// Should not use any BGM overrides before swapping from DEFAULT mode // Should not use any BGM overrides before swapping from DEFAULT mode
@ -222,7 +240,7 @@ export default class Battle {
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) { if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
return `encounter_${this.trainer?.getEncounterBgm()}`; return `encounter_${this.trainer?.getEncounterBgm()}`;
} }
if (scene.musicPreference === MusicPreference.GENFIVE) { if (globalScene.musicPreference === MusicPreference.GENFIVE) {
return this.trainer?.getBattleBgm() ?? null; return this.trainer?.getBattleBgm() ?? null;
} else { } else {
return this.trainer?.getMixedBattleBgm() ?? null; return this.trainer?.getMixedBattleBgm() ?? null;
@ -230,7 +248,7 @@ export default class Battle {
} else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) { } else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) {
return "end_summit"; return "end_summit";
} }
const wildOpponents = scene.getEnemyParty(); const wildOpponents = globalScene.getEnemyParty();
for (const pokemon of wildOpponents) { for (const pokemon of wildOpponents) {
if (this.battleSpec === BattleSpec.FINAL_BOSS) { if (this.battleSpec === BattleSpec.FINAL_BOSS) {
if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ETERNAMAX) { if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ETERNAMAX) {
@ -239,7 +257,7 @@ export default class Battle {
return "battle_final_encounter"; return "battle_final_encounter";
} }
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) { if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
if (scene.musicPreference === MusicPreference.GENFIVE) { if (globalScene.musicPreference === MusicPreference.GENFIVE) {
switch (pokemon.species.speciesId) { switch (pokemon.species.speciesId) {
case Species.REGIROCK: case Species.REGIROCK:
case Species.REGICE: case Species.REGICE:
@ -256,7 +274,7 @@ export default class Battle {
} }
return "battle_legendary_unova"; return "battle_legendary_unova";
} }
} else if (scene.musicPreference === MusicPreference.ALLGENS) { } else if (globalScene.musicPreference === MusicPreference.ALLGENS) {
switch (pokemon.species.speciesId) { switch (pokemon.species.speciesId) {
case Species.ARTICUNO: case Species.ARTICUNO:
case Species.ZAPDOS: case Species.ZAPDOS:
@ -396,7 +414,7 @@ export default class Battle {
} }
} }
if (scene.gameMode.isClassic && this.waveIndex <= 4) { if (globalScene.gameMode.isClassic && this.waveIndex <= 4) {
return "battle_wild"; return "battle_wild";
} }
@ -409,12 +427,12 @@ export default class Battle {
* @param min The minimum integer to pick, default `0` * @param min The minimum integer to pick, default `0`
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1) * @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
*/ */
randSeedInt(scene: BattleScene, range: number, min: number = 0): number { randSeedInt(range: number, min: number = 0): number {
if (range <= 1) { if (range <= 1) {
return min; return min;
} }
const tempRngCounter = scene.rngCounter; const tempRngCounter = globalScene.rngCounter;
const tempSeedOverride = scene.rngSeedOverride; const tempSeedOverride = globalScene.rngSeedOverride;
const state = Phaser.Math.RND.state(); const state = Phaser.Math.RND.state();
if (this.battleSeedState) { if (this.battleSeedState) {
Phaser.Math.RND.state(this.battleSeedState); Phaser.Math.RND.state(this.battleSeedState);
@ -422,13 +440,13 @@ export default class Battle {
Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.battleSeed, this.turn << 6) ]); Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.battleSeed, this.turn << 6) ]);
console.log("Battle Seed:", this.battleSeed); console.log("Battle Seed:", this.battleSeed);
} }
scene.rngCounter = this.rngCounter++; globalScene.rngCounter = this.rngCounter++;
scene.rngSeedOverride = this.battleSeed; globalScene.rngSeedOverride = this.battleSeed;
const ret = Utils.randSeedInt(range, min); const ret = Utils.randSeedInt(range, min);
this.battleSeedState = Phaser.Math.RND.state(); this.battleSeedState = Phaser.Math.RND.state();
Phaser.Math.RND.state(state); Phaser.Math.RND.state(state);
scene.rngCounter = tempRngCounter; globalScene.rngCounter = tempRngCounter;
scene.rngSeedOverride = tempSeedOverride; globalScene.rngSeedOverride = tempSeedOverride;
return ret; return ret;
} }
@ -441,16 +459,16 @@ export default class Battle {
} }
export class FixedBattle extends Battle { export class FixedBattle extends Battle {
constructor(scene: BattleScene, waveIndex: number, config: FixedBattleConfig) { constructor(waveIndex: number, config: FixedBattleConfig) {
super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : undefined, config.double); super(globalScene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer() : undefined, config.double);
if (config.getEnemyParty) { if (config.getEnemyParty) {
this.enemyParty = config.getEnemyParty(scene); this.enemyParty = config.getEnemyParty();
} }
} }
} }
type GetTrainerFunc = (scene: BattleScene) => Trainer; type GetTrainerFunc = () => Trainer;
type GetEnemyPartyFunc = (scene: BattleScene) => EnemyPokemon[]; type GetEnemyPartyFunc = () => EnemyPokemon[];
export class FixedBattleConfig { export class FixedBattleConfig {
public battleType: BattleType; public battleType: BattleType;
@ -499,12 +517,12 @@ export class FixedBattleConfig {
* @param seedOffset the seed offset to use for the random generation of the trainer * @param seedOffset the seed offset to use for the random generation of the trainer
* @returns the generated trainer * @returns the generated trainer
*/ */
function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], randomGender: boolean = false, seedOffset: number = 0): GetTrainerFunc { export function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], randomGender: boolean = false, seedOffset: number = 0): GetTrainerFunc {
return (scene: BattleScene) => { return () => {
const rand = Utils.randSeedInt(trainerPool.length); const rand = Utils.randSeedInt(trainerPool.length);
const trainerTypes: TrainerType[] = []; const trainerTypes: TrainerType[] = [];
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
for (const trainerPoolEntry of trainerPool) { for (const trainerPoolEntry of trainerPool) {
const trainerType = Array.isArray(trainerPoolEntry) const trainerType = Array.isArray(trainerPoolEntry)
? Utils.randSeedItem(trainerPoolEntry) ? Utils.randSeedItem(trainerPoolEntry)
@ -523,10 +541,10 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]); const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]);
if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) { if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) {
return new Trainer(scene, trainerTypes[rand], (Utils.randInt(3) === 0) ? TrainerVariant.DOUBLE : trainerGender); return new Trainer(trainerTypes[rand], (Utils.randInt(3) === 0) ? TrainerVariant.DOUBLE : trainerGender);
} }
return new Trainer(scene, trainerTypes[rand], trainerGender); return new Trainer(trainerTypes[rand], trainerGender);
}; };
} }
@ -543,51 +561,51 @@ export interface FixedBattleConfigs {
* Champion on 190 * Champion on 190
*/ */
export const classicFixedBattles: FixedBattleConfigs = { export const classicFixedBattles: FixedBattleConfigs = {
[5]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.TOWN_YOUNGSTER]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.YOUNGSTER, Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(() => new Trainer(TrainerType.YOUNGSTER, Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.RIVAL_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)), .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.RIVAL_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_2, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }),
[35]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.EVIL_GRUNT_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
[55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.RIVAL_3]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_3, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }),
[62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_GRUNT_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
[64]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_GRUNT_3]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
[66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_ADMIN_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]], true)), .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]], true)),
[95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.RIVAL_4]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_4, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_GRUNT_4]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
[114]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_ADMIN_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]], true, 1)), .setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]], true, 1)),
[ClassicFixedBossWaves.EVIL_BOSS_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_BOSS_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE, TrainerType.PENNY ])) .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE, TrainerType.PENNY ]))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.RIVAL_5]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_5, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35) [ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2, TrainerType.PENNY_2 ])) .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2, TrainerType.PENNY_2 ]))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }), .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false }),
[182]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.ELITE_FOUR_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, [ TrainerType.HALA, TrainerType.MOLAYNE ], TrainerType.MARNIE_ELITE, TrainerType.RIKA, TrainerType.CRISPIN ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, [ TrainerType.HALA, TrainerType.MOLAYNE ], TrainerType.MARNIE_ELITE, TrainerType.RIKA, TrainerType.CRISPIN ])),
[184]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182) [ClassicFixedBossWaves.ELITE_FOUR_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BRUNO, TrainerType.KOGA, TrainerType.PHOEBE, TrainerType.BERTHA, TrainerType.MARSHAL, TrainerType.SIEBOLD, TrainerType.OLIVIA, TrainerType.NESSA_ELITE, TrainerType.POPPY, TrainerType.AMARYS ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BRUNO, TrainerType.KOGA, TrainerType.PHOEBE, TrainerType.BERTHA, TrainerType.MARSHAL, TrainerType.SIEBOLD, TrainerType.OLIVIA, TrainerType.NESSA_ELITE, TrainerType.POPPY, TrainerType.AMARYS ])),
[186]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182) [ClassicFixedBossWaves.ELITE_FOUR_3]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, [ TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE ], TrainerType.LARRY_ELITE, TrainerType.LACEY ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, [ TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE ], TrainerType.LARRY_ELITE, TrainerType.LACEY ])),
[188]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182) [ClassicFixedBossWaves.ELITE_FOUR_4]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LANCE, TrainerType.KAREN, TrainerType.DRAKE, TrainerType.LUCIAN, TrainerType.CAITLIN, TrainerType.DRASNA, TrainerType.KAHILI, TrainerType.RAIHAN_ELITE, TrainerType.HASSEL, TrainerType.DRAYTON ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LANCE, TrainerType.KAREN, TrainerType.DRAKE, TrainerType.LUCIAN, TrainerType.CAITLIN, TrainerType.DRASNA, TrainerType.KAHILI, TrainerType.RAIHAN_ELITE, TrainerType.HASSEL, TrainerType.DRAYTON ])),
[190]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182) [ClassicFixedBossWaves.CHAMPION]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.ELITE_FOUR_1)
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])), .setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])),
[195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER) [ClassicFixedBossWaves.RIVAL_6]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_6, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)) .setGetTrainerFunc(() => new Trainer(TrainerType.RIVAL_6, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false }) .setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], allowLuckUpgrades: false })
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
import { Arena } from "#app/field/arena"; import { globalScene } from "#app/global-scene";
import BattleScene from "#app/battle-scene"; import type { Arena } from "#app/field/arena";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils"; import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils";
import { MoveCategory, allMoves, MoveTarget } from "#app/data/move"; import { MoveCategory, allMoves, MoveTarget } from "#app/data/move";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import Pokemon, { HitResult, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { HitResult, PokemonMove } from "#app/field/pokemon";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { BattlerIndex } from "#app/battle"; import type { BattlerIndex } from "#app/battle";
import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability"; import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims"; import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
@ -44,7 +45,7 @@ export abstract class ArenaTag {
onRemove(arena: Arena, quiet: boolean = false): void { onRemove(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: this.getMoveName() })); globalScene.queueMessage(i18next.t(`arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: this.getMoveName() }));
} }
} }
@ -74,27 +75,25 @@ export abstract class ArenaTag {
/** /**
* Helper function that retrieves the source Pokemon * Helper function that retrieves the source Pokemon
* @param scene medium to retrieve the source Pokemon
* @returns The source {@linkcode Pokemon} or `null` if none is found * @returns The source {@linkcode Pokemon} or `null` if none is found
*/ */
public getSourcePokemon(scene: BattleScene): Pokemon | null { public getSourcePokemon(): Pokemon | null {
return this.sourceId ? scene.getPokemonById(this.sourceId) : null; return this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
} }
/** /**
* Helper function that retrieves the Pokemon affected * Helper function that retrieves the Pokemon affected
* @param scene - medium to retrieve the involved Pokemon
* @returns list of PlayerPokemon or EnemyPokemon on the field * @returns list of PlayerPokemon or EnemyPokemon on the field
*/ */
public getAffectedPokemon(scene: BattleScene): Pokemon[] { public getAffectedPokemon(): Pokemon[] {
switch (this.side) { switch (this.side) {
case ArenaTagSide.PLAYER: case ArenaTagSide.PLAYER:
return scene.getPlayerField() ?? []; return globalScene.getPlayerField() ?? [];
case ArenaTagSide.ENEMY: case ArenaTagSide.ENEMY:
return scene.getEnemyField() ?? []; return globalScene.getEnemyField() ?? [];
case ArenaTagSide.BOTH: case ArenaTagSide.BOTH:
default: default:
return scene.getField(true) ?? []; return globalScene.getField(true) ?? [];
} }
} }
} }
@ -112,10 +111,10 @@ export class MistTag extends ArenaTag {
super.onAdd(arena); super.onAdd(arena);
if (this.sourceId) { if (this.sourceId) {
const source = arena.scene.getPokemonById(this.sourceId); const source = globalScene.getPokemonById(this.sourceId);
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); globalScene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} else if (!quiet) { } else if (!quiet) {
console.warn("Failed to get source for MistTag onAdd"); console.warn("Failed to get source for MistTag onAdd");
} }
@ -146,7 +145,7 @@ export class MistTag extends ArenaTag {
cancelled.value = true; cancelled.value = true;
if (!simulated) { if (!simulated) {
arena.scene.queueMessage(i18next.t("arenaTag:mistApply")); globalScene.queueMessage(i18next.t("arenaTag:mistApply"));
} }
return true; return true;
@ -193,7 +192,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
if (bypassed.value) { if (bypassed.value) {
return false; return false;
} }
damageMultiplier.value = arena.scene.currentBattle.double ? 2732 / 4096 : 0.5; damageMultiplier.value = globalScene.currentBattle.double ? 2732 / 4096 : 0.5;
return true; return true;
} }
return false; return false;
@ -211,7 +210,7 @@ class ReflectTag extends WeakenMoveScreenTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
} }
@ -227,7 +226,7 @@ class LightScreenTag extends WeakenMoveScreenTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
} }
@ -243,7 +242,7 @@ class AuroraVeilTag extends WeakenMoveScreenTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
} }
@ -268,7 +267,7 @@ export class ConditionalProtectTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t(`arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: super.getMoveName() })); globalScene.queueMessage(i18next.t(`arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: super.getMoveName() }));
} }
// Removes default message for effect removal // Removes default message for effect removal
@ -296,8 +295,8 @@ export class ConditionalProtectTag extends ArenaTag {
if (!simulated) { if (!simulated) {
attacker.stopMultiHit(defender); attacker.stopMultiHit(defender);
new CommonBattleAnim(CommonAnim.PROTECT, defender).play(arena.scene); new CommonBattleAnim(CommonAnim.PROTECT, defender).play();
arena.scene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(defender) })); globalScene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(defender) }));
} }
} }
@ -318,7 +317,7 @@ export class ConditionalProtectTag extends ArenaTag {
*/ */
const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
const move = allMoves[moveId]; const move = allMoves[moveId];
const effectPhase = arena.scene.getCurrentPhase(); const effectPhase = globalScene.getCurrentPhase();
if (effectPhase instanceof MoveEffectPhase) { if (effectPhase instanceof MoveEffectPhase) {
const attacker = effectPhase.getUserPokemon(); const attacker = effectPhase.getUserPokemon();
@ -393,9 +392,9 @@ class MatBlockTag extends ConditionalProtectTag {
onAdd(arena: Arena) { onAdd(arena: Arena) {
if (this.sourceId) { if (this.sourceId) {
const source = arena.scene.getPokemonById(this.sourceId); const source = globalScene.getPokemonById(this.sourceId);
if (source) { if (source) {
arena.scene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); globalScene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} else { } else {
console.warn("Failed to get source for MatBlockTag onAdd"); console.warn("Failed to get source for MatBlockTag onAdd");
} }
@ -448,15 +447,15 @@ export class NoCritTag extends ArenaTag {
/** Queues a message upon adding this effect to the field */ /** Queues a message upon adding this effect to the field */
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, { globalScene.queueMessage(i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, {
moveName: this.getMoveName() moveName: this.getMoveName()
})); }));
} }
/** Queues a message upon removing this effect from the field */ /** Queues a message upon removing this effect from the field */
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
const source = arena.scene.getPokemonById(this.sourceId!); // TODO: is this bang correct? const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct?
arena.scene.queueMessage(i18next.t("arenaTag:noCritOnRemove", { globalScene.queueMessage(i18next.t("arenaTag:noCritOnRemove", {
pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined), pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined),
moveName: this.getMoveName() moveName: this.getMoveName()
})); }));
@ -478,7 +477,7 @@ class WishTag extends ArenaTag {
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
if (this.sourceId) { if (this.sourceId) {
const user = arena.scene.getPokemonById(this.sourceId); const user = globalScene.getPokemonById(this.sourceId);
if (user) { if (user) {
this.battlerIndex = user.getBattlerIndex(); this.battlerIndex = user.getBattlerIndex();
this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) });
@ -490,10 +489,10 @@ class WishTag extends ArenaTag {
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
const target = arena.scene.getField()[this.battlerIndex]; const target = globalScene.getField()[this.battlerIndex];
if (target?.isActive(true)) { if (target?.isActive(true)) {
arena.scene.queueMessage(this.triggerMessage); globalScene.queueMessage(this.triggerMessage);
arena.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), this.healHp, null, true, false)); globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false));
} }
} }
} }
@ -546,11 +545,11 @@ class MudSportTag extends WeakenMoveTypeTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:mudSportOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:mudSportOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:mudSportOnRemove"));
} }
} }
@ -564,11 +563,11 @@ class WaterSportTag extends WeakenMoveTypeTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:waterSportOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:waterSportOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:waterSportOnRemove"));
} }
} }
@ -584,7 +583,7 @@ export class IonDelugeTag extends ArenaTag {
/** Queues an on-add message */ /** Queues an on-add message */
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
} }
onRemove(arena: Arena): void { } // Removes default on-remove message onRemove(arena: Arena): void { } // Removes default on-remove message
@ -679,9 +678,9 @@ class SpikesTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:spikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); globalScene.queueMessage(i18next.t("arenaTag:spikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -698,7 +697,7 @@ class SpikesTag extends ArenaTrapTag {
const damageHpRatio = 1 / (10 - 2 * this.layers); const damageHpRatio = 1 / (10 - 2 * this.layers);
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio); const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); globalScene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.damageAndUpdate(damage, HitResult.OTHER); pokemon.damageAndUpdate(damage, HitResult.OTHER);
if (pokemon.turnData) { if (pokemon.turnData) {
pokemon.turnData.damageTaken += damage; pokemon.turnData.damageTaken += damage;
@ -728,9 +727,9 @@ class ToxicSpikesTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:toxicSpikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); globalScene.queueMessage(i18next.t("arenaTag:toxicSpikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -747,8 +746,8 @@ class ToxicSpikesTag extends ArenaTrapTag {
} }
if (pokemon.isOfType(Type.POISON)) { if (pokemon.isOfType(Type.POISON)) {
this.neutralized = true; this.neutralized = true;
if (pokemon.scene.arena.removeTag(this.tagType)) { if (globalScene.arena.removeTag(this.tagType)) {
pokemon.scene.queueMessage(i18next.t("arenaTag:toxicSpikesActivateTrapPoison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() })); globalScene.queueMessage(i18next.t("arenaTag:toxicSpikesActivateTrapPoison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() }));
return true; return true;
} }
} else if (!pokemon.status) { } else if (!pokemon.status) {
@ -792,7 +791,7 @@ export class DelayedAttackTag extends ArenaTag {
const ret = super.lapse(arena); const ret = super.lapse(arena);
if (!ret) { if (!ret) {
arena.scene.unshiftPhase(new MoveEffectPhase(arena.scene, this.sourceId!, [ this.targetIndex ], new PokemonMove(this.sourceMove!, 0, 0, true))); // TODO: are those bangs correct? globalScene.unshiftPhase(new MoveEffectPhase(this.sourceId!, [ this.targetIndex ], new PokemonMove(this.sourceMove!, 0, 0, true))); // TODO: are those bangs correct?
} }
return ret; return ret;
@ -814,9 +813,9 @@ class StealthRockTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor() })); globalScene.queueMessage(i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -864,7 +863,7 @@ class StealthRockTag extends ArenaTrapTag {
return true; return true;
} }
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio); const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); globalScene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
pokemon.damageAndUpdate(damage, HitResult.OTHER); pokemon.damageAndUpdate(damage, HitResult.OTHER);
if (pokemon.turnData) { if (pokemon.turnData) {
pokemon.turnData.damageTaken += damage; pokemon.turnData.damageTaken += damage;
@ -893,9 +892,9 @@ class StickyWebTag extends ArenaTrapTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena); super.onAdd(arena);
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (!quiet && source) { if (!quiet && source) {
arena.scene.queueMessage(i18next.t("arenaTag:stickyWebOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); globalScene.queueMessage(i18next.t("arenaTag:stickyWebOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() }));
} }
} }
@ -909,9 +908,9 @@ class StickyWebTag extends ArenaTrapTag {
} }
if (!cancelled.value) { if (!cancelled.value) {
pokemon.scene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() })); globalScene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() }));
const stages = new NumberHolder(-1); const stages = new NumberHolder(-1);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [ Stat.SPD ], stages.value)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, [ Stat.SPD ], stages.value));
return true; return true;
} }
} }
@ -945,14 +944,14 @@ export class TrickRoomTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
if (source) { if (source) {
arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} }
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnRemove"));
} }
} }
@ -967,8 +966,8 @@ export class GravityTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
arena.scene.getField(true).forEach((pokemon) => { globalScene.getField(true).forEach((pokemon) => {
if (pokemon !== null) { if (pokemon !== null) {
pokemon.removeTag(BattlerTagType.FLOATING); pokemon.removeTag(BattlerTagType.FLOATING);
pokemon.removeTag(BattlerTagType.TELEKINESIS); pokemon.removeTag(BattlerTagType.TELEKINESIS);
@ -980,7 +979,7 @@ export class GravityTag extends ArenaTag {
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:gravityOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:gravityOnRemove"));
} }
} }
@ -996,29 +995,29 @@ class TailwindTag extends ArenaTag {
onAdd(arena: Arena, quiet: boolean = false): void { onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
const source = arena.scene.getPokemonById(this.sourceId!); //TODO: this bang is questionable! const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable!
const party = (source?.isPlayer() ? source.scene.getPlayerField() : source?.scene.getEnemyField()) ?? []; const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? [];
for (const pokemon of party) { for (const pokemon of party) {
// Apply the CHARGED tag to party members with the WIND_POWER ability // Apply the CHARGED tag to party members with the WIND_POWER ability
if (pokemon.hasAbility(Abilities.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) { if (pokemon.hasAbility(Abilities.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
pokemon.addTag(BattlerTagType.CHARGED); pokemon.addTag(BattlerTagType.CHARGED);
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() })); globalScene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() }));
} }
// Raise attack by one stage if party member has WIND_RIDER ability // Raise attack by one stage if party member has WIND_RIDER ability
if (pokemon.hasAbility(Abilities.WIND_RIDER)) { if (pokemon.hasAbility(Abilities.WIND_RIDER)) {
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.getBattlerIndex())); globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.getBattlerIndex()));
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK ], 1, true)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.ATK ], 1, true));
} }
} }
} }
onRemove(arena: Arena, quiet: boolean = false): void { onRemove(arena: Arena, quiet: boolean = false): void {
if (!quiet) { if (!quiet) {
arena.scene.queueMessage(i18next.t(`arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
} }
@ -1033,11 +1032,11 @@ class HappyHourTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:happyHourOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:happyHourOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:happyHourOnRemove"));
} }
} }
@ -1047,11 +1046,11 @@ class SafeguardTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
onRemove(arena: Arena): void { onRemove(arena: Arena): void {
arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
@ -1074,16 +1073,16 @@ class ImprisonTag extends ArenaTrapTag {
* This function applies the effects of Imprison to the opposing Pokemon already present on the field. * This function applies the effects of Imprison to the opposing Pokemon already present on the field.
* @param arena * @param arena
*/ */
override onAdd({ scene }: Arena) { override onAdd() {
const source = this.getSourcePokemon(scene); const source = this.getSourcePokemon();
if (source) { if (source) {
const party = this.getAffectedPokemon(scene); const party = this.getAffectedPokemon();
party?.forEach((p: Pokemon ) => { party?.forEach((p: Pokemon ) => {
if (p.isAllowedInBattle()) { if (p.isAllowedInBattle()) {
p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
} }
}); });
scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); globalScene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
} }
} }
@ -1092,8 +1091,8 @@ class ImprisonTag extends ArenaTrapTag {
* @param _arena * @param _arena
* @returns `true` if the source of the tag is still active on the field | `false` if not * @returns `true` if the source of the tag is still active on the field | `false` if not
*/ */
override lapse({ scene }: Arena): boolean { override lapse(): boolean {
const source = this.getSourcePokemon(scene); const source = this.getSourcePokemon();
return source ? source.isActive(true) : false; return source ? source.isActive(true) : false;
} }
@ -1103,7 +1102,7 @@ class ImprisonTag extends ArenaTrapTag {
* @returns `true` * @returns `true`
*/ */
override activateTrap(pokemon: Pokemon): boolean { override activateTrap(pokemon: Pokemon): boolean {
const source = this.getSourcePokemon(pokemon.scene); const source = this.getSourcePokemon();
if (source && source.isActive(true) && pokemon.isAllowedInBattle()) { if (source && source.isActive(true) && pokemon.isAllowedInBattle()) {
pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
} }
@ -1114,8 +1113,8 @@ class ImprisonTag extends ArenaTrapTag {
* When the arena tag is removed, it also attempts to remove any related Battler Tags if they haven't already been removed from the affected Pokemon * When the arena tag is removed, it also attempts to remove any related Battler Tags if they haven't already been removed from the affected Pokemon
* @param arena * @param arena
*/ */
override onRemove({ scene }: Arena): void { override onRemove(): void {
const party = this.getAffectedPokemon(scene); const party = this.getAffectedPokemon();
party?.forEach((p: Pokemon) => { party?.forEach((p: Pokemon) => {
p.removeTag(BattlerTagType.IMPRISON); p.removeTag(BattlerTagType.IMPRISON);
}); });
@ -1136,19 +1135,19 @@ class FireGrassPledgeTag extends ArenaTag {
override onAdd(arena: Arena): void { override onAdd(arena: Arena): void {
// "A sea of fire enveloped your/the opposing team!" // "A sea of fire enveloped your/the opposing team!"
arena.scene.queueMessage(i18next.t(`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
override lapse(arena: Arena): boolean { override lapse(arena: Arena): boolean {
const field: Pokemon[] = (this.side === ArenaTagSide.PLAYER) const field: Pokemon[] = (this.side === ArenaTagSide.PLAYER)
? arena.scene.getPlayerField() ? globalScene.getPlayerField()
: arena.scene.getEnemyField(); : globalScene.getEnemyField();
field.filter(pokemon => !pokemon.isOfType(Type.FIRE) && !pokemon.switchOutStatus).forEach(pokemon => { field.filter(pokemon => !pokemon.isOfType(Type.FIRE) && !pokemon.switchOutStatus).forEach(pokemon => {
// "{pokemonNameWithAffix} was hurt by the sea of fire!" // "{pokemonNameWithAffix} was hurt by the sea of fire!"
pokemon.scene.queueMessage(i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); globalScene.queueMessage(i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
// TODO: Replace this with a proper animation // TODO: Replace this with a proper animation
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM)); globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM));
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8)); pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8));
}); });
@ -1170,7 +1169,7 @@ class WaterFirePledgeTag extends ArenaTag {
override onAdd(arena: Arena): void { override onAdd(arena: Arena): void {
// "A rainbow appeared in the sky on your/the opposing team's side!" // "A rainbow appeared in the sky on your/the opposing team's side!"
arena.scene.queueMessage(i18next.t(`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
/** /**
@ -1200,7 +1199,7 @@ class GrassWaterPledgeTag extends ArenaTag {
override onAdd(arena: Arena): void { override onAdd(arena: Arena): void {
// "A swamp enveloped your/the opposing team!" // "A swamp enveloped your/the opposing team!"
arena.scene.queueMessage(i18next.t(`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); globalScene.queueMessage(i18next.t(`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
} }
} }
@ -1217,7 +1216,7 @@ export class FairyLockTag extends ArenaTag {
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd")); globalScene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
} }
} }

View File

@ -1,6 +1,7 @@
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { pokemonEvolutions, SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import i18next from "i18next"; import i18next from "i18next";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { Species } from "#enums/species"; import { Species } from "#enums/species";

View File

@ -1,6 +1,7 @@
import { globalScene } from "#app/global-scene";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
@ -11,6 +12,8 @@ import { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day"; import { TimeOfDay } from "#enums/time-of-day";
import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifier, TempExtraModifierModifier } from "#app/modifier/modifier"; import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifier, TempExtraModifierModifier } from "#app/modifier/modifier";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
import { speciesStarterCosts } from "./starters";
import i18next from "i18next";
export enum SpeciesWildEvolutionDelay { export enum SpeciesWildEvolutionDelay {
@ -119,17 +122,214 @@ export class FusionSpeciesFormEvolution extends SpeciesFormEvolution {
export class SpeciesEvolutionCondition { export class SpeciesEvolutionCondition {
public predicate: EvolutionConditionPredicate; public predicate: EvolutionConditionPredicate;
public enforceFunc: EvolutionConditionEnforceFunc | undefined; public enforceFunc?: EvolutionConditionEnforceFunc;
public description: string;
constructor(predicate: EvolutionConditionPredicate, enforceFunc?: EvolutionConditionEnforceFunc) { constructor(predicate: EvolutionConditionPredicate, enforceFunc?: EvolutionConditionEnforceFunc) {
this.predicate = predicate; this.predicate = predicate;
this.enforceFunc = enforceFunc; this.enforceFunc = enforceFunc;
this.description = "";
} }
} }
export class SpeciesFriendshipEvolutionCondition extends SpeciesEvolutionCondition { class GenderEvolutionCondition extends SpeciesEvolutionCondition {
constructor(friendshipAmount: integer, predicate?: EvolutionConditionPredicate, enforceFunc?: EvolutionConditionEnforceFunc) { public gender: Gender;
super(p => p.friendship >= friendshipAmount && (!predicate || predicate(p)), enforceFunc); constructor(gender: Gender) {
super(p => p.gender === gender, p => p.gender = gender);
this.gender = gender;
this.description = i18next.t("pokemonEvolutions:gender", { gender: i18next.t(`pokemonEvolutions:${Gender[gender]}`) });
}
}
class TimeOfDayEvolutionCondition extends SpeciesEvolutionCondition {
public timesOfDay: TimeOfDay[];
constructor(tod: "day" | "night") {
if (tod === "day") {
super(() => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY);
this.timesOfDay = [ TimeOfDay.DAWN, TimeOfDay.DAY ];
} else if (tod === "night") {
super(() => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT);
this.timesOfDay = [ TimeOfDay.DUSK, TimeOfDay.NIGHT ];
} else {
super(() => false);
this.timesOfDay = [];
}
this.description = i18next.t("pokemonEvolutions:timeOfDay", { tod: i18next.t(`pokemonEvolutions:${tod}`) });
}
}
class MoveEvolutionCondition extends SpeciesEvolutionCondition {
public move: Moves;
constructor(move: Moves) {
super(p => p.moveset.filter(m => m?.moveId === move).length > 0);
this.move = move;
const moveKey = Moves[this.move].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("");
this.description = i18next.t("pokemonEvolutions:move", { move: i18next.t(`move:${moveKey}.name`) });
}
}
class FriendshipEvolutionCondition extends SpeciesEvolutionCondition {
public amount: integer;
constructor(amount: number) {
super(p => p.friendship >= amount);
this.amount = amount;
this.description = i18next.t("pokemonEvolutions:friendship");
}
}
class FriendshipTimeOfDayEvolutionCondition extends SpeciesEvolutionCondition {
public amount: integer;
public timesOfDay: TimeOfDay[];
constructor(amount: number, tod: "day" | "night") {
if (tod === "day") {
super(p => p.friendship >= amount && (globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY));
this.timesOfDay = [ TimeOfDay.DAWN, TimeOfDay.DAY ];
} else if (tod === "night") {
super(p => p.friendship >= amount && (globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT));
this.timesOfDay = [ TimeOfDay.DUSK, TimeOfDay.NIGHT ];
} else {
super(p => false);
this.timesOfDay = [];
}
this.amount = amount;
this.description = i18next.t("pokemonEvolutions:friendshipTimeOfDay", { tod: i18next.t(`pokemonEvolutions:${tod}`) });
}
}
class FriendshipMoveTypeEvolutionCondition extends SpeciesEvolutionCondition {
public amount: integer;
public type: Type;
constructor(amount: number, type: Type) {
super(p => p.friendship >= amount && !!p.getMoveset().find(m => m?.getMove().type === type));
this.amount = amount;
this.type = type;
this.description = i18next.t("pokemonEvolutions:friendshipMoveType", { type: i18next.t(`pokemonInfo:Type.${Type[this.type]}`) });
}
}
class ShedinjaEvolutionCondition extends SpeciesEvolutionCondition {
constructor() {
super(() => globalScene.getPlayerParty().length < 6 && globalScene.pokeballCounts[PokeballType.POKEBALL] > 0);
this.description = i18next.t("pokemonEvolutions:shedinja");
}
}
class PartyTypeEvolutionCondition extends SpeciesEvolutionCondition {
public type: Type;
constructor(type: Type) {
super(() => !!globalScene.getPlayerParty().find(p => p.getTypes(false, false, true).indexOf(type) > -1));
this.type = type;
this.description = i18next.t("pokemonEvolutions:partyType", { type: i18next.t(`pokemonInfo:Type.${Type[this.type]}`) });
}
}
class CaughtEvolutionCondition extends SpeciesEvolutionCondition {
public species: Species;
constructor(species: Species) {
super(() => !!globalScene.gameData.dexData[species].caughtAttr);
this.species = species;
this.description = i18next.t("pokemonEvolutions:caught", { species: i18next.t(`pokemon:${Species[this.species].toLowerCase()}`) });
}
}
class WeatherEvolutionCondition extends SpeciesEvolutionCondition {
public weatherTypes: WeatherType[];
constructor(weatherTypes: WeatherType[]) {
super(() => weatherTypes.indexOf(globalScene.arena.weather?.weatherType || WeatherType.NONE) > -1);
this.weatherTypes = weatherTypes;
}
}
class MoveTypeEvolutionCondition extends SpeciesEvolutionCondition {
public type: Type;
constructor(type: Type) {
super(p => p.moveset.filter(m => m?.getMove().type === type).length > 0);
this.type = type;
this.description = i18next.t("pokemonEvolutions:moveType", { type: i18next.t(`pokemonInfo:Type.${Type[this.type]}`) });
}
}
class TreasureEvolutionCondition extends SpeciesEvolutionCondition {
constructor() {
super(p => p.evoCounter
+ p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
+ globalScene.findModifiers(m => m instanceof MoneyMultiplierModifier
|| m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9);
this.description = i18next.t("pokemonEvolutions:treasure");
}
}
class TyrogueEvolutionCondition extends SpeciesEvolutionCondition {
public move: Moves;
constructor(move: Moves) {
super(p =>
p.getMoveset(true).find(m => m && [ Moves.LOW_SWEEP, Moves.MACH_PUNCH, Moves.RAPID_SPIN ].includes(m?.moveId))?.moveId === move);
this.move = move;
const moveKey = Moves[this.move].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("");
this.description = i18next.t("pokemonEvolutions:move", { move: i18next.t(`move:${moveKey}.name`) });
}
}
class NatureEvolutionCondition extends SpeciesEvolutionCondition {
public natures: Nature[];
constructor(natures: Nature[]) {
super(p => natures.indexOf(p.getNature()) > -1);
this.natures = natures;
this.description = i18next.t("pokemonEvolutions:nature");
}
}
class MoveTimeOfDayEvolutionCondition extends SpeciesEvolutionCondition {
public move: Moves;
public timesOfDay: TimeOfDay[];
constructor(move: Moves, tod: "day" | "night") {
if (tod === "day") {
super(p => p.moveset.filter(m => m?.moveId === move).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY));
this.move = move;
this.timesOfDay = [ TimeOfDay.DAWN, TimeOfDay.DAY ];
} else if (tod === "night") {
super(p => p.moveset.filter(m => m?.moveId === move).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT));
this.move = move;
this.timesOfDay = [ TimeOfDay.DUSK, TimeOfDay.NIGHT ];
} else {
super(() => false);
this.timesOfDay = [];
}
const moveKey = Moves[this.move].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("");
this.description = i18next.t("pokemonEvolutions:moveTimeOfDay", { move: i18next.t(`move:${moveKey}.name`), tod: i18next.t(`pokemonEvolutions:${tod}`) });
}
}
class BiomeEvolutionCondition extends SpeciesEvolutionCondition {
public biomes: Biome[];
constructor(biomes: Biome[]) {
super(() => biomes.filter(b => b === globalScene.arena.biomeType).length > 0);
this.biomes = biomes;
this.description = i18next.t("pokemonEvolutions:biome");
}
}
class DunsparceEvolutionCondition extends SpeciesEvolutionCondition {
constructor() {
super(p => {
let ret = false;
if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) {
globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
}
return ret;
});
const moveKey = Moves[Moves.HYPER_DRILL].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("");
this.description = i18next.t("pokemonEvolutions:move", { move: i18next.t(`move:${moveKey}.name`) });
}
}
class TandemausEvolutionCondition extends SpeciesEvolutionCondition {
constructor() {
super(p => {
let ret = false;
globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
return ret;
});
} }
} }
@ -266,8 +466,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ELECTRODE, 30, null, null) new SpeciesEvolution(Species.ELECTRODE, 30, null, null)
], ],
[Species.CUBONE]: [ [Species.CUBONE]: [
new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new TimeOfDayEvolutionCondition("night")),
new SpeciesEvolution(Species.MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.MAROWAK, 28, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.TYROGUE]: [ [Species.TYROGUE]: [
/** /**
@ -276,19 +476,13 @@ export const pokemonEvolutions: PokemonEvolutions = {
* If Tyrogue knows multiple of these moves, its evolution is based on * If Tyrogue knows multiple of these moves, its evolution is based on
* the first qualifying move in its moveset. * the first qualifying move in its moveset.
*/ */
new SpeciesEvolution(Species.HITMONLEE, 20, null, new SpeciesEvolutionCondition(p => new SpeciesEvolution(Species.HITMONLEE, 20, null, new TyrogueEvolutionCondition(Moves.LOW_SWEEP)),
p.getMoveset(true).find(move => move && [ Moves.LOW_SWEEP, Moves.MACH_PUNCH, Moves.RAPID_SPIN ].includes(move?.moveId))?.moveId === Moves.LOW_SWEEP new SpeciesEvolution(Species.HITMONCHAN, 20, null, new TyrogueEvolutionCondition(Moves.MACH_PUNCH)),
)), new SpeciesEvolution(Species.HITMONTOP, 20, null, new TyrogueEvolutionCondition(Moves.RAPID_SPIN)),
new SpeciesEvolution(Species.HITMONCHAN, 20, null, new SpeciesEvolutionCondition(p =>
p.getMoveset(true).find(move => move && [ Moves.LOW_SWEEP, Moves.MACH_PUNCH, Moves.RAPID_SPIN ].includes(move?.moveId))?.moveId === Moves.MACH_PUNCH
)),
new SpeciesEvolution(Species.HITMONTOP, 20, null, new SpeciesEvolutionCondition(p =>
p.getMoveset(true).find(move => move && [ Moves.LOW_SWEEP, Moves.MACH_PUNCH, Moves.RAPID_SPIN ].includes(move?.moveId))?.moveId === Moves.RAPID_SPIN
)),
], ],
[Species.KOFFING]: [ [Species.KOFFING]: [
new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new TimeOfDayEvolutionCondition("night")),
new SpeciesEvolution(Species.WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.WEEZING, 35, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.RHYHORN]: [ [Species.RHYHORN]: [
new SpeciesEvolution(Species.RHYDON, 42, null, null) new SpeciesEvolution(Species.RHYDON, 42, null, null)
@ -333,8 +527,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.QUILAVA, 14, null, null) new SpeciesEvolution(Species.QUILAVA, 14, null, null)
], ],
[Species.QUILAVA]: [ [Species.QUILAVA]: [
new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new TimeOfDayEvolutionCondition("night")),
new SpeciesEvolution(Species.TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.TYPHLOSION, 36, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.TOTODILE]: [ [Species.TOTODILE]: [
new SpeciesEvolution(Species.CROCONAW, 18, null, null) new SpeciesEvolution(Species.CROCONAW, 18, null, null)
@ -436,8 +630,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.LINOONE, 20, null, null) new SpeciesEvolution(Species.LINOONE, 20, null, null)
], ],
[Species.WURMPLE]: [ [Species.WURMPLE]: [
new SpeciesEvolution(Species.SILCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), new SpeciesEvolution(Species.SILCOON, 7, null, new TimeOfDayEvolutionCondition("day")),
new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.CASCOON, 7, null, new TimeOfDayEvolutionCondition("night"))
], ],
[Species.SILCOON]: [ [Species.SILCOON]: [
new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null) new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null)
@ -461,8 +655,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.KIRLIA, 20, null, null) new SpeciesEvolution(Species.KIRLIA, 20, null, null)
], ],
[Species.KIRLIA]: [ [Species.KIRLIA]: [
new SpeciesEvolution(Species.GARDEVOIR, 30, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)), new SpeciesEvolution(Species.GARDEVOIR, 30, null, new GenderEvolutionCondition(Gender.FEMALE)),
new SpeciesEvolution(Species.GALLADE, 30, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE)) new SpeciesEvolution(Species.GALLADE, 30, null, new GenderEvolutionCondition(Gender.MALE))
], ],
[Species.SURSKIT]: [ [Species.SURSKIT]: [
new SpeciesEvolution(Species.MASQUERAIN, 22, null, null) new SpeciesEvolution(Species.MASQUERAIN, 22, null, null)
@ -478,7 +672,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
], ],
[Species.NINCADA]: [ [Species.NINCADA]: [
new SpeciesEvolution(Species.NINJASK, 20, null, null), new SpeciesEvolution(Species.NINJASK, 20, null, null),
new SpeciesEvolution(Species.SHEDINJA, 20, null, new SpeciesEvolutionCondition(p => p.scene.getPlayerParty().length < 6 && p.scene.pokeballCounts[PokeballType.POKEBALL] > 0)) new SpeciesEvolution(Species.SHEDINJA, 20, null, new ShedinjaEvolutionCondition())
], ],
[Species.WHISMUR]: [ [Species.WHISMUR]: [
new SpeciesEvolution(Species.LOUDRED, 20, null, null) new SpeciesEvolution(Species.LOUDRED, 20, null, null)
@ -550,8 +744,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DUSCLOPS, 37, null, null) new SpeciesEvolution(Species.DUSCLOPS, 37, null, null)
], ],
[Species.SNORUNT]: [ [Species.SNORUNT]: [
new SpeciesEvolution(Species.GLALIE, 42, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE)), new SpeciesEvolution(Species.GLALIE, 42, null, new GenderEvolutionCondition(Gender.MALE)),
new SpeciesEvolution(Species.FROSLASS, 42, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)) new SpeciesEvolution(Species.FROSLASS, 42, null, new GenderEvolutionCondition(Gender.FEMALE))
], ],
[Species.SPHEAL]: [ [Species.SPHEAL]: [
new SpeciesEvolution(Species.SEALEO, 32, null, null) new SpeciesEvolution(Species.SEALEO, 32, null, null)
@ -614,11 +808,11 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.BASTIODON, 30, null, null) new SpeciesEvolution(Species.BASTIODON, 30, null, null)
], ],
[Species.BURMY]: [ [Species.BURMY]: [
new SpeciesEvolution(Species.MOTHIM, 20, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE)), new SpeciesEvolution(Species.MOTHIM, 20, null, new GenderEvolutionCondition(Gender.MALE)),
new SpeciesEvolution(Species.WORMADAM, 20, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)) new SpeciesEvolution(Species.WORMADAM, 20, null, new GenderEvolutionCondition(Gender.FEMALE))
], ],
[Species.COMBEE]: [ [Species.COMBEE]: [
new SpeciesEvolution(Species.VESPIQUEN, 21, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)) new SpeciesEvolution(Species.VESPIQUEN, 21, null, new GenderEvolutionCondition(Gender.FEMALE))
], ],
[Species.BUIZEL]: [ [Species.BUIZEL]: [
new SpeciesEvolution(Species.FLOATZEL, 26, null, null) new SpeciesEvolution(Species.FLOATZEL, 26, null, null)
@ -660,7 +854,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.LUMINEON, 31, null, null) new SpeciesEvolution(Species.LUMINEON, 31, null, null)
], ],
[Species.MANTYKE]: [ [Species.MANTYKE]: [
new SpeciesEvolution(Species.MANTINE, 32, null, new SpeciesEvolutionCondition(p => !!p.scene.gameData.dexData[Species.REMORAID].caughtAttr), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.MANTINE, 32, null, new CaughtEvolutionCondition(Species.REMORAID), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.SNOVER]: [ [Species.SNOVER]: [
new SpeciesEvolution(Species.ABOMASNOW, 40, null, null) new SpeciesEvolution(Species.ABOMASNOW, 40, null, null)
@ -681,8 +875,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DEWOTT, 17, null, null) new SpeciesEvolution(Species.DEWOTT, 17, null, null)
], ],
[Species.DEWOTT]: [ [Species.DEWOTT]: [
new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new TimeOfDayEvolutionCondition("night")),
new SpeciesEvolution(Species.SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.SAMUROTT, 36, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.PATRAT]: [ [Species.PATRAT]: [
new SpeciesEvolution(Species.WATCHOG, 20, null, null) new SpeciesEvolution(Species.WATCHOG, 20, null, null)
@ -832,8 +1026,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.RUFFLET]: [ [Species.RUFFLET]: [
new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new TimeOfDayEvolutionCondition("night")),
new SpeciesEvolution(Species.BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.BRAVIARY, 54, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.VULLABY]: [ [Species.VULLABY]: [
new SpeciesEvolution(Species.MANDIBUZZ, 54, null, null) new SpeciesEvolution(Species.MANDIBUZZ, 54, null, null)
@ -890,11 +1084,11 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GOGOAT, 32, null, null) new SpeciesEvolution(Species.GOGOAT, 32, null, null)
], ],
[Species.PANCHAM]: [ [Species.PANCHAM]: [
new SpeciesEvolution(Species.PANGORO, 32, null, new SpeciesEvolutionCondition(p => !!p.scene.getPlayerParty().find(p => p.getTypes(false, false, true).indexOf(Type.DARK) > -1)), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.PANGORO, 32, null, new PartyTypeEvolutionCondition(Type.DARK), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.ESPURR]: [ [Species.ESPURR]: [
new SpeciesFormEvolution(Species.MEOWSTIC, "", "female", 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)), new SpeciesFormEvolution(Species.MEOWSTIC, "", "female", 25, null, new GenderEvolutionCondition(Gender.FEMALE)),
new SpeciesFormEvolution(Species.MEOWSTIC, "", "", 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE)) new SpeciesFormEvolution(Species.MEOWSTIC, "", "", 25, null, new GenderEvolutionCondition(Gender.MALE))
], ],
[Species.HONEDGE]: [ [Species.HONEDGE]: [
new SpeciesEvolution(Species.DOUBLADE, 35, null, null) new SpeciesEvolution(Species.DOUBLADE, 35, null, null)
@ -912,21 +1106,21 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CLAWITZER, 37, null, null) new SpeciesEvolution(Species.CLAWITZER, 37, null, null)
], ],
[Species.TYRUNT]: [ [Species.TYRUNT]: [
new SpeciesEvolution(Species.TYRANTRUM, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.TYRANTRUM, 39, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.AMAURA]: [ [Species.AMAURA]: [
new SpeciesEvolution(Species.AURORUS, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.AURORUS, 39, null, new TimeOfDayEvolutionCondition("night"))
], ],
[Species.GOOMY]: [ [Species.GOOMY]: [
new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new TimeOfDayEvolutionCondition("night")),
new SpeciesEvolution(Species.SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.SLIGGOO, 40, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.SLIGGOO]: [ [Species.SLIGGOO]: [
new SpeciesEvolution(Species.GOODRA, 50, null, new SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(p.scene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.GOODRA, 50, null, new WeatherEvolutionCondition([ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.BERGMITE]: [ [Species.BERGMITE]: [
new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new TimeOfDayEvolutionCondition("night")),
new SpeciesEvolution(Species.AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.AVALUGG, 37, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.NOIBAT]: [ [Species.NOIBAT]: [
new SpeciesEvolution(Species.NOIVERN, 48, null, null) new SpeciesEvolution(Species.NOIVERN, 48, null, null)
@ -935,8 +1129,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DARTRIX, 17, null, null) new SpeciesEvolution(Species.DARTRIX, 17, null, null)
], ],
[Species.DARTRIX]: [ [Species.DARTRIX]: [
new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new TimeOfDayEvolutionCondition("night")),
new SpeciesEvolution(Species.DECIDUEYE, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.DECIDUEYE, 34, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.LITTEN]: [ [Species.LITTEN]: [
new SpeciesEvolution(Species.TORRACAT, 17, null, null) new SpeciesEvolution(Species.TORRACAT, 17, null, null)
@ -957,7 +1151,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.TOUCANNON, 28, null, null) new SpeciesEvolution(Species.TOUCANNON, 28, null, null)
], ],
[Species.YUNGOOS]: [ [Species.YUNGOOS]: [
new SpeciesEvolution(Species.GUMSHOOS, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.GUMSHOOS, 20, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.GRUBBIN]: [ [Species.GRUBBIN]: [
new SpeciesEvolution(Species.CHARJABUG, 20, null, null) new SpeciesEvolution(Species.CHARJABUG, 20, null, null)
@ -975,13 +1169,13 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ARAQUANID, 22, null, null) new SpeciesEvolution(Species.ARAQUANID, 22, null, null)
], ],
[Species.FOMANTIS]: [ [Species.FOMANTIS]: [
new SpeciesEvolution(Species.LURANTIS, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)) new SpeciesEvolution(Species.LURANTIS, 34, null, new TimeOfDayEvolutionCondition("day"))
], ],
[Species.MORELULL]: [ [Species.MORELULL]: [
new SpeciesEvolution(Species.SHIINOTIC, 24, null, null) new SpeciesEvolution(Species.SHIINOTIC, 24, null, null)
], ],
[Species.SALANDIT]: [ [Species.SALANDIT]: [
new SpeciesEvolution(Species.SALAZZLE, 33, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)) new SpeciesEvolution(Species.SALAZZLE, 33, null, new GenderEvolutionCondition(Gender.FEMALE))
], ],
[Species.STUFFUL]: [ [Species.STUFFUL]: [
new SpeciesEvolution(Species.BEWEAR, 27, null, null) new SpeciesEvolution(Species.BEWEAR, 27, null, null)
@ -1012,7 +1206,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.MELMETAL, 48, null, null) new SpeciesEvolution(Species.MELMETAL, 48, null, null)
], ],
[Species.ALOLA_RATTATA]: [ [Species.ALOLA_RATTATA]: [
new SpeciesEvolution(Species.ALOLA_RATICATE, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.ALOLA_RATICATE, 20, null, new TimeOfDayEvolutionCondition("night"))
], ],
[Species.ALOLA_DIGLETT]: [ [Species.ALOLA_DIGLETT]: [
new SpeciesEvolution(Species.ALOLA_DUGTRIO, 26, null, null) new SpeciesEvolution(Species.ALOLA_DUGTRIO, 26, null, null)
@ -1085,7 +1279,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
], ],
[Species.TOXEL]: [ [Species.TOXEL]: [
new SpeciesFormEvolution(Species.TOXTRICITY, "", "lowkey", 30, null, new SpeciesFormEvolution(Species.TOXTRICITY, "", "lowkey", 30, null,
new SpeciesEvolutionCondition(p => [ Nature.LONELY, Nature.BOLD, Nature.RELAXED, Nature.TIMID, Nature.SERIOUS, Nature.MODEST, Nature.MILD, Nature.QUIET, Nature.BASHFUL, Nature.CALM, Nature.GENTLE, Nature.CAREFUL ].indexOf(p.getNature()) > -1)), new NatureEvolutionCondition([ Nature.LONELY, Nature.BOLD, Nature.RELAXED, Nature.TIMID, Nature.SERIOUS, Nature.MODEST, Nature.MILD, Nature.QUIET, Nature.BASHFUL, Nature.CALM, Nature.GENTLE, Nature.CAREFUL ])
),
new SpeciesFormEvolution(Species.TOXTRICITY, "", "amped", 30, null, null) new SpeciesFormEvolution(Species.TOXTRICITY, "", "amped", 30, null, null)
], ],
[Species.SIZZLIPEDE]: [ [Species.SIZZLIPEDE]: [
@ -1135,7 +1330,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GALAR_LINOONE, 20, null, null) new SpeciesEvolution(Species.GALAR_LINOONE, 20, null, null)
], ],
[Species.GALAR_LINOONE]: [ [Species.GALAR_LINOONE]: [
new SpeciesEvolution(Species.OBSTAGOON, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.OBSTAGOON, 35, null, new TimeOfDayEvolutionCondition("night"))
], ],
[Species.GALAR_YAMASK]: [ [Species.GALAR_YAMASK]: [
new SpeciesEvolution(Species.RUNERIGUS, 34, null, null) new SpeciesEvolution(Species.RUNERIGUS, 34, null, null)
@ -1144,7 +1339,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.HISUI_ZOROARK, 30, null, null) new SpeciesEvolution(Species.HISUI_ZOROARK, 30, null, null)
], ],
[Species.HISUI_SLIGGOO]: [ [Species.HISUI_SLIGGOO]: [
new SpeciesEvolution(Species.HISUI_GOODRA, 50, null, new SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(p.scene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.HISUI_GOODRA, 50, null, new WeatherEvolutionCondition([ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.SPRIGATITO]: [ [Species.SPRIGATITO]: [
new SpeciesEvolution(Species.FLORAGATO, 16, null, null) new SpeciesEvolution(Species.FLORAGATO, 16, null, null)
@ -1165,8 +1360,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.QUAQUAVAL, 36, null, null) new SpeciesEvolution(Species.QUAQUAVAL, 36, null, null)
], ],
[Species.LECHONK]: [ [Species.LECHONK]: [
new SpeciesFormEvolution(Species.OINKOLOGNE, "", "female", 18, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)), new SpeciesFormEvolution(Species.OINKOLOGNE, "", "female", 18, null, new GenderEvolutionCondition(Gender.FEMALE)),
new SpeciesFormEvolution(Species.OINKOLOGNE, "", "", 18, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE)) new SpeciesFormEvolution(Species.OINKOLOGNE, "", "", 18, null, new GenderEvolutionCondition(Gender.MALE))
], ],
[Species.TAROUNTULA]: [ [Species.TAROUNTULA]: [
new SpeciesEvolution(Species.SPIDOPS, 15, null, null) new SpeciesEvolution(Species.SPIDOPS, 15, null, null)
@ -1181,11 +1376,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.PAWMOT, 32, null, null) new SpeciesEvolution(Species.PAWMOT, 32, null, null)
], ],
[Species.TANDEMAUS]: [ [Species.TANDEMAUS]: [
new SpeciesFormEvolution(Species.MAUSHOLD, "", "three", 25, null, new SpeciesEvolutionCondition(p => { new SpeciesFormEvolution(Species.MAUSHOLD, "", "three", 25, null, new TandemausEvolutionCondition()),
let ret = false;
p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
return ret;
})),
new SpeciesEvolution(Species.MAUSHOLD, 25, null, null) new SpeciesEvolution(Species.MAUSHOLD, 25, null, null)
], ],
[Species.FIDOUGH]: [ [Species.FIDOUGH]: [
@ -1243,7 +1434,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GLIMMORA, 35, null, null) new SpeciesEvolution(Species.GLIMMORA, 35, null, null)
], ],
[Species.GREAVARD]: [ [Species.GREAVARD]: [
new SpeciesEvolution(Species.HOUNDSTONE, 30, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)) new SpeciesEvolution(Species.HOUNDSTONE, 30, null, new TimeOfDayEvolutionCondition("night"))
], ],
[Species.FRIGIBAX]: [ [Species.FRIGIBAX]: [
new SpeciesEvolution(Species.ARCTIBAX, 35, null, null) new SpeciesEvolution(Species.ARCTIBAX, 35, null, null)
@ -1300,21 +1491,21 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.TANGELA]: [ [Species.TANGELA]: [
new SpeciesEvolution(Species.TANGROWTH, 34, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.TANGROWTH, 34, null, new MoveEvolutionCondition(Moves.ANCIENT_POWER), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.LICKITUNG]: [ [Species.LICKITUNG]: [
new SpeciesEvolution(Species.LICKILICKY, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ROLLOUT).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.LICKILICKY, 32, null, new MoveEvolutionCondition(Moves.ROLLOUT), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.STARYU]: [ [Species.STARYU]: [
new SpeciesEvolution(Species.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.EEVEE]: [ [Species.EEVEE]: [
new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new FriendshipMoveTypeEvolutionCondition(120, Type.FAIRY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new FriendshipMoveTypeEvolutionCondition(120, Type.FAIRY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new FriendshipTimeOfDayEvolutionCondition(120, "day"), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new FriendshipTimeOfDayEvolutionCondition(120, "day"), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new FriendshipTimeOfDayEvolutionCondition(120, "night"), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new FriendshipTimeOfDayEvolutionCondition(120, "night"), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.VAPOREON, "", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.VAPOREON, "", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.VAPOREON, "partner", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.VAPOREON, "partner", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG),
@ -1330,13 +1521,13 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.AIPOM]: [ [Species.AIPOM]: [
new SpeciesEvolution(Species.AMBIPOM, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DOUBLE_HIT).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.AMBIPOM, 32, null, new MoveEvolutionCondition(Moves.DOUBLE_HIT), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.SUNKERN]: [ [Species.SUNKERN]: [
new SpeciesEvolution(Species.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.YANMA]: [ [Species.YANMA]: [
new SpeciesEvolution(Species.YANMEGA, 33, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.YANMEGA, 33, null, new MoveEvolutionCondition(Moves.ANCIENT_POWER), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.MURKROW]: [ [Species.MURKROW]: [
new SpeciesEvolution(Species.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1345,32 +1536,26 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.GIRAFARIG]: [ [Species.GIRAFARIG]: [
new SpeciesEvolution(Species.FARIGIRAF, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TWIN_BEAM).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.FARIGIRAF, 32, null, new MoveEvolutionCondition(Moves.TWIN_BEAM), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.DUNSPARCE]: [ [Species.DUNSPARCE]: [
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => { new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new DunsparceEvolutionCondition(), SpeciesWildEvolutionDelay.LONG),
let ret = false; new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new MoveEvolutionCondition(Moves.HYPER_DRILL), SpeciesWildEvolutionDelay.LONG)
if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) {
p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
}
return ret;
}), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.GLIGAR]: [ [Species.GLIGAR]: [
new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new TimeOfDayEvolutionCondition("night") /* Razor fang at night*/, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.SNEASEL]: [ [Species.SNEASEL]: [
new SpeciesEvolution(Species.WEAVILE, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor claw at night*/), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.WEAVILE, 1, EvolutionItem.RAZOR_CLAW, new TimeOfDayEvolutionCondition("night") /* Razor claw at night*/, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.URSARING]: [ [Species.URSARING]: [
new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna
], ],
[Species.PILOSWINE]: [ [Species.PILOSWINE]: [
new SpeciesEvolution(Species.MAMOSWINE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.MAMOSWINE, 1, null, new MoveEvolutionCondition(Moves.ANCIENT_POWER), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.STANTLER]: [ [Species.STANTLER]: [
new SpeciesEvolution(Species.WYRDEER, 25, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.PSYSHIELD_BASH).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.WYRDEER, 25, null, new MoveEvolutionCondition(Moves.PSYSHIELD_BASH), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.LOMBRE]: [ [Species.LOMBRE]: [
new SpeciesEvolution(Species.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1388,11 +1573,11 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.BONSLY]: [ [Species.BONSLY]: [
new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.SUDOWOODO, 1, null, new MoveEvolutionCondition(Moves.MIMIC), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.MIME_JR]: [ [Species.MIME_JR]: [
new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), SpeciesWildEvolutionDelay.MEDIUM), new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new MoveTimeOfDayEvolutionCondition(Moves.MIMIC, "night"), SpeciesWildEvolutionDelay.MEDIUM),
new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.MR_MIME, 1, null, new MoveTimeOfDayEvolutionCondition(Moves.MIMIC, "day"), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.PANSAGE]: [ [Species.PANSAGE]: [
new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1414,8 +1599,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.LILLIGANT, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.LILLIGANT, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.BASCULIN]: [ [Species.BASCULIN]: [
new SpeciesFormEvolution(Species.BASCULEGION, "white-striped", "female", 40, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE), SpeciesWildEvolutionDelay.VERY_LONG), new SpeciesFormEvolution(Species.BASCULEGION, "white-striped", "female", 40, null, new GenderEvolutionCondition(Gender.FEMALE), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesFormEvolution(Species.BASCULEGION, "white-striped", "male", 40, null, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesFormEvolution(Species.BASCULEGION, "white-striped", "male", 40, null, new GenderEvolutionCondition(Gender.MALE), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.MINCCINO]: [ [Species.MINCCINO]: [
new SpeciesEvolution(Species.CINCCINO, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.CINCCINO, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1442,15 +1627,15 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.ROCKRUFF]: [ [Species.ROCKRUFF]: [
new SpeciesFormEvolution(Species.LYCANROC, "", "midday", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0))), new SpeciesFormEvolution(Species.LYCANROC, "own-tempo", "dusk", 25, null, null),
new SpeciesFormEvolution(Species.LYCANROC, "own-tempo", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1)), new SpeciesFormEvolution(Species.LYCANROC, "", "midday", 25, null, new TimeOfDayEvolutionCondition("day")),
new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0))) new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new TimeOfDayEvolutionCondition("night"))
], ],
[Species.STEENEE]: [ [Species.STEENEE]: [
new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.TSAREENA, 28, null, new MoveEvolutionCondition(Moves.STOMP), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.POIPOLE]: [ [Species.POIPOLE]: [
new SpeciesEvolution(Species.NAGANADEL, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DRAGON_PULSE).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.NAGANADEL, 1, null, new MoveEvolutionCondition(Moves.DRAGON_PULSE), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.ALOLA_SANDSHREW]: [ [Species.ALOLA_SANDSHREW]: [
new SpeciesEvolution(Species.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1464,22 +1649,40 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.CLOBBOPUS]: [ [Species.CLOBBOPUS]: [
new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TAUNT).length > 0)/*Once Taunt is implemented, change evo level to 1 and delay to LONG*/) new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new MoveEvolutionCondition(Moves.TAUNT)/*Once Taunt is implemented, change evo level to 1 and delay to LONG*/)
], ],
[Species.SINISTEA]: [ [Species.SINISTEA]: [
new SpeciesFormEvolution(Species.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.POLTEAGEIST, "antique", "antique", 1, EvolutionItem.CHIPPED_POT, null, SpeciesWildEvolutionDelay.LONG) new SpeciesFormEvolution(Species.POLTEAGEIST, "antique", "antique", 1, EvolutionItem.CHIPPED_POT, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.MILCERY]: [ [Species.MILCERY]: [
new SpeciesFormEvolution(Species.ALCREMIE, "", "vanilla-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.TOWN || p.scene.arena.biomeType === Biome.PLAINS || p.scene.arena.biomeType === Biome.GRASS || p.scene.arena.biomeType === Biome.TALL_GRASS || p.scene.arena.biomeType === Biome.METROPOLIS), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "vanilla-cream", 1, EvolutionItem.STRAWBERRY_SWEET,
new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.BADLANDS || p.scene.arena.biomeType === Biome.VOLCANO || p.scene.arena.biomeType === Biome.GRAVEYARD || p.scene.arena.biomeType === Biome.FACTORY || p.scene.arena.biomeType === Biome.SLUM), SpeciesWildEvolutionDelay.LONG), new BiomeEvolutionCondition([ Biome.TOWN, Biome.PLAINS, Biome.GRASS, Biome.TALL_GRASS, Biome.METROPOLIS ]),
new SpeciesFormEvolution(Species.ALCREMIE, "", "matcha-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.FOREST || p.scene.arena.biomeType === Biome.SWAMP || p.scene.arena.biomeType === Biome.MEADOW || p.scene.arena.biomeType === Biome.JUNGLE), SpeciesWildEvolutionDelay.LONG), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "mint-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.SEA || p.scene.arena.biomeType === Biome.BEACH || p.scene.arena.biomeType === Biome.LAKE || p.scene.arena.biomeType === Biome.SEABED), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-cream", 1, EvolutionItem.STRAWBERRY_SWEET,
new SpeciesFormEvolution(Species.ALCREMIE, "", "lemon-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.DESERT || p.scene.arena.biomeType === Biome.POWER_PLANT || p.scene.arena.biomeType === Biome.DOJO || p.scene.arena.biomeType === Biome.RUINS || p.scene.arena.biomeType === Biome.CONSTRUCTION_SITE), SpeciesWildEvolutionDelay.LONG), new BiomeEvolutionCondition([ Biome.BADLANDS, Biome.VOLCANO, Biome.GRAVEYARD, Biome.FACTORY, Biome.SLUM ]),
new SpeciesFormEvolution(Species.ALCREMIE, "", "salted-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.MOUNTAIN || p.scene.arena.biomeType === Biome.CAVE || p.scene.arena.biomeType === Biome.ICE_CAVE || p.scene.arena.biomeType === Biome.FAIRY_CAVE || p.scene.arena.biomeType === Biome.SNOWY_FOREST), SpeciesWildEvolutionDelay.LONG), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.WASTELAND || p.scene.arena.biomeType === Biome.LABORATORY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ALCREMIE, "", "matcha-cream", 1, EvolutionItem.STRAWBERRY_SWEET,
new SpeciesFormEvolution(Species.ALCREMIE, "", "caramel-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.TEMPLE || p.scene.arena.biomeType === Biome.ISLAND), SpeciesWildEvolutionDelay.LONG), new BiomeEvolutionCondition([ Biome.FOREST, Biome.SWAMP, Biome.MEADOW, Biome.JUNGLE ]),
new SpeciesFormEvolution(Species.ALCREMIE, "", "rainbow-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ABYSS || p.scene.arena.biomeType === Biome.SPACE || p.scene.arena.biomeType === Biome.END), SpeciesWildEvolutionDelay.LONG) SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "mint-cream", 1, EvolutionItem.STRAWBERRY_SWEET,
new BiomeEvolutionCondition([ Biome.SEA, Biome.BEACH, Biome.LAKE, Biome.SEABED ]),
SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "lemon-cream", 1, EvolutionItem.STRAWBERRY_SWEET,
new BiomeEvolutionCondition([ Biome.DESERT, Biome.POWER_PLANT, Biome.DOJO, Biome.RUINS, Biome.CONSTRUCTION_SITE ]),
SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "salted-cream", 1, EvolutionItem.STRAWBERRY_SWEET,
new BiomeEvolutionCondition([ Biome.MOUNTAIN, Biome.CAVE, Biome.ICE_CAVE, Biome.FAIRY_CAVE, Biome.SNOWY_FOREST ]),
SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-swirl", 1, EvolutionItem.STRAWBERRY_SWEET,
new BiomeEvolutionCondition([ Biome.WASTELAND, Biome.LABORATORY ]),
SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "caramel-swirl", 1, EvolutionItem.STRAWBERRY_SWEET,
new BiomeEvolutionCondition([ Biome.TEMPLE, Biome.ISLAND ]),
SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALCREMIE, "", "rainbow-swirl", 1, EvolutionItem.STRAWBERRY_SWEET,
new BiomeEvolutionCondition([ Biome.ABYSS, Biome.SPACE, Biome.END ]),
SpeciesWildEvolutionDelay.LONG)
], ],
[Species.DURALUDON]: [ [Species.DURALUDON]: [
new SpeciesFormEvolution(Species.ARCHALUDON, "", "", 1, EvolutionItem.METAL_ALLOY, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesFormEvolution(Species.ARCHALUDON, "", "", 1, EvolutionItem.METAL_ALLOY, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1498,10 +1701,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.HISUI_QWILFISH]: [ [Species.HISUI_QWILFISH]: [
new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.OVERQWIL, 28, null, new MoveEvolutionCondition(Moves.BARB_BARRAGE), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.HISUI_SNEASEL]: [ [Species.HISUI_SNEASEL]: [
new SpeciesEvolution(Species.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, new TimeOfDayEvolutionCondition("day") /* Razor claw at day*/, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.CHARCADET]: [ [Species.CHARCADET]: [
new SpeciesEvolution(Species.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG), new SpeciesEvolution(Species.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG),
@ -1521,7 +1724,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesFormEvolution(Species.SINISTCHA, "artisan", "masterpiece", 1, EvolutionItem.MASTERPIECE_TEACUP, null, SpeciesWildEvolutionDelay.LONG) new SpeciesFormEvolution(Species.SINISTCHA, "artisan", "masterpiece", 1, EvolutionItem.MASTERPIECE_TEACUP, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.DIPPLIN]: [ [Species.DIPPLIN]: [
new SpeciesEvolution(Species.HYDRAPPLE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DRAGON_CHEER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.HYDRAPPLE, 1, null, new MoveEvolutionCondition(Moves.DRAGON_CHEER), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.KADABRA]: [ [Species.KADABRA]: [
new SpeciesEvolution(Species.ALAKAZAM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ALAKAZAM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1536,9 +1739,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GENGAR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.GENGAR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.ONIX]: [ [Species.ONIX]: [
new SpeciesEvolution(Species.STEELIX, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( new SpeciesEvolution(Species.STEELIX, 1, EvolutionItem.LINKING_CORD, new MoveTypeEvolutionCondition(Type.STEEL), SpeciesWildEvolutionDelay.VERY_LONG)
p => p.moveset.filter(m => m?.getMove().type === Type.STEEL).length > 0),
SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.RHYDON]: [ [Species.RHYDON]: [
new SpeciesEvolution(Species.RHYPERIOR, 1, EvolutionItem.PROTECTOR, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.RHYPERIOR, 1, EvolutionItem.PROTECTOR, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1547,9 +1748,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.KINGDRA, 1, EvolutionItem.DRAGON_SCALE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.KINGDRA, 1, EvolutionItem.DRAGON_SCALE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.SCYTHER]: [ [Species.SCYTHER]: [
new SpeciesEvolution(Species.SCIZOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( new SpeciesEvolution(Species.SCIZOR, 1, EvolutionItem.LINKING_CORD, new MoveTypeEvolutionCondition(Type.STEEL), SpeciesWildEvolutionDelay.VERY_LONG),
p => p.moveset.filter(m => m?.getMove().type === Type.STEEL).length > 0),
SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesEvolution(Species.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.ELECTABUZZ]: [ [Species.ELECTABUZZ]: [
@ -1571,8 +1770,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DUSKNOIR, 1, EvolutionItem.REAPER_CLOTH, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.DUSKNOIR, 1, EvolutionItem.REAPER_CLOTH, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.CLAMPERL]: [ [Species.CLAMPERL]: [
new SpeciesEvolution(Species.HUNTAIL, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE /* Deep Sea Tooth */), SpeciesWildEvolutionDelay.VERY_LONG), new SpeciesEvolution(Species.HUNTAIL, 1, EvolutionItem.LINKING_CORD, new GenderEvolutionCondition(Gender.MALE /* Deep Sea Tooth */), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesEvolution(Species.GOREBYSS, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE /* Deep Sea Scale */), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.GOREBYSS, 1, EvolutionItem.LINKING_CORD, new GenderEvolutionCondition(Gender.FEMALE /* Deep Sea Scale */), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.BOLDORE]: [ [Species.BOLDORE]: [
new SpeciesEvolution(Species.GIGALITH, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.GIGALITH, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1581,10 +1780,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CONKELDURR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.CONKELDURR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.KARRABLAST]: [ [Species.KARRABLAST]: [
new SpeciesEvolution(Species.ESCAVALIER, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => !!p.scene.gameData.dexData[Species.SHELMET].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ESCAVALIER, 1, EvolutionItem.LINKING_CORD, new CaughtEvolutionCondition(Species.SHELMET), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.SHELMET]: [ [Species.SHELMET]: [
new SpeciesEvolution(Species.ACCELGOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => !!p.scene.gameData.dexData[Species.KARRABLAST].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ACCELGOR, 1, EvolutionItem.LINKING_CORD, new CaughtEvolutionCondition(Species.KARRABLAST), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.SPRITZEE]: [ [Species.SPRITZEE]: [
new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.SACHET, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.SACHET, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1602,72 +1801,66 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ALOLA_GOLEM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ALOLA_GOLEM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.PRIMEAPE]: [ [Species.PRIMEAPE]: [
new SpeciesEvolution(Species.ANNIHILAPE, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.RAGE_FIST).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ANNIHILAPE, 35, null, new MoveEvolutionCondition(Moves.RAGE_FIST), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.GOLBAT]: [ [Species.GOLBAT]: [
new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.CROBAT, 1, null, new FriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.CHANSEY]: [ [Species.CHANSEY]: [
new SpeciesEvolution(Species.BLISSEY, 1, null, new SpeciesFriendshipEvolutionCondition(200), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.BLISSEY, 1, null, new FriendshipEvolutionCondition(200), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.PICHU]: [ [Species.PICHU]: [
new SpeciesFormEvolution(Species.PIKACHU, "spiky", "partner", 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.SHORT), new SpeciesFormEvolution(Species.PIKACHU, "spiky", "partner", 1, null, new FriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.SHORT),
new SpeciesFormEvolution(Species.PIKACHU, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.SHORT), new SpeciesFormEvolution(Species.PIKACHU, "", "", 1, null, new FriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.SHORT),
], ],
[Species.CLEFFA]: [ [Species.CLEFFA]: [
new SpeciesEvolution(Species.CLEFAIRY, 1, null, new SpeciesFriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.CLEFAIRY, 1, null, new FriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT)
], ],
[Species.IGGLYBUFF]: [ [Species.IGGLYBUFF]: [
new SpeciesEvolution(Species.JIGGLYPUFF, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.JIGGLYPUFF, 1, null, new FriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT)
], ],
[Species.TOGEPI]: [ [Species.TOGEPI]: [
new SpeciesEvolution(Species.TOGETIC, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.TOGETIC, 1, null, new FriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT)
], ],
[Species.AZURILL]: [ [Species.AZURILL]: [
new SpeciesEvolution(Species.MARILL, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.MARILL, 1, null, new FriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT)
], ],
[Species.BUDEW]: [ [Species.BUDEW]: [
new SpeciesEvolution(Species.ROSELIA, 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.ROSELIA, 1, null, new FriendshipTimeOfDayEvolutionCondition(70, "day"), SpeciesWildEvolutionDelay.SHORT)
], ],
[Species.BUNEARY]: [ [Species.BUNEARY]: [
new SpeciesEvolution(Species.LOPUNNY, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.LOPUNNY, 1, null, new FriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.CHINGLING]: [ [Species.CHINGLING]: [
new SpeciesEvolution(Species.CHIMECHO, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.CHIMECHO, 1, null, new FriendshipTimeOfDayEvolutionCondition(90, "night"), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.HAPPINY]: [ [Species.HAPPINY]: [
new SpeciesEvolution(Species.CHANSEY, 1, null, new SpeciesFriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT) new SpeciesEvolution(Species.CHANSEY, 1, null, new FriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT)
], ],
[Species.MUNCHLAX]: [ [Species.MUNCHLAX]: [
new SpeciesEvolution(Species.SNORLAX, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.SNORLAX, 1, null, new FriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.RIOLU]: [ [Species.RIOLU]: [
new SpeciesEvolution(Species.LUCARIO, 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.LUCARIO, 1, null, new FriendshipTimeOfDayEvolutionCondition(120, "day"), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.WOOBAT]: [ [Species.WOOBAT]: [
new SpeciesEvolution(Species.SWOOBAT, 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.SWOOBAT, 1, null, new FriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.SWADLOON]: [ [Species.SWADLOON]: [
new SpeciesEvolution(Species.LEAVANNY, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.LEAVANNY, 1, null, new FriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.TYPE_NULL]: [ [Species.TYPE_NULL]: [
new SpeciesEvolution(Species.SILVALLY, 1, null, new SpeciesFriendshipEvolutionCondition(100), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.SILVALLY, 1, null, new FriendshipEvolutionCondition(100), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.ALOLA_MEOWTH]: [ [Species.ALOLA_MEOWTH]: [
new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new FriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.SNOM]: [ [Species.SNOM]: [
new SpeciesEvolution(Species.FROSMOTH, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.FROSMOTH, 1, null, new FriendshipTimeOfDayEvolutionCondition(90, "night"), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.GIMMIGHOUL]: [ [Species.GIMMIGHOUL]: [
new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new TreasureEvolutionCondition(), SpeciesWildEvolutionDelay.VERY_LONG),
+ p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new TreasureEvolutionCondition(), SpeciesWildEvolutionDelay.VERY_LONG)
+ p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier
|| m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter
+ p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
+ p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier
|| m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG)
] ]
}; };
@ -1690,3 +1883,19 @@ export function initPokemonPrevolutions(): void {
} }
}); });
} }
// TODO: This may cause funny business for double starters such as Pichu/Pikachu
export const pokemonStarters: PokemonPrevolutions = {};
export function initPokemonStarters(): void {
const starterKeys = Object.keys(pokemonPrevolutions);
starterKeys.forEach(pk => {
const prevolution = pokemonPrevolutions[pk];
if (speciesStarterCosts.hasOwnProperty(prevolution)) {
pokemonStarters[pk] = prevolution;
} else {
pokemonStarters[pk] = pokemonPrevolutions[prevolution];
}
});
}

View File

@ -19718,6 +19718,44 @@ export const pokemonFormLevelMoves: PokemonSpeciesFormLevelMoves = {
[ 48, Moves.CLOSE_COMBAT ], [ 48, Moves.CLOSE_COMBAT ],
[ 52, Moves.FOCUS_PUNCH ], [ 52, Moves.FOCUS_PUNCH ],
], ],
2: [
[ EVOLVE_MOVE, Moves.WICKED_BLOW ],
[ 1, Moves.LEER ],
[ 1, Moves.FOCUS_ENERGY ],
[ 1, Moves.ENDURE ],
[ 1, Moves.ROCK_SMASH ],
[ 1, Moves.SUCKER_PUNCH ],
[ 12, Moves.AERIAL_ACE ],
[ 16, Moves.SCARY_FACE ],
[ 20, Moves.HEADBUTT ],
[ 24, Moves.BRICK_BREAK ],
[ 28, Moves.DETECT ],
[ 32, Moves.BULK_UP ],
[ 36, Moves.IRON_HEAD ],
[ 40, Moves.DYNAMIC_PUNCH ],
[ 44, Moves.COUNTER ],
[ 48, Moves.CLOSE_COMBAT ],
[ 52, Moves.FOCUS_PUNCH ],
],
3: [
[ EVOLVE_MOVE, Moves.SURGING_STRIKES ],
[ 1, Moves.LEER ],
[ 1, Moves.FOCUS_ENERGY ],
[ 1, Moves.ENDURE ],
[ 1, Moves.ROCK_SMASH ],
[ 1, Moves.AQUA_JET ],
[ 12, Moves.AERIAL_ACE ],
[ 16, Moves.SCARY_FACE ],
[ 20, Moves.HEADBUTT ],
[ 24, Moves.BRICK_BREAK ],
[ 28, Moves.DETECT ],
[ 32, Moves.BULK_UP ],
[ 36, Moves.IRON_HEAD ],
[ 40, Moves.DYNAMIC_PUNCH ],
[ 44, Moves.COUNTER ],
[ 48, Moves.CLOSE_COMBAT ],
[ 52, Moves.FOCUS_PUNCH ],
],
}, },
[Species.CALYREX]: { [Species.CALYREX]: {
1: [ 1: [

View File

@ -51,9 +51,7 @@ export const speciesStarterCosts = {
[Species.SANDSHREW]: 2, [Species.SANDSHREW]: 2,
[Species.NIDORAN_F]: 3, [Species.NIDORAN_F]: 3,
[Species.NIDORAN_M]: 3, [Species.NIDORAN_M]: 3,
[Species.CLEFAIRY]: 3,
[Species.VULPIX]: 3, [Species.VULPIX]: 3,
[Species.JIGGLYPUFF]: 2,
[Species.ZUBAT]: 3, [Species.ZUBAT]: 3,
[Species.ODDISH]: 3, [Species.ODDISH]: 3,
[Species.PARAS]: 2, [Species.PARAS]: 2,
@ -84,22 +82,15 @@ export const speciesStarterCosts = {
[Species.VOLTORB]: 2, [Species.VOLTORB]: 2,
[Species.EXEGGCUTE]: 3, [Species.EXEGGCUTE]: 3,
[Species.CUBONE]: 3, [Species.CUBONE]: 3,
[Species.HITMONLEE]: 4,
[Species.HITMONCHAN]: 4,
[Species.LICKITUNG]: 3, [Species.LICKITUNG]: 3,
[Species.KOFFING]: 2, [Species.KOFFING]: 2,
[Species.RHYHORN]: 4, [Species.RHYHORN]: 4,
[Species.CHANSEY]: 3,
[Species.TANGELA]: 3, [Species.TANGELA]: 3,
[Species.KANGASKHAN]: 4, [Species.KANGASKHAN]: 4,
[Species.HORSEA]: 3, [Species.HORSEA]: 3,
[Species.GOLDEEN]: 2, [Species.GOLDEEN]: 2,
[Species.STARYU]: 3, [Species.STARYU]: 3,
[Species.MR_MIME]: 3,
[Species.SCYTHER]: 5, [Species.SCYTHER]: 5,
[Species.JYNX]: 4,
[Species.ELECTABUZZ]: 4,
[Species.MAGMAR]: 4,
[Species.PINSIR]: 4, [Species.PINSIR]: 4,
[Species.TAUROS]: 4, [Species.TAUROS]: 4,
[Species.MAGIKARP]: 4, [Species.MAGIKARP]: 4,
@ -110,7 +101,6 @@ export const speciesStarterCosts = {
[Species.OMANYTE]: 3, [Species.OMANYTE]: 3,
[Species.KABUTO]: 3, [Species.KABUTO]: 3,
[Species.AERODACTYL]: 5, [Species.AERODACTYL]: 5,
[Species.SNORLAX]: 5,
[Species.ARTICUNO]: 5, [Species.ARTICUNO]: 5,
[Species.ZAPDOS]: 6, [Species.ZAPDOS]: 6,
[Species.MOLTRES]: 6, [Species.MOLTRES]: 6,
@ -132,8 +122,6 @@ export const speciesStarterCosts = {
[Species.TOGEPI]: 3, [Species.TOGEPI]: 3,
[Species.NATU]: 2, [Species.NATU]: 2,
[Species.MAREEP]: 2, [Species.MAREEP]: 2,
[Species.MARILL]: 4,
[Species.SUDOWOODO]: 3,
[Species.HOPPIP]: 2, [Species.HOPPIP]: 2,
[Species.AIPOM]: 2, [Species.AIPOM]: 2,
[Species.SUNKERN]: 1, [Species.SUNKERN]: 1,
@ -142,7 +130,6 @@ export const speciesStarterCosts = {
[Species.MURKROW]: 3, [Species.MURKROW]: 3,
[Species.MISDREAVUS]: 2, [Species.MISDREAVUS]: 2,
[Species.UNOWN]: 1, [Species.UNOWN]: 1,
[Species.WOBBUFFET]: 2,
[Species.GIRAFARIG]: 3, [Species.GIRAFARIG]: 3,
[Species.PINECO]: 2, [Species.PINECO]: 2,
[Species.DUNSPARCE]: 3, [Species.DUNSPARCE]: 3,
@ -158,7 +145,6 @@ export const speciesStarterCosts = {
[Species.CORSOLA]: 2, [Species.CORSOLA]: 2,
[Species.REMORAID]: 2, [Species.REMORAID]: 2,
[Species.DELIBIRD]: 2, [Species.DELIBIRD]: 2,
[Species.MANTINE]: 3,
[Species.SKARMORY]: 4, [Species.SKARMORY]: 4,
[Species.HOUNDOUR]: 3, [Species.HOUNDOUR]: 3,
[Species.PHANPY]: 3, [Species.PHANPY]: 3,
@ -206,7 +192,6 @@ export const speciesStarterCosts = {
[Species.MINUN]: 2, [Species.MINUN]: 2,
[Species.VOLBEAT]: 2, [Species.VOLBEAT]: 2,
[Species.ILLUMISE]: 2, [Species.ILLUMISE]: 2,
[Species.ROSELIA]: 3,
[Species.GULPIN]: 1, [Species.GULPIN]: 1,
[Species.CARVANHA]: 3, [Species.CARVANHA]: 3,
[Species.WAILMER]: 2, [Species.WAILMER]: 2,
@ -232,7 +217,6 @@ export const speciesStarterCosts = {
[Species.SHUPPET]: 2, [Species.SHUPPET]: 2,
[Species.DUSKULL]: 3, [Species.DUSKULL]: 3,
[Species.TROPIUS]: 3, [Species.TROPIUS]: 3,
[Species.CHIMECHO]: 3,
[Species.ABSOL]: 4, [Species.ABSOL]: 4,
[Species.WYNAUT]: 2, [Species.WYNAUT]: 2,
[Species.SNORUNT]: 2, [Species.SNORUNT]: 2,
@ -543,7 +527,6 @@ export const speciesStarterCosts = {
[Species.GALAR_PONYTA]: 2, [Species.GALAR_PONYTA]: 2,
[Species.GALAR_SLOWPOKE]: 3, [Species.GALAR_SLOWPOKE]: 3,
[Species.GALAR_FARFETCHD]: 3, [Species.GALAR_FARFETCHD]: 3,
[Species.GALAR_MR_MIME]: 3,
[Species.GALAR_ARTICUNO]: 6, [Species.GALAR_ARTICUNO]: 6,
[Species.GALAR_ZAPDOS]: 6, [Species.GALAR_ZAPDOS]: 6,
[Species.GALAR_MOLTRES]: 6, [Species.GALAR_MOLTRES]: 6,

View File

@ -1,16 +1,14 @@
//import { battleAnimRawData } from "./battle-anim-raw-data"; import { globalScene } from "#app/global-scene";
import BattleScene from "../battle-scene";
import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, MoveFlags, SelfStatusMove, allMoves } from "./move"; import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, MoveFlags, SelfStatusMove, allMoves } from "./move";
import Pokemon from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { BattlerIndex } from "../battle"; import type { BattlerIndex } from "../battle";
import { Element } from "json-stable-stringify"; import type { Element } from "json-stable-stringify";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { SubstituteTag } from "./battler-tags"; import { SubstituteTag } from "./battler-tags";
import { isNullOrUndefined } from "../utils"; import { isNullOrUndefined } from "../utils";
import Phaser from "phaser"; import Phaser from "phaser";
import { EncounterAnim } from "#enums/encounter-anims"; import { EncounterAnim } from "#enums/encounter-anims";
//import fs from 'vite-plugin-fs/browser';
export enum AnimFrameTarget { export enum AnimFrameTarget {
USER, USER,
@ -308,7 +306,7 @@ abstract class AnimTimedEvent {
this.resourceName = resourceName; this.resourceName = resourceName;
} }
abstract execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer; abstract execute(battleAnim: BattleAnim, priority?: number): integer;
abstract getEventType(): string; abstract getEventType(): string;
} }
@ -326,15 +324,15 @@ class AnimTimedSoundEvent extends AnimTimedEvent {
} }
} }
execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer { execute(battleAnim: BattleAnim, priority?: number): integer {
const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) }; const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) };
if (this.resourceName) { if (this.resourceName) {
try { try {
scene.playSound(`battle_anims/${this.resourceName}`, soundConfig); globalScene.playSound(`battle_anims/${this.resourceName}`, soundConfig);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
return Math.ceil((scene.sound.get(`battle_anims/${this.resourceName}`).totalDuration * 1000) / 33.33); return Math.ceil((globalScene.sound.get(`battle_anims/${this.resourceName}`).totalDuration * 1000) / 33.33);
} else { } else {
return Math.ceil((battleAnim.user!.cry(soundConfig).totalDuration * 1000) / 33.33); // TODO: is the bang behind user correct? return Math.ceil((battleAnim.user!.cry(soundConfig).totalDuration * 1000) / 33.33); // TODO: is the bang behind user correct?
} }
@ -388,7 +386,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
super(frameIndex, resourceName, source); super(frameIndex, resourceName, source);
} }
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer { execute(moveAnim: MoveAnim, priority?: number): integer {
const tweenProps = {}; const tweenProps = {};
if (this.bgX !== undefined) { if (this.bgX !== undefined) {
tweenProps["x"] = (this.bgX * 0.5) - 320; tweenProps["x"] = (this.bgX * 0.5) - 320;
@ -400,7 +398,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
tweenProps["alpha"] = (this.opacity || 0) / 255; tweenProps["alpha"] = (this.opacity || 0) / 255;
} }
if (Object.keys(tweenProps).length) { if (Object.keys(tweenProps).length) {
scene.tweens.add(Object.assign({ globalScene.tweens.add(Object.assign({
targets: moveAnim.bgSprite, targets: moveAnim.bgSprite,
duration: Utils.getFrameMs(this.duration * 3) duration: Utils.getFrameMs(this.duration * 3)
}, tweenProps)); }, tweenProps));
@ -418,25 +416,25 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
super(frameIndex, resourceName, source); super(frameIndex, resourceName, source);
} }
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer { execute(moveAnim: MoveAnim, priority?: number): integer {
if (moveAnim.bgSprite) { if (moveAnim.bgSprite) {
moveAnim.bgSprite.destroy(); moveAnim.bgSprite.destroy();
} }
moveAnim.bgSprite = this.resourceName moveAnim.bgSprite = this.resourceName
? scene.add.tileSprite(this.bgX - 320, this.bgY - 284, 896, 576, this.resourceName) ? globalScene.add.tileSprite(this.bgX - 320, this.bgY - 284, 896, 576, this.resourceName)
: scene.add.rectangle(this.bgX - 320, this.bgY - 284, 896, 576, 0); : globalScene.add.rectangle(this.bgX - 320, this.bgY - 284, 896, 576, 0);
moveAnim.bgSprite.setOrigin(0, 0); moveAnim.bgSprite.setOrigin(0, 0);
moveAnim.bgSprite.setScale(1.25); moveAnim.bgSprite.setScale(1.25);
moveAnim.bgSprite.setAlpha(this.opacity / 255); moveAnim.bgSprite.setAlpha(this.opacity / 255);
scene.field.add(moveAnim.bgSprite); globalScene.field.add(moveAnim.bgSprite);
const fieldPokemon = scene.getEnemyPokemon(false) ?? scene.getPlayerPokemon(false); const fieldPokemon = globalScene.getEnemyPokemon(false) ?? globalScene.getPlayerPokemon(false);
if (!isNullOrUndefined(priority)) { if (!isNullOrUndefined(priority)) {
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority); globalScene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority);
} else if (fieldPokemon?.isOnField()) { } else if (fieldPokemon?.isOnField()) {
scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon); globalScene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon);
} }
scene.tweens.add({ globalScene.tweens.add({
targets: moveAnim.bgSprite, targets: moveAnim.bgSprite,
duration: Utils.getFrameMs(this.duration * 3) duration: Utils.getFrameMs(this.duration * 3)
}); });
@ -454,14 +452,14 @@ export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimCon
export const commonAnims = new Map<CommonAnim, AnimConfig>(); export const commonAnims = new Map<CommonAnim, AnimConfig>();
export const encounterAnims = new Map<EncounterAnim, AnimConfig>(); export const encounterAnims = new Map<EncounterAnim, AnimConfig>();
export function initCommonAnims(scene: BattleScene): Promise<void> { export function initCommonAnims(): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const commonAnimNames = Utils.getEnumKeys(CommonAnim); const commonAnimNames = Utils.getEnumKeys(CommonAnim);
const commonAnimIds = Utils.getEnumValues(CommonAnim); const commonAnimIds = Utils.getEnumValues(CommonAnim);
const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = []; const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = [];
for (let ca = 0; ca < commonAnimIds.length; ca++) { for (let ca = 0; ca < commonAnimIds.length; ca++) {
const commonAnimId = commonAnimIds[ca]; const commonAnimId = commonAnimIds[ca];
commonAnimFetches.push(scene.cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`) commonAnimFetches.push(globalScene.cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas)))); .then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))));
} }
@ -469,7 +467,7 @@ export function initCommonAnims(scene: BattleScene): Promise<void> {
}); });
} }
export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> { export function initMoveAnim(move: Moves): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
if (moveAnims.has(move)) { if (moveAnims.has(move)) {
if (moveAnims.get(move) !== null) { if (moveAnims.get(move) !== null) {
@ -494,7 +492,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP; const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP;
const fetchAnimAndResolve = (move: Moves) => { const fetchAnimAndResolve = (move: Moves) => {
scene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`) globalScene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`)
.then(response => { .then(response => {
const contentType = response.headers.get("content-type"); const contentType = response.headers.get("content-type");
if (!response.ok || contentType?.indexOf("application/json") === -1) { if (!response.ok || contentType?.indexOf("application/json") === -1) {
@ -516,7 +514,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] : (allMoves[move].getAttrs(DelayedAttackAttr)[0]
?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]); ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
if (chargeAnimSource) { if (chargeAnimSource) {
initMoveChargeAnim(scene, chargeAnimSource.chargeAnim).then(() => resolve()); initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
} else { } else {
resolve(); resolve();
} }
@ -557,10 +555,9 @@ function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
/** /**
* Fetches animation configs to be used in a Mystery Encounter * Fetches animation configs to be used in a Mystery Encounter
* @param scene
* @param encounterAnim one or more animations to fetch * @param encounterAnim one or more animations to fetch
*/ */
export async function initEncounterAnims(scene: BattleScene, encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> { export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
const anims = Array.isArray(encounterAnim) ? encounterAnim : [ encounterAnim ]; const anims = Array.isArray(encounterAnim) ? encounterAnim : [ encounterAnim ];
const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); const encounterAnimNames = Utils.getEnumKeys(EncounterAnim);
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = []; const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
@ -568,14 +565,14 @@ export async function initEncounterAnims(scene: BattleScene, encounterAnim: Enco
if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) { if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) {
continue; continue;
} }
encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`) encounterAnimFetches.push(globalScene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(cas => encounterAnims.set(anim, new AnimConfig(cas)))); .then(cas => encounterAnims.set(anim, new AnimConfig(cas))));
} }
await Promise.allSettled(encounterAnimFetches); await Promise.allSettled(encounterAnimFetches);
} }
export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim): Promise<void> { export function initMoveChargeAnim(chargeAnim: ChargeAnim): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
if (chargeAnims.has(chargeAnim)) { if (chargeAnims.has(chargeAnim)) {
if (chargeAnims.get(chargeAnim) !== null) { if (chargeAnims.get(chargeAnim) !== null) {
@ -590,7 +587,7 @@ export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim):
} }
} else { } else {
chargeAnims.set(chargeAnim, null); chargeAnims.set(chargeAnim, null);
scene.cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/\_/g, "-")}.json`) globalScene.cachedFetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/\_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(ca => { .then(ca => {
if (Array.isArray(ca)) { if (Array.isArray(ca)) {
@ -623,23 +620,22 @@ function populateMoveChargeAnim(chargeAnim: ChargeAnim, animSource: any) {
chargeAnims.set(chargeAnim, [ chargeAnims.get(chargeAnim) as AnimConfig, moveChargeAnim ]); chargeAnims.set(chargeAnim, [ chargeAnims.get(chargeAnim) as AnimConfig, moveChargeAnim ]);
} }
export function loadCommonAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> { export function loadCommonAnimAssets(startLoad?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
loadAnimAssets(scene, Array.from(commonAnims.values()), startLoad).then(() => resolve()); loadAnimAssets(Array.from(commonAnims.values()), startLoad).then(() => resolve());
}); });
} }
/** /**
* Loads encounter animation assets to scene * Loads encounter animation assets to scene
* MUST be called after {@linkcode initEncounterAnims()} to load all required animations properly * MUST be called after {@linkcode initEncounterAnims()} to load all required animations properly
* @param scene
* @param startLoad * @param startLoad
*/ */
export async function loadEncounterAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> { export async function loadEncounterAnimAssets(startLoad?: boolean): Promise<void> {
await loadAnimAssets(scene, Array.from(encounterAnims.values()), startLoad); await loadAnimAssets(Array.from(encounterAnims.values()), startLoad);
} }
export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise<void> { export function loadMoveAnimAssets(moveIds: Moves[], startLoad?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat(); const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
for (const moveId of moveIds) { for (const moveId of moveIds) {
@ -655,11 +651,11 @@ export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLo
} }
} }
} }
loadAnimAssets(scene, moveAnimations, startLoad).then(() => resolve()); loadAnimAssets(moveAnimations, startLoad).then(() => resolve());
}); });
} }
function loadAnimAssets(scene: BattleScene, anims: AnimConfig[], startLoad?: boolean): Promise<void> { function loadAnimAssets(anims: AnimConfig[], startLoad?: boolean): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const backgrounds = new Set<string>(); const backgrounds = new Set<string>();
const sounds = new Set<string>(); const sounds = new Set<string>();
@ -676,19 +672,19 @@ function loadAnimAssets(scene: BattleScene, anims: AnimConfig[], startLoad?: boo
backgrounds.add(abg); backgrounds.add(abg);
} }
if (a.graphic) { if (a.graphic) {
scene.loadSpritesheet(a.graphic, "battle_anims", 96); globalScene.loadSpritesheet(a.graphic, "battle_anims", 96);
} }
} }
for (const bg of backgrounds) { for (const bg of backgrounds) {
scene.loadImage(bg, "battle_anims"); globalScene.loadImage(bg, "battle_anims");
} }
for (const s of sounds) { for (const s of sounds) {
scene.loadSe(s, "battle_anims", s); globalScene.loadSe(s, "battle_anims", s);
} }
if (startLoad) { if (startLoad) {
scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve()); globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve());
if (!scene.load.isLoading()) { if (!globalScene.load.isLoading()) {
scene.load.start(); globalScene.load.start();
} }
} else { } else {
resolve(); resolve();
@ -778,7 +774,7 @@ export abstract class BattleAnim {
return false; return false;
} }
private getGraphicFrameData(scene: BattleScene, frames: AnimFrame[], onSubstitute?: boolean): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> { private getGraphicFrameData(frames: AnimFrame[], onSubstitute?: boolean): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> {
const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([ const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([
[ AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ], [ AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
[ AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ], [ AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
@ -835,7 +831,7 @@ export abstract class BattleAnim {
return ret; return ret;
} }
play(scene: BattleScene, onSubstitute?: boolean, callback?: Function) { play(onSubstitute?: boolean, callback?: Function) {
const isOppAnim = this.isOppAnim(); const isOppAnim = this.isOppAnim();
const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct? const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
const target = !isOppAnim ? this.target! : this.user!; const target = !isOppAnim ? this.target! : this.user!;
@ -907,7 +903,7 @@ export abstract class BattleAnim {
} }
}; };
if (!scene.moveAnimations && !this.playRegardlessOfIssues) { if (!globalScene.moveAnimations && !this.playRegardlessOfIssues) {
return cleanUpAndComplete(); return cleanUpAndComplete();
} }
@ -924,7 +920,7 @@ export abstract class BattleAnim {
let r = anim?.frames.length ?? 0; let r = anim?.frames.length ?? 0;
let f = 0; let f = 0;
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(3), duration: Utils.getFrameMs(3),
repeat: anim?.frames.length ?? 0, repeat: anim?.frames.length ?? 0,
onRepeat: () => { onRepeat: () => {
@ -934,7 +930,7 @@ export abstract class BattleAnim {
} }
const spriteFrames = anim!.frames[f]; // TODO: is the bang correcT? const spriteFrames = anim!.frames[f]; // TODO: is the bang correcT?
const frameData = this.getGraphicFrameData(scene, anim!.frames[f], onSubstitute); // TODO: is the bang correct? const frameData = this.getGraphicFrameData(anim!.frames[f], onSubstitute); // TODO: is the bang correct?
let u = 0; let u = 0;
let t = 0; let t = 0;
let g = 0; let g = 0;
@ -950,19 +946,19 @@ export abstract class BattleAnim {
const spriteSource = isUser ? userSprite : targetSprite; const spriteSource = isUser ? userSprite : targetSprite;
if ((isUser ? u : t) === sprites.length) { if ((isUser ? u : t) === sprites.length) {
if (isUser || !targetSubstitute) { if (isUser || !targetSubstitute) {
const sprite = scene.addPokemonSprite(isUser ? user! : target, 0, 0, spriteSource!.texture, spriteSource!.frame.name, true); // TODO: are those bangs correct? const sprite = globalScene.addPokemonSprite(isUser ? user! : target, 0, 0, spriteSource!.texture, spriteSource!.frame.name, true); // TODO: are those bangs correct?
[ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user! : target).getSprite().pipelineData[k]); // TODO: are those bangs correct? [ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user! : target).getSprite().pipelineData[k]); // TODO: are those bangs correct?
sprite.setPipelineData("spriteKey", (isUser ? user! : target).getBattleSpriteKey()); sprite.setPipelineData("spriteKey", (isUser ? user! : target).getBattleSpriteKey());
sprite.setPipelineData("shiny", (isUser ? user : target).shiny); sprite.setPipelineData("shiny", (isUser ? user : target).shiny);
sprite.setPipelineData("variant", (isUser ? user : target).variant); sprite.setPipelineData("variant", (isUser ? user : target).variant);
sprite.setPipelineData("ignoreFieldPos", true); sprite.setPipelineData("ignoreFieldPos", true);
spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame)); spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame));
scene.field.add(sprite); globalScene.field.add(sprite);
sprites.push(sprite); sprites.push(sprite);
} else { } else {
const sprite = scene.addFieldSprite(spriteSource.x, spriteSource.y, spriteSource.texture); const sprite = globalScene.addFieldSprite(spriteSource.x, spriteSource.y, spriteSource.texture);
spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame)); spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame));
scene.field.add(sprite); globalScene.field.add(sprite);
sprites.push(sprite); sprites.push(sprite);
} }
} }
@ -987,9 +983,9 @@ export abstract class BattleAnim {
} else { } else {
const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
if (g === sprites.length) { if (g === sprites.length) {
const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim!.graphic, 1); // TODO: is the bang correct? const newSprite: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(0, 0, anim!.graphic, 1); // TODO: is the bang correct?
sprites.push(newSprite); sprites.push(newSprite);
scene.field.add(newSprite); globalScene.field.add(newSprite);
spritePriorities.push(1); spritePriorities.push(1);
} }
@ -1000,22 +996,22 @@ export abstract class BattleAnim {
const setSpritePriority = (priority: integer) => { const setSpritePriority = (priority: integer) => {
switch (priority) { switch (priority) {
case 0: case 0:
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon(false) ?? scene.getPlayerPokemon(false)!); // TODO: is this bang correct? globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, globalScene.getEnemyPokemon(false) ?? globalScene.getPlayerPokemon(false)!); // TODO: is this bang correct?
break; break;
case 1: case 1:
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); globalScene.field.moveTo(moveSprite, globalScene.field.getAll().length - 1);
break; break;
case 2: case 2:
switch (frame.focus) { switch (frame.focus) {
case AnimFocus.USER: case AnimFocus.USER:
if (this.bgSprite) { if (this.bgSprite) {
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite); globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite);
} else { } else {
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct?
} }
break; break;
case AnimFocus.TARGET: case AnimFocus.TARGET:
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct?
break; break;
default: default:
setSpritePriority(1); setSpritePriority(1);
@ -1025,10 +1021,10 @@ export abstract class BattleAnim {
case 3: case 3:
switch (frame.focus) { switch (frame.focus) {
case AnimFocus.USER: case AnimFocus.USER:
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct?
break; break;
case AnimFocus.TARGET: case AnimFocus.TARGET:
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct?
break; break;
default: default:
setSpritePriority(1); setSpritePriority(1);
@ -1056,7 +1052,7 @@ export abstract class BattleAnim {
} }
if (anim?.frameTimedEvents.has(f)) { if (anim?.frameTimedEvents.has(f)) {
for (const event of anim.frameTimedEvents.get(f)!) { // TODO: is this bang correct? for (const event of anim.frameTimedEvents.get(f)!) { // TODO: is this bang correct?
r = Math.max((anim.frames.length - f) + event.execute(scene, this), r); r = Math.max((anim.frames.length - f) + event.execute(this), r);
} }
} }
const targets = Utils.getEnumValues(AnimFrameTarget); const targets = Utils.getEnumValues(AnimFrameTarget);
@ -1086,7 +1082,7 @@ export abstract class BattleAnim {
} }
} }
if (r) { if (r) {
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(r), duration: Utils.getFrameMs(r),
onComplete: () => cleanUpAndComplete() onComplete: () => cleanUpAndComplete()
}); });
@ -1123,8 +1119,6 @@ export abstract class BattleAnim {
} }
/** /**
*
* @param scene
* @param targetInitialX * @param targetInitialX
* @param targetInitialY * @param targetInitialY
* @param frameTimeMult * @param frameTimeMult
@ -1135,7 +1129,7 @@ export abstract class BattleAnim {
* - 5 is on top of player sprite * - 5 is on top of player sprite
* @param callback * @param callback
*/ */
playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, callback?: Function) { playWithoutTargets(targetInitialX: number, targetInitialY: number, frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, callback?: Function) {
const spriteCache: SpriteCache = { const spriteCache: SpriteCache = {
[AnimFrameTarget.GRAPHIC]: [], [AnimFrameTarget.GRAPHIC]: [],
[AnimFrameTarget.USER]: [], [AnimFrameTarget.USER]: [],
@ -1156,7 +1150,7 @@ export abstract class BattleAnim {
} }
}; };
if (!scene.moveAnimations && !this.playRegardlessOfIssues) { if (!globalScene.moveAnimations && !this.playRegardlessOfIssues) {
return cleanUpAndComplete(); return cleanUpAndComplete();
} }
@ -1168,13 +1162,13 @@ export abstract class BattleAnim {
let totalFrames = anim!.frames.length; let totalFrames = anim!.frames.length;
let frameCount = 0; let frameCount = 0;
let existingFieldSprites = scene.field.getAll().slice(0); let existingFieldSprites = globalScene.field.getAll().slice(0);
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(3) * frameTimeMult, duration: Utils.getFrameMs(3) * frameTimeMult,
repeat: anim!.frames.length, repeat: anim!.frames.length,
onRepeat: () => { onRepeat: () => {
existingFieldSprites = scene.field.getAll().slice(0); existingFieldSprites = globalScene.field.getAll().slice(0);
const spriteFrames = anim!.frames[frameCount]; const spriteFrames = anim!.frames[frameCount];
const frameData = this.getGraphicFrameDataWithoutTarget(anim!.frames[frameCount], targetInitialX, targetInitialY); const frameData = this.getGraphicFrameDataWithoutTarget(anim!.frames[frameCount], targetInitialX, targetInitialY);
let graphicFrameCount = 0; let graphicFrameCount = 0;
@ -1186,9 +1180,9 @@ export abstract class BattleAnim {
const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
if (graphicFrameCount === sprites.length) { if (graphicFrameCount === sprites.length) {
const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim!.graphic, 1); const newSprite: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(0, 0, anim!.graphic, 1);
sprites.push(newSprite); sprites.push(newSprite);
scene.field.add(newSprite); globalScene.field.add(newSprite);
} }
const graphicIndex = graphicFrameCount++; const graphicIndex = graphicFrameCount++;
@ -1197,11 +1191,11 @@ export abstract class BattleAnim {
const setSpritePriority = (priority: integer) => { const setSpritePriority = (priority: integer) => {
if (existingFieldSprites.length > priority) { if (existingFieldSprites.length > priority) {
// Move to specified priority index // Move to specified priority index
const index = scene.field.getIndex(existingFieldSprites[priority]); const index = globalScene.field.getIndex(existingFieldSprites[priority]);
scene.field.moveTo(moveSprite, index); globalScene.field.moveTo(moveSprite, index);
} else { } else {
// Move to top of scene // Move to top of scene
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); globalScene.field.moveTo(moveSprite, globalScene.field.getAll().length - 1);
} }
}; };
setSpritePriority(frame.priority); setSpritePriority(frame.priority);
@ -1221,7 +1215,7 @@ export abstract class BattleAnim {
} }
if (anim?.frameTimedEvents.get(frameCount)) { if (anim?.frameTimedEvents.get(frameCount)) {
for (const event of anim.frameTimedEvents.get(frameCount)!) { for (const event of anim.frameTimedEvents.get(frameCount)!) {
totalFrames = Math.max((anim.frames.length - frameCount) + event.execute(scene, this, frameTimedEventPriority), totalFrames); totalFrames = Math.max((anim.frames.length - frameCount) + event.execute(this, frameTimedEventPriority), totalFrames);
} }
} }
const targets = Utils.getEnumValues(AnimFrameTarget); const targets = Utils.getEnumValues(AnimFrameTarget);
@ -1248,7 +1242,7 @@ export abstract class BattleAnim {
} }
} }
if (totalFrames) { if (totalFrames) {
scene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(totalFrames), duration: Utils.getFrameMs(totalFrames),
onComplete: () => cleanUpAndComplete() onComplete: () => cleanUpAndComplete()
}); });
@ -1282,7 +1276,7 @@ export class MoveAnim extends BattleAnim {
public move: Moves; public move: Moves;
constructor(move: Moves, user: Pokemon, target: BattlerIndex, playOnEmptyField: boolean = false) { constructor(move: Moves, user: Pokemon, target: BattlerIndex, playOnEmptyField: boolean = false) {
super(user, user.scene.getField()[target], playOnEmptyField); super(user, globalScene.getField()[target], playOnEmptyField);
this.move = move; this.move = move;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
import { getPokemonNameWithAffix } from "../messages"; import { getPokemonNameWithAffix } from "../messages";
import Pokemon, { HitResult } from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import { HitResult } from "../field/pokemon";
import { getStatusEffectHealText } from "./status-effect"; import { getStatusEffectHealText } from "./status-effect";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { DoubleBerryEffectAbAttr, PostItemLostAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs, applyPostItemLostAbAttrs } from "./ability"; import { DoubleBerryEffectAbAttr, PostItemLostAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs, applyPostItemLostAbAttrs } from "./ability";
@ -9,6 +10,7 @@ import { BerryType } from "#enums/berry-type";
import { Stat, type BattleStat } from "#app/enums/stat"; import { Stat, type BattleStat } from "#app/enums/stat";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { globalScene } from "#app/global-scene";
export function getBerryName(berryType: BerryType): string { export function getBerryName(berryType: BerryType): string {
return i18next.t(`berry:${BerryType[berryType]}.name`); return i18next.t(`berry:${BerryType[berryType]}.name`);
@ -73,7 +75,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
} }
const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4)); const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4));
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed);
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(),
hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true)); hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
}; };
@ -83,7 +85,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
pokemon.battleData.berriesEaten.push(berryType); pokemon.battleData.berriesEaten.push(berryType);
} }
if (pokemon.status) { if (pokemon.status) {
pokemon.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); globalScene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)));
} }
pokemon.resetStatus(true, true); pokemon.resetStatus(true, true);
pokemon.updateInfo(); pokemon.updateInfo();
@ -102,7 +104,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
const stat: BattleStat = berryType - BerryType.ENIGMA; const stat: BattleStat = berryType - BerryType.ENIGMA;
const statStages = new Utils.NumberHolder(1); const statStages = new Utils.NumberHolder(1);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], statStages.value)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], statStages.value));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
}; };
case BerryType.LANSAT: case BerryType.LANSAT:
@ -121,7 +123,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK); const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK);
const stages = new Utils.NumberHolder(2); const stages = new Utils.NumberHolder(2);
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randStat ], stages.value)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ randStat ], stages.value));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
}; };
case BerryType.LEPPA: case BerryType.LEPPA:
@ -132,7 +134,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
const ppRestoreMove = pokemon.getMoveset().find(m => !m?.getPpRatio()) ? pokemon.getMoveset().find(m => !m?.getPpRatio()) : pokemon.getMoveset().find(m => m!.getPpRatio() < 1); // TODO: is this bang correct? const ppRestoreMove = pokemon.getMoveset().find(m => !m?.getPpRatio()) ? pokemon.getMoveset().find(m => !m?.getPpRatio()) : pokemon.getMoveset().find(m => m!.getPpRatio() < 1); // TODO: is this bang correct?
if (ppRestoreMove !== undefined) { if (ppRestoreMove !== undefined) {
ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0); ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0);
pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) })); globalScene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) }));
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
} }
}; };

View File

@ -1,21 +1,26 @@
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import i18next from "i18next"; import i18next from "i18next";
import { defaultStarterSpecies, DexAttrProps, GameData } from "#app/system/game-data"; import type { DexAttrProps, GameData } from "#app/system/game-data";
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { defaultStarterSpecies } from "#app/system/game-data";
import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import Pokemon, { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { BattleType, FixedBattleConfig } from "#app/battle"; import { PokemonMove } from "#app/field/pokemon";
import type { FixedBattleConfig } from "#app/battle";
import { ClassicFixedBossWaves, BattleType, getRandomTrainerFunc } from "#app/battle";
import Trainer, { TrainerVariant } from "#app/field/trainer"; import Trainer, { TrainerVariant } from "#app/field/trainer";
import { GameMode } from "#app/game-mode"; import type { GameMode } from "#app/game-mode";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { Moves } from "#enums/moves"; import type { Moves } from "#enums/moves";
import { TypeColor, TypeShadow } from "#enums/color"; import { TypeColor, TypeShadow } from "#enums/color";
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { pokemonFormChanges } from "#app/data/pokemon-forms"; import { pokemonFormChanges } from "#app/data/pokemon-forms";
import { ModifierTier } from "#app/modifier/modifier-tier";
/** A constant for the default max cost of the starting party before a run */ /** A constant for the default max cost of the starting party before a run */
const DEFAULT_PARTY_MAX_COST = 10; const DEFAULT_PARTY_MAX_COST = 10;
@ -84,6 +89,11 @@ export enum ChallengeType {
* Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED. * Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED.
*/ */
MOVE_WEIGHT, MOVE_WEIGHT,
/**
* Modifies what the pokemon stats for Flip Stat Mode.
*/
FLIP_STAT,
} }
/** /**
@ -401,6 +411,16 @@ export abstract class Challenge {
applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean { applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean {
return false; return false;
} }
/**
* An apply function for FlipStats. Derived classes should alter this.
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param baseStats What are the stats to flip.
* @returns {@link boolean} Whether this function did anything.
*/
applyFlipStat(pokemon: Pokemon, baseStats: number[]) {
return false;
}
} }
type ChallengeCondition = (data: GameData) => boolean; type ChallengeCondition = (data: GameData) => boolean;
@ -445,30 +465,64 @@ export class SingleGenerationChallenge extends Challenge {
return false; return false;
} }
applyFixedBattle(waveIndex: Number, battleConfig: FixedBattleConfig): boolean { applyFixedBattle(waveIndex: number, battleConfig: FixedBattleConfig): boolean {
let trainerTypes: TrainerType[] = []; let trainerTypes: (TrainerType | TrainerType[])[] = [];
const evilTeamWaves: number[] = [ ClassicFixedBossWaves.EVIL_GRUNT_1, ClassicFixedBossWaves.EVIL_GRUNT_2, ClassicFixedBossWaves.EVIL_GRUNT_3, ClassicFixedBossWaves.EVIL_ADMIN_1, ClassicFixedBossWaves.EVIL_GRUNT_4, ClassicFixedBossWaves.EVIL_ADMIN_2, ClassicFixedBossWaves.EVIL_BOSS_1, ClassicFixedBossWaves.EVIL_BOSS_2 ];
const evilTeamGrunts = [[ TrainerType.ROCKET_GRUNT ], [ TrainerType.ROCKET_GRUNT ], [ TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT ], [ TrainerType.GALACTIC_GRUNT ], [ TrainerType.PLASMA_GRUNT ], [ TrainerType.FLARE_GRUNT ], [ TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT ], [ TrainerType.MACRO_GRUNT ], [ TrainerType.STAR_GRUNT ]];
const evilTeamAdmins = [[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [[ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ]], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], [ TrainerType.FABA, TrainerType.PLUMERIA ], [ TrainerType.OLEANA ], [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ]];
const evilTeamBosses = [[ TrainerType.ROCKET_BOSS_GIOVANNI_1 ], [ TrainerType.ROCKET_BOSS_GIOVANNI_1 ], [ TrainerType.MAXIE, TrainerType.ARCHIE ], [ TrainerType.CYRUS ], [ TrainerType.GHETSIS ], [ TrainerType.LYSANDRE ], [ TrainerType.LUSAMINE, TrainerType.GUZMA ], [ TrainerType.ROSE ], [ TrainerType.PENNY ]];
const evilTeamBossRematches = [[ TrainerType.ROCKET_BOSS_GIOVANNI_2 ], [ TrainerType.ROCKET_BOSS_GIOVANNI_2 ], [ TrainerType.MAXIE_2, TrainerType.ARCHIE_2 ], [ TrainerType.CYRUS_2 ], [ TrainerType.GHETSIS_2 ], [ TrainerType.LYSANDRE_2 ], [ TrainerType.LUSAMINE_2, TrainerType.GUZMA_2 ], [ TrainerType.ROSE_2 ], [ TrainerType.PENNY_2 ]];
switch (waveIndex) { switch (waveIndex) {
case 182: case ClassicFixedBossWaves.EVIL_GRUNT_1:
trainerTypes = evilTeamGrunts[this.value - 1];
battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true));
return true;
case ClassicFixedBossWaves.EVIL_GRUNT_2:
case ClassicFixedBossWaves.EVIL_GRUNT_3:
case ClassicFixedBossWaves.EVIL_GRUNT_4:
trainerTypes = evilTeamGrunts[this.value - 1];
break;
case ClassicFixedBossWaves.EVIL_ADMIN_1:
case ClassicFixedBossWaves.EVIL_ADMIN_2:
trainerTypes = evilTeamAdmins[this.value - 1];
break;
case ClassicFixedBossWaves.EVIL_BOSS_1:
trainerTypes = evilTeamBosses[this.value - 1];
battleConfig.setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1).setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false });
return true;
case ClassicFixedBossWaves.EVIL_BOSS_2:
trainerTypes = evilTeamBossRematches[this.value - 1];
battleConfig.setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1).setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true))
.setCustomModifierRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA ], allowLuckUpgrades: false });
return true;
case ClassicFixedBossWaves.ELITE_FOUR_1:
trainerTypes = [ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, Utils.randSeedItem([ TrainerType.HALA, TrainerType.MOLAYNE ]), TrainerType.MARNIE_ELITE, TrainerType.RIKA ]; trainerTypes = [ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, Utils.randSeedItem([ TrainerType.HALA, TrainerType.MOLAYNE ]), TrainerType.MARNIE_ELITE, TrainerType.RIKA ];
break; break;
case 184: case ClassicFixedBossWaves.ELITE_FOUR_2:
trainerTypes = [ TrainerType.BRUNO, TrainerType.KOGA, TrainerType.PHOEBE, TrainerType.BERTHA, TrainerType.MARSHAL, TrainerType.SIEBOLD, TrainerType.OLIVIA, TrainerType.NESSA_ELITE, TrainerType.POPPY ]; trainerTypes = [ TrainerType.BRUNO, TrainerType.KOGA, TrainerType.PHOEBE, TrainerType.BERTHA, TrainerType.MARSHAL, TrainerType.SIEBOLD, TrainerType.OLIVIA, TrainerType.NESSA_ELITE, TrainerType.POPPY ];
break; break;
case 186: case ClassicFixedBossWaves.ELITE_FOUR_3:
trainerTypes = [ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, Utils.randSeedItem([ TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE ]), TrainerType.LARRY_ELITE ]; trainerTypes = [ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, Utils.randSeedItem([ TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE ]), TrainerType.LARRY_ELITE ];
break; break;
case 188: case ClassicFixedBossWaves.ELITE_FOUR_4:
trainerTypes = [ TrainerType.LANCE, TrainerType.KAREN, TrainerType.DRAKE, TrainerType.LUCIAN, TrainerType.CAITLIN, TrainerType.DRASNA, TrainerType.KAHILI, TrainerType.RAIHAN_ELITE, TrainerType.HASSEL ]; trainerTypes = [ TrainerType.LANCE, TrainerType.KAREN, TrainerType.DRAKE, TrainerType.LUCIAN, TrainerType.CAITLIN, TrainerType.DRASNA, TrainerType.KAHILI, TrainerType.RAIHAN_ELITE, TrainerType.HASSEL ];
break; break;
case 190: case ClassicFixedBossWaves.CHAMPION:
trainerTypes = [ TrainerType.BLUE, Utils.randSeedItem([ TrainerType.RED, TrainerType.LANCE_CHAMPION ]), Utils.randSeedItem([ TrainerType.STEVEN, TrainerType.WALLACE ]), TrainerType.CYNTHIA, Utils.randSeedItem([ TrainerType.ALDER, TrainerType.IRIS ]), TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, Utils.randSeedItem([ TrainerType.GEETA, TrainerType.NEMONA ]) ]; trainerTypes = [ TrainerType.BLUE, Utils.randSeedItem([ TrainerType.RED, TrainerType.LANCE_CHAMPION ]), Utils.randSeedItem([ TrainerType.STEVEN, TrainerType.WALLACE ]), TrainerType.CYNTHIA, Utils.randSeedItem([ TrainerType.ALDER, TrainerType.IRIS ]), TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, Utils.randSeedItem([ TrainerType.GEETA, TrainerType.NEMONA ]) ];
break; break;
} }
if (trainerTypes.length === 0) { if (trainerTypes.length === 0) {
return false; return false;
} else { } else if (evilTeamWaves.includes(waveIndex)) {
battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(scene => new Trainer(scene, trainerTypes[this.value - 1], TrainerVariant.DEFAULT)); battleConfig.setBattleType(BattleType.TRAINER).setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1).setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true));
return true; return true;
} else if (waveIndex >= ClassicFixedBossWaves.ELITE_FOUR_1 && waveIndex <= ClassicFixedBossWaves.CHAMPION) {
const ttypes = trainerTypes as TrainerType[];
battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(() => new Trainer(ttypes[this.value - 1], TrainerVariant.DEFAULT));
return true;
} else {
return false;
} }
} }
@ -701,6 +755,33 @@ export class InverseBattleChallenge extends Challenge {
} }
} }
/**
* Implements a flip stat challenge.
*/
export class FlipStatChallenge extends Challenge {
constructor() {
super(Challenges.FLIP_STAT, 1);
}
override applyFlipStat(pokemon: Pokemon, baseStats: number[]) {
const origStats = Utils.deepCopy(baseStats);
baseStats[0] = origStats[5];
baseStats[1] = origStats[4];
baseStats[2] = origStats[3];
baseStats[3] = origStats[2];
baseStats[4] = origStats[1];
baseStats[5] = origStats[0];
return true;
}
static loadChallenge(source: FlipStatChallenge | any): FlipStatChallenge {
const newChallenge = new FlipStatChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
/** /**
* Lowers the amount of starter points available. * Lowers the amount of starter points available.
*/ */
@ -886,6 +967,9 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
* @returns True if any challenge was successfully applied. * @returns True if any challenge was successfully applied.
*/ */
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_WEIGHT, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, weight: Utils.IntegerHolder): boolean; export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_WEIGHT, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, weight: Utils.IntegerHolder): boolean;
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType, ...args: any[]): boolean { export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType, ...args: any[]): boolean {
let ret = false; let ret = false;
gameMode.challenges.forEach(c => { gameMode.challenges.forEach(c => {
@ -930,6 +1014,9 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
case ChallengeType.MOVE_WEIGHT: case ChallengeType.MOVE_WEIGHT:
ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]); ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]);
break; break;
case ChallengeType.FLIP_STAT:
ret ||= c.applyFlipStat(args[0], args[1]);
break;
} }
} }
}); });
@ -955,6 +1042,8 @@ export function copyChallenge(source: Challenge | any): Challenge {
return FreshStartChallenge.loadChallenge(source); return FreshStartChallenge.loadChallenge(source);
case Challenges.INVERSE_BATTLE: case Challenges.INVERSE_BATTLE:
return InverseBattleChallenge.loadChallenge(source); return InverseBattleChallenge.loadChallenge(source);
case Challenges.FLIP_STAT:
return FlipStatChallenge.loadChallenge(source);
} }
throw new Error("Unknown challenge copied"); throw new Error("Unknown challenge copied");
} }
@ -967,5 +1056,6 @@ export function initChallenges() {
new SingleTypeChallenge(), new SingleTypeChallenge(),
new FreshStartChallenge(), new FreshStartChallenge(),
new InverseBattleChallenge(), new InverseBattleChallenge(),
new FlipStatChallenge()
); );
} }

View File

@ -1,11 +1,12 @@
import { Abilities } from "#enums/abilities"; import type { Abilities } from "#enums/abilities";
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import { isNullOrUndefined } from "#app/utils"; import { isNullOrUndefined } from "#app/utils";
import { Nature } from "#enums/nature"; import type { Nature } from "#enums/nature";
/** /**
* Data that can customize a Pokemon in non-standard ways from its Species * Data that can customize a Pokemon in non-standard ways from its Species
* Currently only used by Mystery Encounters and Mints. * Used by Mystery Encounters and Mints
* Also used as a counter how often a Pokemon got hit until new arena encounter
*/ */
export class CustomPokemonData { export class CustomPokemonData {
public spriteScale: number; public spriteScale: number;
@ -13,6 +14,8 @@ export class CustomPokemonData {
public passive: Abilities | -1; public passive: Abilities | -1;
public nature: Nature | -1; public nature: Nature | -1;
public types: Type[]; public types: Type[];
/** `hitsReceivedCount` aka `hitsRecCount` saves how often the pokemon got hit until a new arena encounter (used for Rage Fist) */
public hitsRecCount: number;
constructor(data?: CustomPokemonData | Partial<CustomPokemonData>) { constructor(data?: CustomPokemonData | Partial<CustomPokemonData>) {
if (!isNullOrUndefined(data)) { if (!isNullOrUndefined(data)) {
@ -24,5 +27,10 @@ export class CustomPokemonData {
this.passive = this.passive ?? -1; this.passive = this.passive ?? -1;
this.nature = this.nature ?? -1; this.nature = this.nature ?? -1;
this.types = this.types ?? []; this.types = this.types ?? [];
this.hitsRecCount = this.hitsRecCount ?? 0;
}
resetHitReceivedCount(): void {
this.hitsRecCount = 0;
} }
} }

View File

@ -1,12 +1,14 @@
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import { Species } from "#enums/species"; import type { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { PlayerPokemon } from "#app/field/pokemon"; import { PlayerPokemon } from "#app/field/pokemon";
import { Starter } from "#app/ui/starter-select-ui-handler"; import type { Starter } from "#app/ui/starter-select-ui-handler";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import PokemonSpecies, { PokemonSpeciesForm, getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
import { Biome } from "#app/enums/biome";
export interface DailyRunConfig { export interface DailyRunConfig {
seed: integer; seed: integer;
@ -21,17 +23,17 @@ export function fetchDailyRunSeed(): Promise<string | null> {
}); });
} }
export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[] { export function getDailyRunStarters(seed: string): Starter[] {
const starters: Starter[] = []; const starters: Starter[] = [];
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
const startingLevel = scene.gameMode.getStartingLevel(); const startingLevel = globalScene.gameMode.getStartingLevel();
if (/\d{18}$/.test(seed)) { if (/\d{18}$/.test(seed)) {
for (let s = 0; s < 3; s++) { for (let s = 0; s < 3; s++) {
const offset = 6 + s * 6; const offset = 6 + s * 6;
const starterSpeciesForm = getPokemonSpeciesForm(parseInt(seed.slice(offset, offset + 4)) as Species, parseInt(seed.slice(offset + 4, offset + 6))); const starterSpeciesForm = getPokemonSpeciesForm(parseInt(seed.slice(offset, offset + 4)) as Species, parseInt(seed.slice(offset + 4, offset + 6)));
starters.push(getDailyRunStarter(scene, starterSpeciesForm, startingLevel)); starters.push(getDailyRunStarter(starterSpeciesForm, startingLevel));
} }
return; return;
} }
@ -48,17 +50,17 @@ export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[]
.filter(s => speciesStarterCosts[s] === cost); .filter(s => speciesStarterCosts[s] === cost);
const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies)); const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies));
const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER)); const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER));
starters.push(getDailyRunStarter(scene, starterSpecies, startingLevel)); starters.push(getDailyRunStarter(starterSpecies, startingLevel));
} }
}, 0, seed); }, 0, seed);
return starters; return starters;
} }
function getDailyRunStarter(scene: BattleScene, starterSpeciesForm: PokemonSpeciesForm, startingLevel: integer): Starter { function getDailyRunStarter(starterSpeciesForm: PokemonSpeciesForm, startingLevel: integer): Starter {
const starterSpecies = starterSpeciesForm instanceof PokemonSpecies ? starterSpeciesForm : getPokemonSpecies(starterSpeciesForm.speciesId); const starterSpecies = starterSpeciesForm instanceof PokemonSpecies ? starterSpeciesForm : getPokemonSpecies(starterSpeciesForm.speciesId);
const formIndex = starterSpeciesForm instanceof PokemonSpecies ? undefined : starterSpeciesForm.formIndex; const formIndex = starterSpeciesForm instanceof PokemonSpecies ? undefined : starterSpeciesForm.formIndex;
const pokemon = new PlayerPokemon(scene, starterSpecies, startingLevel, undefined, formIndex, undefined, undefined, undefined, undefined, undefined, undefined); const pokemon = new PlayerPokemon(starterSpecies, startingLevel, undefined, formIndex, undefined, undefined, undefined, undefined, undefined, undefined);
const starter: Starter = { const starter: Starter = {
species: starterSpecies, species: starterSpecies,
dexAttr: pokemon.getDexAttr(), dexAttr: pokemon.getDexAttr(),
@ -70,3 +72,76 @@ function getDailyRunStarter(scene: BattleScene, starterSpeciesForm: PokemonSpeci
pokemon.destroy(); pokemon.destroy();
return starter; return starter;
} }
interface BiomeWeights {
[key: integer]: integer
}
// Initially weighted by amount of exits each biome has
// Town and End are set to 0 however
// And some other biomes were balanced +1/-1 based on average size of the total daily.
const dailyBiomeWeights: BiomeWeights = {
[Biome.CAVE]: 3,
[Biome.LAKE]: 3,
[Biome.PLAINS]: 3,
[Biome.SNOWY_FOREST]: 3,
[Biome.SWAMP]: 3, // 2 -> 3
[Biome.TALL_GRASS]: 3, // 2 -> 3
[Biome.ABYSS]: 2, // 3 -> 2
[Biome.RUINS]: 2,
[Biome.BADLANDS]: 2,
[Biome.BEACH]: 2,
[Biome.CONSTRUCTION_SITE]: 2,
[Biome.DESERT]: 2,
[Biome.DOJO]: 2, // 3 -> 2
[Biome.FACTORY]: 2,
[Biome.FAIRY_CAVE]: 2,
[Biome.FOREST]: 2,
[Biome.GRASS]: 2, // 1 -> 2
[Biome.MEADOW]: 2,
[Biome.MOUNTAIN]: 2, // 3 -> 2
[Biome.SEA]: 2,
[Biome.SEABED]: 2,
[Biome.SLUM]: 2,
[Biome.TEMPLE]: 2, // 3 -> 2
[Biome.VOLCANO]: 2,
[Biome.GRAVEYARD]: 1,
[Biome.ICE_CAVE]: 1,
[Biome.ISLAND]: 1,
[Biome.JUNGLE]: 1,
[Biome.LABORATORY]: 1,
[Biome.METROPOLIS]: 1,
[Biome.POWER_PLANT]: 1,
[Biome.SPACE]: 1,
[Biome.WASTELAND]: 1,
[Biome.TOWN]: 0,
[Biome.END]: 0,
};
export function getDailyStartingBiome(): Biome {
const biomes = Utils.getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
let totalWeight = 0;
const biomeThresholds: integer[] = [];
for (const biome of biomes) {
// Keep track of the total weight
totalWeight += dailyBiomeWeights[biome];
// Keep track of each biomes cumulative weight
biomeThresholds.push(totalWeight);
}
const randInt = Utils.randSeedInt(totalWeight);
for (let i = 0; i < biomes.length; i++) {
if (randInt < biomeThresholds[i]) {
return biomes[i];
}
}
// Fallback in case something went wrong
return biomes[Utils.randSeedInt(biomes.length)];
}

View File

@ -1,6 +1,6 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { DexEntry, StarterDataEntry } from "#app/system/game-data"; import type { DexEntry, StarterDataEntry } from "#app/system/game-data";
/** /**
* Stores data associated with a specific egg and the hatched pokemon * Stores data associated with a specific egg and the hatched pokemon
@ -17,11 +17,8 @@ export class EggHatchData {
public dexEntryBeforeUpdate: DexEntry; public dexEntryBeforeUpdate: DexEntry;
/** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */ /** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */
public starterDataEntryBeforeUpdate: StarterDataEntry; public starterDataEntryBeforeUpdate: StarterDataEntry;
/** reference to the battle scene to get gamedata and update dex */
private scene: BattleScene;
constructor(scene: BattleScene, pokemon: PlayerPokemon, eggMoveIndex: number) { constructor(pokemon: PlayerPokemon, eggMoveIndex: number) {
this.scene = scene;
this.pokemon = pokemon; this.pokemon = pokemon;
this.eggMoveIndex = eggMoveIndex; this.eggMoveIndex = eggMoveIndex;
} }
@ -39,8 +36,8 @@ export class EggHatchData {
* Used before updating the dex, so comparing the pokemon to these entries will show the new attributes * Used before updating the dex, so comparing the pokemon to these entries will show the new attributes
*/ */
setDex() { setDex() {
const currDexEntry = this.scene.gameData.dexData[this.pokemon.species.speciesId]; const currDexEntry = globalScene.gameData.dexData[this.pokemon.species.speciesId];
const currStarterDataEntry = this.scene.gameData.starterData[this.pokemon.species.getRootSpeciesId()]; const currStarterDataEntry = globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId()];
this.dexEntryBeforeUpdate = { this.dexEntryBeforeUpdate = {
seenAttr: currDexEntry.seenAttr, seenAttr: currDexEntry.seenAttr,
caughtAttr: currDexEntry.caughtAttr, caughtAttr: currDexEntry.caughtAttr,
@ -86,9 +83,9 @@ export class EggHatchData {
*/ */
updatePokemon(showMessage : boolean = false) { updatePokemon(showMessage : boolean = false) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
this.scene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => { globalScene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => {
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); globalScene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => { globalScene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => {
this.setEggMoveUnlocked(value); this.setEggMoveUnlocked(value);
resolve(); resolve();
}); });

View File

@ -1,11 +1,13 @@
import BattleScene from "#app/battle-scene"; import type BattleScene from "#app/battle-scene";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; import { globalScene } from "#app/global-scene";
import type PokemonSpecies from "#app/data/pokemon-species";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { speciesStarterCosts } from "#app/data/balance/starters"; import { speciesStarterCosts } from "#app/data/balance/starters";
import { VariantTier } from "#enums/variant-tier"; import { VariantTier } from "#enums/variant-tier";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
import { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import i18next from "i18next"; import i18next from "i18next";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
@ -22,9 +24,8 @@ export interface IEggOptions {
/** Timestamp when this egg got created */ /** Timestamp when this egg got created */
timestamp?: number; timestamp?: number;
/** /**
* Defines if the egg got pulled from a gacha or not. If true, egg pity and pull statistics will be applyed. * Defines if the egg got pulled from a gacha or not. If true, egg pity and pull statistics will be applied.
* Egg will be automaticly added to the game data. * Egg will be automaticly added to the game data.
* NEEDS `scene` `eggOption` to work.
*/ */
pulled?: boolean; pulled?: boolean;
/** /**
@ -32,7 +33,7 @@ export interface IEggOptions {
* Will also define the text displayed in the egg list. * Will also define the text displayed in the egg list.
*/ */
sourceType?: EggSourceType; sourceType?: EggSourceType;
/** Needs to be defined if `eggOption` pulled is defined or if no species or `isShiny` is defined since this will be needed to generate them. */ /** Legacy field, kept for backwards-compatibility */
scene?: BattleScene; scene?: BattleScene;
/** /**
* Sets the tier of the egg. Only species of this tier can be hatched from this egg. * Sets the tier of the egg. Only species of this tier can be hatched from this egg.
@ -41,10 +42,7 @@ export interface IEggOptions {
tier?: EggTier; tier?: EggTier;
/** Sets how many waves it will take till this egg hatches. */ /** Sets how many waves it will take till this egg hatches. */
hatchWaves?: number; hatchWaves?: number;
/** /** Sets the exact species that will hatch from this egg. */
* Sets the exact species that will hatch from this egg.
* Needs `scene` `eggOption` if not provided.
*/
species?: Species; species?: Species;
/** Defines if the hatched pokemon will be a shiny. */ /** Defines if the hatched pokemon will be a shiny. */
isShiny?: boolean; isShiny?: boolean;
@ -56,8 +54,7 @@ export interface IEggOptions {
* Defines if the egg will hatch with the hidden ability of this species. * Defines if the egg will hatch with the hidden ability of this species.
* If no hidden ability exist, a random one will get choosen. * If no hidden ability exist, a random one will get choosen.
*/ */
overrideHiddenAbility?: boolean, overrideHiddenAbility?: boolean;
/** Can customize the message displayed for where the egg was obtained */ /** Can customize the message displayed for where the egg was obtained */
eggDescriptor?: string; eggDescriptor?: string;
} }
@ -148,7 +145,7 @@ export class Egg {
// If egg was pulled, check if egg pity needs to override the egg tier // If egg was pulled, check if egg pity needs to override the egg tier
if (eggOptions?.pulled) { if (eggOptions?.pulled) {
// Needs this._tier and this._sourceType to work // Needs this._tier and this._sourceType to work
this.checkForPityTierOverrides(eggOptions.scene!); // TODO: is this bang correct? this.checkForPityTierOverrides();
} }
this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier);
@ -160,7 +157,7 @@ export class Egg {
// First roll shiny and variant so we can filter if species with an variant exist // First roll shiny and variant so we can filter if species with an variant exist
this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny());
this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant());
this._species = eggOptions?.species ?? this.rollSpecies(eggOptions!.scene!)!; // TODO: Are those bangs correct? this._species = eggOptions?.species ?? this.rollSpecies()!; // TODO: Is this bang correct?
this._overrideHiddenAbility = eggOptions?.overrideHiddenAbility ?? false; this._overrideHiddenAbility = eggOptions?.overrideHiddenAbility ?? false;
@ -178,19 +175,15 @@ export class Egg {
// Needs this._tier so it needs to be generated afer the tier override if bought from same species // Needs this._tier so it needs to be generated afer the tier override if bought from same species
this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex(); this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex();
if (eggOptions?.pulled) { if (eggOptions?.pulled) {
this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct? this.increasePullStatistic();
this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct? this.addEggToGameData();
} }
}; };
if (eggOptions?.scene) { const seedOverride = Utils.randomString(24);
const seedOverride = Utils.randomString(24); globalScene.executeWithSeedOffset(() => {
eggOptions?.scene.executeWithSeedOffset(() => {
generateEggProperties(eggOptions);
}, 0, seedOverride);
} else { // For legacy eggs without scene
generateEggProperties(eggOptions); generateEggProperties(eggOptions);
} }, 0, seedOverride);
this._eggDescriptor = eggOptions?.eggDescriptor; this._eggDescriptor = eggOptions?.eggDescriptor;
} }
@ -212,14 +205,14 @@ export class Egg {
} }
// Generates a PlayerPokemon from an egg // Generates a PlayerPokemon from an egg
public generatePlayerPokemon(scene: BattleScene): PlayerPokemon { public generatePlayerPokemon(): PlayerPokemon {
let ret: PlayerPokemon; let ret: PlayerPokemon;
const generatePlayerPokemonHelper = (scene: BattleScene) => { const generatePlayerPokemonHelper = () => {
// Legacy egg wants to hatch. Generate missing properties // Legacy egg wants to hatch. Generate missing properties
if (!this._species) { if (!this._species) {
this._isShiny = this.rollShiny(); this._isShiny = this.rollShiny();
this._species = this.rollSpecies(scene!)!; // TODO: are these bangs correct? this._species = this.rollSpecies()!; // TODO: is this bang correct?
} }
let pokemonSpecies = getPokemonSpecies(this._species); let pokemonSpecies = getPokemonSpecies(this._species);
@ -238,7 +231,7 @@ export class Egg {
} }
// This function has way to many optional parameters // This function has way to many optional parameters
ret = scene.addPlayerPokemon(pokemonSpecies, 1, abilityIndex, undefined, undefined, false); ret = globalScene.addPlayerPokemon(pokemonSpecies, 1, abilityIndex, undefined, undefined, false);
ret.shiny = this._isShiny; ret.shiny = this._isShiny;
ret.variant = this._variantTier; ret.variant = this._variantTier;
@ -250,16 +243,16 @@ export class Egg {
}; };
ret = ret!; // Tell TS compiler it's defined now ret = ret!; // Tell TS compiler it's defined now
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
generatePlayerPokemonHelper(scene); generatePlayerPokemonHelper();
}, this._id, EGG_SEED.toString()); }, this._id, EGG_SEED.toString());
return ret; return ret;
} }
// Doesn't need to be called if the egg got pulled by a gacha machiene // Doesn't need to be called if the egg got pulled by a gacha machiene
public addEggToGameData(scene: BattleScene): void { public addEggToGameData(): void {
scene.gameData.eggs.push(this); globalScene.gameData.eggs.push(this);
} }
public getEggDescriptor(): string { public getEggDescriptor(): string {
@ -291,12 +284,12 @@ export class Egg {
return i18next.t("egg:hatchWavesMessageLongTime"); return i18next.t("egg:hatchWavesMessageLongTime");
} }
public getEggTypeDescriptor(scene: BattleScene): string { public getEggTypeDescriptor(): string {
switch (this.sourceType) { switch (this.sourceType) {
case EggSourceType.SAME_SPECIES_EGG: case EggSourceType.SAME_SPECIES_EGG:
return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName() }); return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName() });
case EggSourceType.GACHA_LEGENDARY: case EggSourceType.GACHA_LEGENDARY:
return this._eggDescriptor ?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp)).getName()})`; return this._eggDescriptor ?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(this.timestamp)).getName()})`;
case EggSourceType.GACHA_SHINY: case EggSourceType.GACHA_SHINY:
return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny"); return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny");
case EggSourceType.GACHA_MOVE: case EggSourceType.GACHA_MOVE:
@ -356,8 +349,8 @@ export class Egg {
return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.RARE : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.EPIC : EggTier.LEGENDARY; return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.RARE : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.EPIC : EggTier.LEGENDARY;
} }
private rollSpecies(scene: BattleScene): Species | null { private rollSpecies(): Species | null {
if (!scene) { if (!globalScene) {
return null; return null;
} }
/** /**
@ -376,7 +369,7 @@ export class Egg {
} else if (this.tier === EggTier.LEGENDARY } else if (this.tier === EggTier.LEGENDARY
&& this._sourceType === EggSourceType.GACHA_LEGENDARY) { && this._sourceType === EggSourceType.GACHA_LEGENDARY) {
if (!Utils.randSeedInt(2)) { if (!Utils.randSeedInt(2)) {
return getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp); return getLegendaryGachaSpeciesForTimestamp(this.timestamp);
} }
} }
@ -410,8 +403,8 @@ export class Egg {
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1); .filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
// If this is the 10th egg without unlocking something new, attempt to force it. // If this is the 10th egg without unlocking something new, attempt to force it.
if (scene.gameData.unlockPity[this.tier] >= 9) { if (globalScene.gameData.unlockPity[this.tier] >= 9) {
const lockedPool = speciesPool.filter(s => !scene.gameData.dexData[s].caughtAttr && !scene.gameData.eggs.some(e => e.species === s)); const lockedPool = speciesPool.filter(s => !globalScene.gameData.dexData[s].caughtAttr && !globalScene.gameData.eggs.some(e => e.species === s));
if (lockedPool.length) { // Skip this if everything is unlocked if (lockedPool.length) { // Skip this if everything is unlocked
speciesPool = lockedPool; speciesPool = lockedPool;
} }
@ -454,10 +447,10 @@ export class Egg {
} }
species = species!; // tell TS compiled it's defined now! species = species!; // tell TS compiled it's defined now!
if (!!scene.gameData.dexData[species].caughtAttr || scene.gameData.eggs.some(e => e.species === species)) { if (globalScene.gameData.dexData[species].caughtAttr || globalScene.gameData.eggs.some(e => e.species === species)) {
scene.gameData.unlockPity[this.tier] = Math.min(scene.gameData.unlockPity[this.tier] + 1, 10); globalScene.gameData.unlockPity[this.tier] = Math.min(globalScene.gameData.unlockPity[this.tier] + 1, 10);
} else { } else {
scene.gameData.unlockPity[this.tier] = 0; globalScene.gameData.unlockPity[this.tier] = 0;
} }
return species; return species;
@ -465,7 +458,7 @@ export class Egg {
/** /**
* Rolls whether the egg is shiny or not. * Rolls whether the egg is shiny or not.
* @returns True if the egg is shiny * @returns `true` if the egg is shiny
**/ **/
private rollShiny(): boolean { private rollShiny(): boolean {
let shinyChance = GACHA_DEFAULT_SHINY_RATE; let shinyChance = GACHA_DEFAULT_SHINY_RATE;
@ -485,6 +478,7 @@ export class Egg {
// Uses the same logic as pokemon.generateVariant(). I would like to only have this logic in one // Uses the same logic as pokemon.generateVariant(). I would like to only have this logic in one
// place but I don't want to touch the pokemon class. // place but I don't want to touch the pokemon class.
// TODO: Remove this or replace the one in the Pokemon class.
private rollVariant(): VariantTier { private rollVariant(): VariantTier {
if (!this.isShiny) { if (!this.isShiny) {
return VariantTier.STANDARD; return VariantTier.STANDARD;
@ -500,38 +494,38 @@ export class Egg {
} }
} }
private checkForPityTierOverrides(scene: BattleScene): void { private checkForPityTierOverrides(): void {
const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0;
scene.gameData.eggPity[EggTier.RARE] += 1; globalScene.gameData.eggPity[EggTier.RARE] += 1;
scene.gameData.eggPity[EggTier.EPIC] += 1; globalScene.gameData.eggPity[EggTier.EPIC] += 1;
scene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset; globalScene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset;
// These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered. // These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered.
if (scene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) { if (globalScene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.LEGENDARY; this._tier = EggTier.LEGENDARY;
} else if (scene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) { } else if (globalScene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.EPIC; this._tier = EggTier.EPIC;
} else if (scene.gameData.eggPity[EggTier.RARE] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) { } else if (globalScene.gameData.eggPity[EggTier.RARE] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) {
this._tier = EggTier.RARE; this._tier = EggTier.RARE;
} }
scene.gameData.eggPity[this._tier] = 0; globalScene.gameData.eggPity[this._tier] = 0;
} }
private increasePullStatistic(scene: BattleScene): void { private increasePullStatistic(): void {
scene.gameData.gameStats.eggsPulled++; globalScene.gameData.gameStats.eggsPulled++;
if (this.isManaphyEgg()) { if (this.isManaphyEgg()) {
scene.gameData.gameStats.manaphyEggsPulled++; globalScene.gameData.gameStats.manaphyEggsPulled++;
this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC); this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC);
return; return;
} }
switch (this.tier) { switch (this.tier) {
case EggTier.RARE: case EggTier.RARE:
scene.gameData.gameStats.rareEggsPulled++; globalScene.gameData.gameStats.rareEggsPulled++;
break; break;
case EggTier.EPIC: case EggTier.EPIC:
scene.gameData.gameStats.epicEggsPulled++; globalScene.gameData.gameStats.epicEggsPulled++;
break; break;
case EggTier.LEGENDARY: case EggTier.LEGENDARY:
scene.gameData.gameStats.legendaryEggsPulled++; globalScene.gameData.gameStats.legendaryEggsPulled++;
break; break;
} }
} }
@ -552,7 +546,7 @@ export function getValidLegendaryGachaSpecies() : Species[] {
.filter(s => getPokemonSpecies(s).isObtainable() && s !== Species.ETERNATUS); .filter(s => getPokemonSpecies(s).isObtainable() && s !== Species.ETERNATUS);
} }
export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species { export function getLegendaryGachaSpeciesForTimestamp(timestamp: number): Species {
const legendarySpecies = getValidLegendaryGachaSpecies(); const legendarySpecies = getValidLegendaryGachaSpecies();
let ret: Species; let ret: Species;
@ -563,7 +557,7 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta
const offset = Math.floor(Math.floor(dayTimestamp / 86400000) / legendarySpecies.length); // Cycle number const offset = Math.floor(Math.floor(dayTimestamp / 86400000) / legendarySpecies.length); // Cycle number
const index = Math.floor(dayTimestamp / 86400000) % legendarySpecies.length; // Index within cycle const index = Math.floor(dayTimestamp / 86400000) % legendarySpecies.length; // Index within cycle
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
ret = Phaser.Math.RND.shuffle(legendarySpecies)[index]; ret = Phaser.Math.RND.shuffle(legendarySpecies)[index];
}, offset, EGG_SEED.toString()); }, offset, EGG_SEED.toString());
ret = ret!; // tell TS compiler it's ret = ret!; // tell TS compiler it's

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,17 @@
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { globalScene } from "#app/global-scene";
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { trainerConfigs, } from "#app/data/trainer-config"; import { trainerConfigs, } from "#app/data/trainer-config";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { randSeedInt } from "#app/utils"; import { randSeedInt } from "#app/utils";
import i18next from "i18next"; import i18next from "i18next";
import { IEggOptions } from "#app/data/egg"; import type { IEggOptions } from "#app/data/egg";
import { EggSourceType } from "#enums/egg-source-types"; import { EggSourceType } from "#enums/egg-source-types";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { PartyHealPhase } from "#app/phases/party-heal-phase";
@ -36,8 +38,8 @@ export const ATrainersTestEncounter: MysteryEncounter =
}, },
]) ])
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Randomly pick from 1 of the 5 stat trainers to spawn // Randomly pick from 1 of the 5 stat trainers to spawn
let trainerType: TrainerType; let trainerType: TrainerType;
@ -138,23 +140,22 @@ export const ATrainersTestEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip` buttonTooltip: `${namespace}:option.1.tooltip`
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Battle the stat trainer for an Egg and great rewards // Battle the stat trainer for an Egg and great rewards
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
const eggOptions: IEggOptions = { const eggOptions: IEggOptions = {
scene,
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: encounter.misc.trainerEggDescription, eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.EPIC tier: EggTier.EPIC
}; };
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`));
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
} }
) )
.withSimpleOption( .withSimpleOption(
@ -162,21 +163,20 @@ export const ATrainersTestEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip` buttonTooltip: `${namespace}:option.2.tooltip`
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Full heal party // Full heal party
scene.unshiftPhase(new PartyHealPhase(scene, true)); globalScene.unshiftPhase(new PartyHealPhase(true));
const eggOptions: IEggOptions = { const eggOptions: IEggOptions = {
scene,
pulled: false, pulled: false,
sourceType: EggSourceType.EVENT, sourceType: EggSourceType.EVENT,
eggDescriptor: encounter.misc.trainerEggDescription, eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.RARE tier: EggTier.RARE
}; };
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`)); encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`));
setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]); setEncounterRewards({ fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withOutroDialogue([ .withOutroDialogue([

View File

@ -1,10 +1,14 @@
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
@ -19,8 +23,8 @@ import { BattlerIndex } from "#app/battle";
import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyModifierTypeToPlayerPokemon, catchPokemon, getHighestLevelPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { BerryType } from "#enums/berry-type"; import type { BerryType } from "#enums/berry-type";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
@ -170,18 +174,18 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
scene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav"); globalScene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav");
scene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3"); globalScene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3");
// Get all player berry items, remove from party, and store reference // Get all player berry items, remove from party, and store reference
const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
// Sort berries by party member ID to more easily re-add later if necessary // Sort berries by party member ID to more easily re-add later if necessary
const berryItemsMap = new Map<number, BerryModifier[]>(); const berryItemsMap = new Map<number, BerryModifier[]>();
scene.getPlayerParty().forEach(pokemon => { globalScene.getPlayerParty().forEach(pokemon => {
const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id); const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id);
if (pokemonBerries?.length > 0) { if (pokemonBerries?.length > 0) {
berryItemsMap.set(pokemon.id, pokemonBerries); berryItemsMap.set(pokemon.id, pokemonBerries);
@ -196,7 +200,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// Can't define stack count on a ModifierType, have to just create separate instances for each stack // Can't define stack count on a ModifierType, have to just create separate instances for each stack
// Overflow berries will be "lost" on the boss, but it's un-catchable anyway // Overflow berries will be "lost" on the boss, but it's un-catchable anyway
for (let i = 0; i < berryMod.stackCount; i++) { for (let i = 0; i < berryMod.stackCount; i++) {
const modifierType = generateModifierType(scene, modifierTypes.BERRY, [ berryMod.berryType ]) as PokemonHeldItemModifierType; const modifierType = generateModifierType(modifierTypes.BERRY, [ berryMod.berryType ]) as PokemonHeldItemModifierType;
bossModifierConfigs.push({ modifier: modifierType }); bossModifierConfigs.push({ modifier: modifierType });
} }
}); });
@ -204,7 +208,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
// Do NOT remove the real berries yet or else it will be persisted in the session data // Do NOT remove the real berries yet or else it will be persisted in the session data
// SpDef buff below wave 50, +1 to all stats otherwise // SpDef buff below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = globalScene.currentBattle.waveIndex < 50 ?
[ Stat.SPDEF ] : [ Stat.SPDEF ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ]; [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
@ -221,8 +225,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
modifierConfigs: bossModifierConfigs, modifierConfigs: bossModifierConfigs,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.boss_enraged`); queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
} }
} }
], ],
@ -233,18 +237,18 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
doGreedentSpriteSteal(scene); doGreedentSpriteSteal();
doBerrySpritePile(scene); doBerrySpritePile();
// Remove the berries from the party // Remove the berries from the party
// Session has been safely saved at this point, so data won't be lost // Session has been safely saved at this point, so data won't be lost
const berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems = globalScene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
berryItems.forEach(berryMod => { berryItems.forEach(berryMod => {
scene.removeModifier(berryMod); globalScene.removeModifier(berryMod);
}); });
scene.updateModifiers(true); globalScene.updateModifiers(true);
return true; return true;
}) })
@ -260,26 +264,26 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Provides 1x Reviver Seed to each party member at end of battle // Provides 1x Reviver Seed to each party member at end of battle
const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED); const revSeed = generateModifierType(modifierTypes.REVIVER_SEED);
encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name")); encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name"));
const givePartyPokemonReviverSeeds = () => { const givePartyPokemonReviverSeeds = () => {
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
party.forEach(p => { party.forEach(p => {
const heldItems = p.getHeldItems(); const heldItems = p.getHeldItems();
if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) { if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) {
const seedModifier = revSeed.newModifier(p); const seedModifier = revSeed.newModifier(p);
scene.addModifier(seedModifier, false, false, false, true); globalScene.addModifier(seedModifier, false, false, false, true);
} }
}); });
queueEncounterMessage(scene, `${namespace}:option.1.food_stash`); queueEncounterMessage(`${namespace}:option.1.food_stash`);
}; };
setEncounterRewards(scene, { fillRemaining: true }, undefined, givePartyPokemonReviverSeeds); setEncounterRewards({ fillRemaining: true }, undefined, givePartyPokemonReviverSeeds);
encounter.startOfBattleEffects.push({ encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY, sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [ BattlerIndex.ENEMY ], targets: [ BattlerIndex.ENEMY ],
@ -287,8 +291,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
ignorePp: true ignorePp: true
}); });
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
}) })
.build() .build()
) )
@ -304,12 +308,12 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const berryMap = encounter.misc.berryItemsMap; const berryMap = encounter.misc.berryItemsMap;
// Returns 2/5 of the berries stolen to each Pokemon // Returns 2/5 of the berries stolen to each Pokemon
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
party.forEach(pokemon => { party.forEach(pokemon => {
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id); const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
const berryTypesAsArray: BerryType[] = []; const berryTypesAsArray: BerryType[] = [];
@ -322,15 +326,15 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
Phaser.Math.RND.shuffle(berryTypesAsArray); Phaser.Math.RND.shuffle(berryTypesAsArray);
const randBerryType = berryTypesAsArray.pop(); const randBerryType = berryTypesAsArray.pop();
const berryModType = generateModifierType(scene, modifierTypes.BERRY, [ randBerryType ]) as BerryModifierType; const berryModType = generateModifierType(modifierTypes.BERRY, [ randBerryType ]) as BerryModifierType;
applyModifierTypeToPlayerPokemon(scene, pokemon, berryModType); applyModifierTypeToPlayerPokemon(pokemon, berryModType);
} }
} }
}); });
await scene.updateModifiers(true); await globalScene.updateModifiers(true);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -346,36 +350,36 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Animate berries being eaten // Animate berries being eaten
doGreedentEatBerries(scene); doGreedentEatBerries();
doBerrySpritePile(scene, true); doBerrySpritePile(true);
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Let it have the food // Let it have the food
// Greedent joins the team, level equal to 2 below highest party member (shiny locked) // Greedent joins the team, level equal to 2 below highest party member (shiny locked)
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2; const level = getHighestLevelPlayerPokemon(false, true).level - 2;
const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false, true); const greedent = new EnemyPokemon(getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false, true);
greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ]; greedent.moveset = [ new PokemonMove(Moves.THRASH), new PokemonMove(Moves.BODY_PRESS), new PokemonMove(Moves.STUFF_CHEEKS), new PokemonMove(Moves.SLACK_OFF) ];
greedent.passive = true; greedent.passive = true;
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false); await catchPokemon(greedent, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
.build(); .build();
function doGreedentSpriteSteal(scene: BattleScene) { function doGreedentSpriteSteal() {
const shakeDelay = 50; const shakeDelay = 50;
const slideDelay = 500; const slideDelay = 500;
const greedentSprites = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1); const greedentSprites = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1);
scene.playSound("battle_anims/Follow Me"); globalScene.playSound("battle_anims/Follow Me");
scene.tweens.chain({ globalScene.tweens.chain({
targets: greedentSprites, targets: greedentSprites,
tweens: [ tweens: [
{ // Slide Greedent diagonally { // Slide Greedent diagonally
@ -445,10 +449,10 @@ function doGreedentSpriteSteal(scene: BattleScene) {
}); });
} }
function doGreedentEatBerries(scene: BattleScene) { function doGreedentEatBerries() {
const greedentSprites = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1); const greedentSprites = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1);
let index = 1; let index = 1;
scene.tweens.add({ globalScene.tweens.add({
targets: greedentSprites, targets: greedentSprites,
duration: 150, duration: 150,
ease: "Cubic.easeOut", ease: "Cubic.easeOut",
@ -456,11 +460,11 @@ function doGreedentEatBerries(scene: BattleScene) {
y: "-=8", y: "-=8",
loop: 5, loop: 5,
onStart: () => { onStart: () => {
scene.playSound("battle_anims/PRSFX- Bug Bite"); globalScene.playSound("battle_anims/PRSFX- Bug Bite");
}, },
onLoop: () => { onLoop: () => {
if (index % 2 === 0) { if (index % 2 === 0) {
scene.playSound("battle_anims/PRSFX- Bug Bite"); globalScene.playSound("battle_anims/PRSFX- Bug Bite");
} }
index++; index++;
} }
@ -468,17 +472,15 @@ function doGreedentEatBerries(scene: BattleScene) {
} }
/** /**
*
* @param scene
* @param isEat Default false. Will "create" pile when false, and remove pile when true. * @param isEat Default false. Will "create" pile when false, and remove pile when true.
*/ */
function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) { function doBerrySpritePile(isEat: boolean = false) {
const berryAddDelay = 150; const berryAddDelay = 150;
let animationOrder = [ "starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa" ]; let animationOrder = [ "starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa" ];
if (isEat) { if (isEat) {
animationOrder = animationOrder.reverse(); animationOrder = animationOrder.reverse();
} }
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
animationOrder.forEach((berry, i) => { animationOrder.forEach((berry, i) => {
const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry)); const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry));
let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite; let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite;
@ -487,7 +489,7 @@ function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
sprite = sprites[0]; sprite = sprites[0];
tintSprite = sprites[1]; tintSprite = sprites[1];
} }
scene.time.delayedCall(berryAddDelay * i + 400, () => { globalScene.time.delayedCall(berryAddDelay * i + 400, () => {
if (sprite) { if (sprite) {
sprite.setVisible(!isEat); sprite.setVisible(!isEat);
} }
@ -497,20 +499,20 @@ function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
// Animate Petaya berry falling off the pile // Animate Petaya berry falling off the pile
if (berry === "petaya" && sprite && tintSprite && !isEat) { if (berry === "petaya" && sprite && tintSprite && !isEat) {
scene.time.delayedCall(200, () => { globalScene.time.delayedCall(200, () => {
doBerryBounce(scene, [ sprite, tintSprite ], 30, 500); doBerryBounce([ sprite, tintSprite ], 30, 500);
}); });
} }
}); });
}); });
} }
function doBerryBounce(scene: BattleScene, berrySprites: Phaser.GameObjects.Sprite[], yd: number, baseBounceDuration: number) { function doBerryBounce(berrySprites: Phaser.GameObjects.Sprite[], yd: number, baseBounceDuration: number) {
let bouncePower = 1; let bouncePower = 1;
let bounceYOffset = yd; let bounceYOffset = yd;
const doBounce = () => { const doBounce = () => {
scene.tweens.add({ globalScene.tweens.add({
targets: berrySprites, targets: berrySprites,
y: "+=" + bounceYOffset, y: "+=" + bounceYOffset,
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" }, x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },
@ -522,7 +524,7 @@ function doBerryBounce(scene: BattleScene, berrySprites: Phaser.GameObjects.Spri
if (bouncePower) { if (bouncePower) {
bounceYOffset = bounceYOffset * bouncePower; bounceYOffset = bounceYOffset * bouncePower;
scene.tweens.add({ globalScene.tweens.add({
targets: berrySprites, targets: berrySprites,
y: "-=" + bounceYOffset, y: "-=" + bounceYOffset,
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" }, x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },

View File

@ -2,8 +2,9 @@ import { generateModifierType, leaveEncounterWithoutBattle, setEncounterExp, upd
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
@ -69,14 +70,14 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true); const pokemon = getHighestStatTotalPlayerPokemon(true, true);
const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId(); const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId();
const starterValue: number = speciesStarterCosts[baseSpecies] ?? 1; const starterValue: number = speciesStarterCosts[baseSpecies] ?? 1;
const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER); const multiplier = Math.max(MONEY_MAXIMUM_MULTIPLIER / 10 * starterValue, MONEY_MINIMUM_MULTIPLIER);
const price = scene.getWaveMoneyAmount(multiplier); const price = globalScene.getWaveMoneyAmount(multiplier);
encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender());
encounter.setDialogueToken("price", price.toString()); encounter.setDialogueToken("price", price.toString());
@ -89,7 +90,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
// If player meets the combo OR requirements for option 2, populate the token // If player meets the combo OR requirements for option 2, populate the token
const opt2Req = encounter.options[1].primaryPokemonRequirements[0]; const opt2Req = encounter.options[1].primaryPokemonRequirements[0];
if (opt2Req.meetsRequirement(scene)) { if (opt2Req.meetsRequirement()) {
const abilityToken = encounter.dialogueTokens["option2PrimaryAbility"]; const abilityToken = encounter.dialogueTokens["option2PrimaryAbility"];
const moveToken = encounter.dialogueTokens["option2PrimaryMove"]; const moveToken = encounter.dialogueTokens["option2PrimaryMove"];
if (abilityToken) { if (abilityToken) {
@ -99,7 +100,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
} }
} }
const shinyCharm = generateModifierType(scene, modifierTypes.SHINY_CHARM); const shinyCharm = generateModifierType(modifierTypes.SHINY_CHARM);
encounter.setDialogueToken("itemName", shinyCharm?.name ?? i18next.t("modifierType:ModifierType.SHINY_CHARM.name")); encounter.setDialogueToken("itemName", shinyCharm?.name ?? i18next.t("modifierType:ModifierType.SHINY_CHARM.name"));
encounter.setDialogueToken("liepardName", getPokemonSpecies(Species.LIEPARD).getName()); encounter.setDialogueToken("liepardName", getPokemonSpecies(Species.LIEPARD).getName());
@ -118,17 +119,17 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Update money and remove pokemon from party // Update money and remove pokemon from party
updatePlayerMoney(scene, encounter.misc.price); updatePlayerMoney(encounter.misc.price);
scene.removePokemonFromPlayerParty(encounter.misc.pokemon); globalScene.removePokemonFromPlayerParty(encounter.misc.pokemon);
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Give the player a Shiny Charm // Give the player a Shiny Charm
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.SHINY_CHARM)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM));
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -152,15 +153,15 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Extort the rich kid for money // Extort the rich kid for money
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Update money and remove pokemon from party // Update money and remove pokemon from party
updatePlayerMoney(scene, encounter.misc.price); updatePlayerMoney(encounter.misc.price);
setEncounterExp(scene, encounter.options[1].primaryPokemon!.id, getPokemonSpecies(Species.LIEPARD).baseExp, true); setEncounterExp(encounter.options[1].primaryPokemon!.id, getPokemonSpecies(Species.LIEPARD).baseExp, true);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -175,9 +176,9 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,28 +1,35 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import type {
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { import {
EnemyPartyConfig, generateModifierType, generateModifierTypeOption, generateModifierType,
generateModifierTypeOption,
getRandomEncounterSpecies,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, setEncounterExp, leaveEncounterWithoutBattle,
setEncounterExp,
setEncounterRewards setEncounterRewards
} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { import type Pokemon from "#app/field/pokemon";
import type {
BerryModifierType, BerryModifierType,
getPartyLuckValue, ModifierTypeOption } from "#app/modifier/modifier-type";
import {
ModifierPoolType, ModifierPoolType,
ModifierTypeOption, modifierTypes, modifierTypes,
regenerateModifierPoolThresholds, regenerateModifierPoolThresholds,
} from "#app/modifier/modifier-type"; } from "#app/modifier/modifier-type";
import { randSeedInt, randSeedItem } from "#app/utils"; import { randSeedInt } from "#app/utils";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { TrainerSlot } from "#app/data/trainer-config";
import { applyModifierTypeToPlayerPokemon, getEncounterPokemonLevelForWave, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyModifierTypeToPlayerPokemon, getEncounterPokemonLevelForWave, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { BerryModifier } from "#app/modifier/modifier"; import { BerryModifier } from "#app/modifier/modifier";
@ -31,7 +38,6 @@ import { BerryType } from "#enums/berry-type";
import { PERMANENT_STATS, Stat } from "#enums/stat"; import { PERMANENT_STATS, Stat } from "#enums/stat";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/berriesAbound"; const namespace = "mysteryEncounters/berriesAbound";
@ -54,25 +60,17 @@ export const BerriesAboundEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon // Calculate boss mon
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
let bossSpecies: PokemonSpecies; const bossPokemon = getRandomEncounterSpecies(level, true);
if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!);
const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, scene.gameMode);
bossSpecies = getPokemonSpecies( levelSpecies );
} else {
bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
}
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [{ pokemonConfigs: [{
level: level, level: level,
species: bossSpecies, species: bossPokemon.species,
dataSource: new PokemonData(bossPokemon), dataSource: new PokemonData(bossPokemon),
isBoss: true isBoss: true
}], }],
@ -82,10 +80,10 @@ export const BerriesAboundEncounter: MysteryEncounter =
// Calculate the number of extra berries that player receives // Calculate the number of extra berries that player receives
// 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7 // 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7
const numBerries = const numBerries =
scene.currentBattle.waveIndex > 160 ? 7 globalScene.currentBattle.waveIndex > 160 ? 7
: scene.currentBattle.waveIndex > 120 ? 5 : globalScene.currentBattle.waveIndex > 120 ? 5
: scene.currentBattle.waveIndex > 40 ? 4 : 2; : globalScene.currentBattle.waveIndex > 40 ? 4 : 2;
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0); regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
encounter.misc = { numBerries }; encounter.misc = { numBerries };
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon); const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
@ -113,7 +111,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
]; ];
// Get fastest party pokemon for option 2 // Get fastest party pokemon for option 2
const fastestPokemon = getHighestStatPlayerPokemon(scene, PERMANENT_STATS[Stat.SPD], true, false); const fastestPokemon = getHighestStatPlayerPokemon(PERMANENT_STATS[Stat.SPD], true, false);
encounter.misc.fastestPokemon = fastestPokemon; encounter.misc.fastestPokemon = fastestPokemon;
encounter.misc.enemySpeed = bossPokemon.getStat(Stat.SPD); encounter.misc.enemySpeed = bossPokemon.getStat(Stat.SPD);
encounter.setDialogueToken("fastestPokemon", fastestPokemon.getNameToRender()); encounter.setDialogueToken("fastestPokemon", fastestPokemon.getNameToRender());
@ -134,34 +132,34 @@ export const BerriesAboundEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const numBerries = encounter.misc.numBerries; const numBerries = encounter.misc.numBerries;
const doBerryRewards = () => { const doBerryRewards = () => {
const berryText = i18next.t(`${namespace}:berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries })); queueEncounterMessage(i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
// Generate a random berry and give it to the first Pokemon with room for it // Generate a random berry and give it to the first Pokemon with room for it
for (let i = 0; i < numBerries; i++) { for (let i = 0; i < numBerries; i++) {
tryGiveBerry(scene); tryGiveBerry();
} }
}; };
const shopOptions: ModifierTypeOption[] = []; const shopOptions: ModifierTypeOption[] = [];
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
// Generate shop berries // Generate shop berries
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY); const mod = generateModifierTypeOption(modifierTypes.BERRY);
if (mod) { if (mod) {
shopOptions.push(mod); shopOptions.push(mod);
} }
} }
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards); setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
} }
) )
.withOption( .withOption(
@ -171,9 +169,9 @@ export const BerriesAboundEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip` buttonTooltip: `${namespace}:option.2.tooltip`
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick race for berries // Pick race for berries
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const fastestPokemon: PlayerPokemon = encounter.misc.fastestPokemon; const fastestPokemon: PlayerPokemon = encounter.misc.fastestPokemon;
const enemySpeed: number = encounter.misc.enemySpeed; const enemySpeed: number = encounter.misc.enemySpeed;
const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1); const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1);
@ -182,7 +180,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
const shopOptions: ModifierTypeOption[] = []; const shopOptions: ModifierTypeOption[] = [];
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
// Generate shop berries // Generate shop berries
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY); const mod = generateModifierTypeOption(modifierTypes.BERRY);
if (mod) { if (mod) {
shopOptions.push(mod); shopOptions.push(mod);
} }
@ -193,29 +191,29 @@ export const BerriesAboundEncounter: MysteryEncounter =
const doBerryRewards = () => { const doBerryRewards = () => {
const berryText = i18next.t(`${namespace}:berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries })); queueEncounterMessage(i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
// Generate a random berry and give it to the first Pokemon with room for it // Generate a random berry and give it to the first Pokemon with room for it
for (let i = 0; i < numBerries; i++) { for (let i = 0; i < numBerries; i++) {
tryGiveBerry(scene); tryGiveBerry();
} }
}; };
// Defense/Spd buffs below wave 50, +1 to all stats otherwise // Defense/Spd buffs below wave 50, +1 to all stats otherwise
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ? const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = globalScene.currentBattle.waveIndex < 50 ?
[ Stat.DEF, Stat.SPDEF, Stat.SPD ] : [ Stat.DEF, Stat.SPDEF, Stat.SPD ] :
[ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ]; [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
const config = scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]; const config = globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
config.pokemonConfigs![0].tags = [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ]; config.pokemonConfigs![0].tags = [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ];
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.2.boss_enraged`); queueEncounterMessage(`${namespace}:option.2.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
}; };
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards); setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
await showEncounterText(scene, `${namespace}:option.2.selected_bad`); await showEncounterText(`${namespace}:option.2.selected_bad`);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
return; return;
} else { } else {
// Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2 // Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2
@ -224,19 +222,19 @@ export const BerriesAboundEncounter: MysteryEncounter =
const doFasterBerryRewards = () => { const doFasterBerryRewards = () => {
const berryText = i18next.t(`${namespace}:berries`); const berryText = i18next.t(`${namespace}:berries`);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerriesGrabbed })); queueEncounterMessage(i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerriesGrabbed }));
// Generate a random berry and give it to the first Pokemon with room for it (trying to give to fastest first) // Generate a random berry and give it to the first Pokemon with room for it (trying to give to fastest first)
for (let i = 0; i < numBerriesGrabbed; i++) { for (let i = 0; i < numBerriesGrabbed; i++) {
tryGiveBerry(scene, fastestPokemon); tryGiveBerry(fastestPokemon);
} }
}; };
setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); setEncounterExp(fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards); setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
await showEncounterText(scene, `${namespace}:option.2.selected`); await showEncounterText(`${namespace}:option.2.selected`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
}) })
.build() .build()
@ -251,38 +249,38 @@ export const BerriesAboundEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
.build(); .build();
function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) { function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) {
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType; const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
const berry = generateModifierType(scene, modifierTypes.BERRY, [ berryType ]) as BerryModifierType; const berry = generateModifierType(modifierTypes.BERRY, [ berryType ]) as BerryModifierType;
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
// Will try to apply to prioritized pokemon first, then do normal application method if it fails // Will try to apply to prioritized pokemon first, then do normal application method if it fails
if (prioritizedPokemon) { if (prioritizedPokemon) {
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier const heldBerriesOfType = globalScene.findModifier(m => m instanceof BerryModifier
&& m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier; && m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) { if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) {
applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry); applyModifierTypeToPlayerPokemon(prioritizedPokemon, berry);
return; return;
} }
} }
// Iterate over the party until berry was successfully given // Iterate over the party until berry was successfully given
for (const pokemon of party) { for (const pokemon of party) {
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier const heldBerriesOfType = globalScene.findModifier(m => m instanceof BerryModifier
&& m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier; && m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) { if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) {
applyModifierTypeToPlayerPokemon(scene, pokemon, berry); applyModifierTypeToPlayerPokemon(pokemon, berry);
return; return;
} }
} }

View File

@ -1,5 +1,7 @@
import type {
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { import {
EnemyPartyConfig, generateModifierType, generateModifierType,
generateModifierTypeOption, generateModifierTypeOption,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
@ -17,17 +19,20 @@ import {
} from "#app/data/trainer-config"; } from "#app/data/trainer-config";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { import {
@ -37,14 +42,17 @@ import {
TypeRequirement TypeRequirement
} from "#app/data/mystery-encounters/mystery-encounter-requirements"; } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { AttackTypeBoosterModifierType, ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; import type { AttackTypeBoosterModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import type {
PokemonHeldItemModifier
} from "#app/modifier/modifier";
import { import {
AttackTypeBoosterModifier, AttackTypeBoosterModifier,
BypassSpeedChanceModifier, BypassSpeedChanceModifier,
ContactHeldItemTransferChanceModifier, ContactHeldItemTransferChanceModifier,
GigantamaxAccessModifier, GigantamaxAccessModifier,
MegaEvolutionAccessModifier, MegaEvolutionAccessModifier
PokemonHeldItemModifier
} from "#app/modifier/modifier"; } from "#app/modifier/modifier";
import i18next from "i18next"; import i18next from "i18next";
import MoveInfoOverlay from "#app/ui/move-info-overlay"; import MoveInfoOverlay from "#app/ui/move-info-overlay";
@ -214,12 +222,12 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
text: `${namespace}:intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculates what trainers are available for battle in the encounter // Calculates what trainers are available for battle in the encounter
// Bug type superfan trainer config // Bug type superfan trainer config
const config = getTrainerConfigForWave(scene.currentBattle.waveIndex); const config = getTrainerConfigForWave(globalScene.currentBattle.waveIndex);
const spriteKey = config.getSpriteKey(); const spriteKey = config.getSpriteKey();
encounter.enemyPartyConfigs.push({ encounter.enemyPartyConfigs.push({
trainerConfig: config, trainerConfig: config,
@ -227,7 +235,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}); });
let beedrillKeys: { spriteKey: string, fileRoot: string }, butterfreeKeys: { spriteKey: string, fileRoot: string }; let beedrillKeys: { spriteKey: string, fileRoot: string }, butterfreeKeys: { spriteKey: string, fileRoot: string };
if (scene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) { if (globalScene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false); beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false);
butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false); butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false);
} else { } else {
@ -270,9 +278,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
]; ];
const requiredItems = [ const requiredItems = [
generateModifierType(scene, modifierTypes.QUICK_CLAW), generateModifierType(modifierTypes.QUICK_CLAW),
generateModifierType(scene, modifierTypes.GRIP_CLAW), generateModifierType(modifierTypes.GRIP_CLAW),
generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.BUG ]), generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.BUG ]),
]; ];
const requiredItemString = requiredItems.map(m => m?.name ?? "unknown").join("/"); const requiredItemString = requiredItems.map(m => m?.name ?? "unknown").join("/");
@ -295,9 +303,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Select battle the bug trainer // Select battle the bug trainer
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
// Init the moves available for tutor // Init the moves available for tutor
@ -313,9 +321,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
// Assigns callback that teaches move before continuing to rewards // Assigns callback that teaches move before continuing to rewards
encounter.onRewards = doBugTypeMoveTutor; encounter.onRewards = doBugTypeMoveTutor;
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
await transitionMysteryEncounterIntroVisuals(scene, true, true); await transitionMysteryEncounterIntroVisuals(true, true);
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
} }
) )
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
@ -326,17 +334,17 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip` disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Player shows off their bug types // Player shows off their bug types
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Player gets different rewards depending on the number of bug types they have // Player gets different rewards depending on the number of bug types they have
const numBugTypes = scene.getPlayerParty().filter(p => p.isOfType(Type.BUG, true)).length; const numBugTypes = globalScene.getPlayerParty().filter(p => p.isOfType(Type.BUG, true)).length;
const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes }); const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes });
encounter.setDialogueToken("numBugTypes", numBugTypesText); encounter.setDialogueToken("numBugTypes", numBugTypesText);
if (numBugTypes < 2) { if (numBugTypes < 2) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
@ -344,7 +352,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}, },
]; ];
} else if (numBugTypes < 4) { } else if (numBugTypes < 4) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
@ -352,7 +360,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}, },
]; ];
} else if (numBugTypes < 6) { } else if (numBugTypes < 6) {
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL ], fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
@ -362,28 +370,28 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
} else { } else {
// If the player has any evolution/form change items that are valid for their party, // If the player has any evolution/form change items that are valid for their party,
// spawn one of those items in addition to Dynamax Band, Mega Band, and Master Ball // spawn one of those items in addition to Dynamax Band, Mega Band, and Master Ball
const modifierOptions: ModifierTypeOption[] = [ generateModifierTypeOption(scene, modifierTypes.MASTER_BALL)! ]; const modifierOptions: ModifierTypeOption[] = [ generateModifierTypeOption(modifierTypes.MASTER_BALL)! ];
const specialOptions: ModifierTypeOption[] = []; const specialOptions: ModifierTypeOption[] = [];
if (!scene.findModifier(m => m instanceof MegaEvolutionAccessModifier)) { if (!globalScene.findModifier(m => m instanceof MegaEvolutionAccessModifier)) {
modifierOptions.push(generateModifierTypeOption(scene, modifierTypes.MEGA_BRACELET)!); modifierOptions.push(generateModifierTypeOption(modifierTypes.MEGA_BRACELET)!);
} }
if (!scene.findModifier(m => m instanceof GigantamaxAccessModifier)) { if (!globalScene.findModifier(m => m instanceof GigantamaxAccessModifier)) {
modifierOptions.push(generateModifierTypeOption(scene, modifierTypes.DYNAMAX_BAND)!); modifierOptions.push(generateModifierTypeOption(modifierTypes.DYNAMAX_BAND)!);
} }
const nonRareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.EVOLUTION_ITEM); const nonRareEvolutionModifier = generateModifierTypeOption(modifierTypes.EVOLUTION_ITEM);
if (nonRareEvolutionModifier) { if (nonRareEvolutionModifier) {
specialOptions.push(nonRareEvolutionModifier); specialOptions.push(nonRareEvolutionModifier);
} }
const rareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.RARE_EVOLUTION_ITEM); const rareEvolutionModifier = generateModifierTypeOption(modifierTypes.RARE_EVOLUTION_ITEM);
if (rareEvolutionModifier) { if (rareEvolutionModifier) {
specialOptions.push(rareEvolutionModifier); specialOptions.push(rareEvolutionModifier);
} }
const formChangeModifier = generateModifierTypeOption(scene, modifierTypes.FORM_CHANGE_ITEM); const formChangeModifier = generateModifierTypeOption(modifierTypes.FORM_CHANGE_ITEM);
if (formChangeModifier) { if (formChangeModifier) {
specialOptions.push(formChangeModifier); specialOptions.push(formChangeModifier);
} }
const rareFormChangeModifier = generateModifierTypeOption(scene, modifierTypes.RARE_FORM_CHANGE_ITEM); const rareFormChangeModifier = generateModifierTypeOption(modifierTypes.RARE_FORM_CHANGE_ITEM);
if (rareFormChangeModifier) { if (rareFormChangeModifier) {
specialOptions.push(rareFormChangeModifier); specialOptions.push(rareFormChangeModifier);
} }
@ -391,7 +399,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]);
} }
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
speaker: `${namespace}:speaker`, speaker: `${namespace}:speaker`,
@ -400,9 +408,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
]; ];
} }
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Player shows off their bug types // Player shows off their bug types
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build()) .build())
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
@ -429,8 +437,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
], ],
secondOptionPrompt: `${namespace}:option.3.select_prompt`, secondOptionPrompt: `${namespace}:option.3.select_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
@ -466,27 +474,27 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
(item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG); (item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG);
}); });
if (!hasValidItem) { if (!hasValidItem) {
return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null; return getEncounterText(`${namespace}:option.3.invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier; const modifier = encounter.misc.chosenModifier;
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
chosenPokemon.loseHeldItem(modifier, false); chosenPokemon.loseHeldItem(modifier, false);
scene.updateModifiers(true, true); globalScene.updateModifiers(true, true);
const bugNet = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!; const bugNet = generateModifierTypeOption(modifierTypes.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!;
bugNet.type.tier = ModifierTier.ROGUE; bugNet.type.tier = ModifierTier.ROGUE;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ bugNet ], guaranteedModifierTypeFuncs: [ modifierTypes.REVIVER_SEED ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ bugNet ], guaranteedModifierTypeFuncs: [ modifierTypes.REVIVER_SEED ], fillRemaining: false });
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build()) .build())
.withOutroDialogue([ .withOutroDialogue([
@ -642,22 +650,22 @@ function getTrainerConfigForWave(waveIndex: number) {
return config; return config;
} }
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> { function doBugTypeMoveTutor(): Promise<void> {
return new Promise<void>(async resolve => { return new Promise<void>(async resolve => {
const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions; const moveOptions = globalScene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
await showEncounterDialogue(scene, `${namespace}:battle_won`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:battle_won`, `${namespace}:speaker`);
const overlayScale = 1; const overlayScale = 1;
const moveInfoOverlay = new MoveInfoOverlay(scene, { const moveInfoOverlay = new MoveInfoOverlay({
delayVisibility: false, delayVisibility: false,
scale: overlayScale, scale: overlayScale,
onSide: true, onSide: true,
right: true, right: true,
x: 1, x: 1,
y: -MoveInfoOverlay.getHeight(overlayScale, true) - 1, y: -MoveInfoOverlay.getHeight(overlayScale, true) - 1,
width: (scene.game.canvas.width / 6) - 2, width: (globalScene.game.canvas.width / 6) - 2,
}); });
scene.ui.add(moveInfoOverlay); globalScene.ui.add(moveInfoOverlay);
const optionSelectItems = moveOptions.map((move: PokemonMove) => { const optionSelectItems = moveOptions.map((move: PokemonMove) => {
const option: OptionSelectItem = { const option: OptionSelectItem = {
@ -680,7 +688,7 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
moveInfoOverlay.setVisible(false); moveInfoOverlay.setVisible(false);
}; };
const result = await selectOptionThenPokemon(scene, optionSelectItems, `${namespace}:teach_move_prompt`, undefined, onHoverOverCancel); const result = await selectOptionThenPokemon(optionSelectItems, `${namespace}:teach_move_prompt`, undefined, onHoverOverCancel);
// let forceExit = !!result; // let forceExit = !!result;
if (!result) { if (!result) {
moveInfoOverlay.active = false; moveInfoOverlay.active = false;
@ -691,7 +699,7 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
// Option select complete, handle if they are learning a move // Option select complete, handle if they are learning a move
if (result && result.selectedOptionIndex < moveOptions.length) { if (result && result.selectedOptionIndex < moveOptions.length) {
scene.unshiftPhase(new LearnMovePhase(scene, result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId)); globalScene.unshiftPhase(new LearnMovePhase(result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId));
} }
// Complete battle and go to rewards // Complete battle and go to rewards

View File

@ -1,26 +1,30 @@
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config"; import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { ModifierPoolType, modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { applyAbilityOverrideToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyAbilityOverrideToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { randSeedInt, randSeedShuffle } from "#app/utils"; import { randSeedInt, randSeedShuffle } from "#app/utils";
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import i18next from "i18next"; import i18next from "i18next";
import { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { Ability } from "#app/data/ability"; import { Ability } from "#app/data/ability";
import { BerryModifier } from "#app/modifier/modifier"; import { BerryModifier } from "#app/modifier/modifier";
import { BerryType } from "#enums/berry-type"; import { BerryType } from "#enums/berry-type";
@ -105,8 +109,8 @@ export const ClowningAroundEncounter: MysteryEncounter =
speaker: `${namespace}:speaker` speaker: `${namespace}:speaker`
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const clownTrainerType = TrainerType.HARLEQUIN; const clownTrainerType = TrainerType.HARLEQUIN;
const clownConfig = trainerConfigs[clownTrainerType].clone(); const clownConfig = trainerConfigs[clownTrainerType].clone();
@ -142,7 +146,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
}); });
// Load animations/sfx for start of fight moves // Load animations/sfx for start of fight moves
loadCustomMovesForEncounter(scene, [ Moves.ROLE_PLAY, Moves.TAUNT ]); loadCustomMovesForEncounter([ Moves.ROLE_PLAY, Moves.TAUNT ]);
encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName()); encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName());
@ -165,12 +169,12 @@ export const ClowningAroundEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn battle // Spawn battle
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
// TODO: when Magic Room and Wonder Room are implemented, add those to start of battle // TODO: when Magic Room and Wonder Room are implemented, add those to start of battle
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(
@ -193,28 +197,28 @@ export const ClowningAroundEncounter: MysteryEncounter =
ignorePp: true ignorePp: true
}); });
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.withPostOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPostOptionPhase(async (): Promise<boolean> => {
// After the battle, offer the player the opportunity to permanently swap ability // After the battle, offer the player the opportunity to permanently swap ability
const abilityWasSwapped = await handleSwapAbility(scene); const abilityWasSwapped = await handleSwapAbility();
if (abilityWasSwapped) { if (abilityWasSwapped) {
await showEncounterText(scene, `${namespace}:option.1.ability_gained`); await showEncounterText(`${namespace}:option.1.ability_gained`);
} }
// Play animations once ability swap is complete // Play animations once ability swap is complete
// Trainer sprite that is shown at end of battle is not the same as mystery encounter intro visuals // Trainer sprite that is shown at end of battle is not the same as mystery encounter intro visuals
scene.tweens.add({ globalScene.tweens.add({
targets: scene.currentBattle.trainer, targets: globalScene.currentBattle.trainer,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
alpha: 0, alpha: 0,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
duration: 250 duration: 250
}); });
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2); background.playWithoutTargets(230, 40, 2);
return true; return true;
}) })
.build() .build()
@ -239,13 +243,13 @@ export const ClowningAroundEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Swap player's items on pokemon with the most items // Swap player's items on pokemon with the most items
// Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items // Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items
// So Vitamins, form change items, etc. are not included // So Vitamins, form change items, etc. are not included
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
let mostHeldItemsPokemon = party[0]; let mostHeldItemsPokemon = party[0];
let count = mostHeldItemsPokemon.getHeldItems() let count = mostHeldItemsPokemon.getHeldItems()
.filter(m => m.isTransferable && !(m instanceof BerryModifier)) .filter(m => m.isTransferable && !(m instanceof BerryModifier))
@ -270,10 +274,10 @@ export const ClowningAroundEncounter: MysteryEncounter =
items.filter(m => m instanceof BerryModifier) items.filter(m => m instanceof BerryModifier)
.forEach(m => { .forEach(m => {
numBerries += m.stackCount; numBerries += m.stackCount;
scene.removeModifier(m); globalScene.removeModifier(m);
}); });
generateItemsOfTier(scene, mostHeldItemsPokemon, numBerries, "Berries"); generateItemsOfTier(mostHeldItemsPokemon, numBerries, "Berries");
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm) // Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
// For the purpose of this ME, Soothe Bells and Lucky Eggs are counted as Ultra tier // For the purpose of this ME, Soothe Bells and Lucky Eggs are counted as Ultra tier
@ -286,24 +290,24 @@ export const ClowningAroundEncounter: MysteryEncounter =
const tier = type.tier ?? ModifierTier.ULTRA; const tier = type.tier ?? ModifierTier.ULTRA;
if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) { if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) {
numRogue += m.stackCount; numRogue += m.stackCount;
scene.removeModifier(m); globalScene.removeModifier(m);
} else if (type.id === "LUCKY_EGG" || type.id === "SOOTHE_BELL" || tier === ModifierTier.ULTRA) { } else if (type.id === "LUCKY_EGG" || type.id === "SOOTHE_BELL" || tier === ModifierTier.ULTRA) {
numUltra += m.stackCount; numUltra += m.stackCount;
scene.removeModifier(m); globalScene.removeModifier(m);
} }
}); });
generateItemsOfTier(scene, mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA); generateItemsOfTier(mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA);
generateItemsOfTier(scene, mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE); generateItemsOfTier(mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async () => {
// Play animations // Play animations
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2); background.playWithoutTargets(230, 40, 2);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200); await transitionMysteryEncounterIntroVisuals(true, true, 200);
}) })
.build() .build()
) )
@ -327,10 +331,10 @@ export const ClowningAroundEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Randomize the second type of all player's pokemon // Randomize the second type of all player's pokemon
// If the pokemon does not normally have a second type, it will gain 1 // If the pokemon does not normally have a second type, it will gain 1
for (const pokemon of scene.getPlayerParty()) { for (const pokemon of globalScene.getPlayerParty()) {
const originalTypes = pokemon.getTypes(false, false, true); const originalTypes = pokemon.getTypes(false, false, true);
// If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type // If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type
@ -367,14 +371,14 @@ export const ClowningAroundEncounter: MysteryEncounter =
} }
} }
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.withPostOptionPhase(async (scene: BattleScene) => { .withPostOptionPhase(async () => {
// Play animations // Play animations
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
background.playWithoutTargets(scene, 230, 40, 2); background.playWithoutTargets(230, 40, 2);
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200); await transitionMysteryEncounterIntroVisuals(true, true, 200);
}) })
.build() .build()
) )
@ -385,24 +389,24 @@ export const ClowningAroundEncounter: MysteryEncounter =
]) ])
.build(); .build();
async function handleSwapAbility(scene: BattleScene) { async function handleSwapAbility() {
return new Promise<boolean>(async resolve => { return new Promise<boolean>(async resolve => {
await showEncounterDialogue(scene, `${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`);
await showEncounterText(scene, `${namespace}:option.1.apply_ability_message`); await showEncounterText(`${namespace}:option.1.apply_ability_message`);
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
displayYesNoOptions(scene, resolve); displayYesNoOptions(resolve);
}); });
}); });
} }
function displayYesNoOptions(scene: BattleScene, resolve) { function displayYesNoOptions(resolve) {
showEncounterText(scene, `${namespace}:option.1.ability_prompt`, null, 500, false); showEncounterText(`${namespace}:option.1.ability_prompt`, null, 500, false);
const fullOptions = [ const fullOptions = [
{ {
label: i18next.t("menu:yes"), label: i18next.t("menu:yes"),
handler: () => { handler: () => {
onYesAbilitySwap(scene, resolve); onYesAbilitySwap(resolve);
return true; return true;
} }
}, },
@ -420,29 +424,29 @@ function displayYesNoOptions(scene: BattleScene, resolve) {
maxOptions: 7, maxOptions: 7,
yOffset: 0 yOffset: 0
}; };
scene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true); globalScene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true);
} }
function onYesAbilitySwap(scene: BattleScene, resolve) { function onYesAbilitySwap(resolve) {
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Do ability swap // Do ability swap
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
applyAbilityOverrideToPokemon(pokemon, encounter.misc.ability); applyAbilityOverrideToPokemon(pokemon, encounter.misc.ability);
encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true)); globalScene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
}; };
const onPokemonNotSelected = () => { const onPokemonNotSelected = () => {
scene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {
displayYesNoOptions(scene, resolve); displayYesNoOptions(resolve);
}); });
}; };
selectPokemonForOption(scene, onPokemonSelected, onPokemonNotSelected); selectPokemonForOption(onPokemonSelected, onPokemonNotSelected);
} }
function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItems: number, tier: ModifierTier | "Berries") { function generateItemsOfTier(pokemon: PlayerPokemon, numItems: number, tier: ModifierTier | "Berries") {
// These pools have to be defined at runtime so that modifierTypes exist // These pools have to be defined at runtime so that modifierTypes exist
// Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon // Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon
// This is to prevent "over-generating" a random item of a certain type during item swaps // This is to prevent "over-generating" a random item of a certain type during item swaps
@ -495,11 +499,11 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem
const newItemType = pool[randIndex]; const newItemType = pool[randIndex];
let newMod: PokemonHeldItemModifierType; let newMod: PokemonHeldItemModifierType;
if (tier === "Berries") { if (tier === "Berries") {
newMod = generateModifierType(scene, modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType; newMod = generateModifierType(modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType;
} else { } else {
newMod = generateModifierType(scene, newItemType[0]) as PokemonHeldItemModifierType; newMod = generateModifierType(newItemType[0]) as PokemonHeldItemModifierType;
} }
applyModifierTypeToPlayerPokemon(scene, pokemon, newMod); applyModifierTypeToPlayerPokemon(pokemon, newMod);
// Decrement max stacks and remove from pool if at max // Decrement max stacks and remove from pool if at max
newItemType[1]--; newItemType[1]--;
if (newItemType[1] <= 0) { if (newItemType[1] <= 0) {

View File

@ -1,22 +1,26 @@
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { EncounterBattleAnim } from "#app/data/battle-anims"; import { EncounterBattleAnim } from "#app/data/battle-anims";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { catchPokemon, getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { catchPokemon, getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { EncounterAnim } from "#enums/encounter-anims"; import { EncounterAnim } from "#enums/encounter-anims";
@ -91,10 +95,10 @@ export const DancingLessonsEncounter: MysteryEncounter =
.withAutoHideIntroVisuals(false) .withAutoHideIntroVisuals(false)
.withCatchAllowed(true) .withCatchAllowed(true)
.withFleeAllowed(false) .withFleeAllowed(false)
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
const oricorio = scene.getEnemyPokemon()!; const oricorio = globalScene.getEnemyPokemon()!;
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, oricorio, scene.getPlayerPokemon()!); const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, oricorio, globalScene.getPlayerPokemon()!);
danceAnim.play(scene, false, () => { danceAnim.play(false, () => {
if (oricorio.shiny) { if (oricorio.shiny) {
oricorio.sparkle(); oricorio.sparkle();
} }
@ -110,12 +114,12 @@ export const DancingLessonsEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const species = getPokemonSpecies(Species.ORICORIO); const species = getPokemonSpecies(Species.ORICORIO);
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
const enemyPokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, false); const enemyPokemon = new EnemyPokemon(species, level, TrainerSlot.NONE, false);
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) { if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) {
if (enemyPokemon.moveset.length < 4) { if (enemyPokemon.moveset.length < 4) {
enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE)); enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE));
@ -126,7 +130,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
// Set the form index based on the biome // Set the form index based on the biome
// Defaults to Baile style if somehow nothing matches // Defaults to Baile style if somehow nothing matches
const currentBiome = scene.arena.biomeType; const currentBiome = globalScene.arena.biomeType;
if (BAILE_STYLE_BIOMES.includes(currentBiome)) { if (BAILE_STYLE_BIOMES.includes(currentBiome)) {
enemyPokemon.formIndex = 0; enemyPokemon.formIndex = 0;
} else if (POM_POM_STYLE_BIOMES.includes(currentBiome)) { } else if (POM_POM_STYLE_BIOMES.includes(currentBiome)) {
@ -140,14 +144,14 @@ export const DancingLessonsEncounter: MysteryEncounter =
} }
const oricorioData = new PokemonData(enemyPokemon); const oricorioData = new PokemonData(enemyPokemon);
const oricorio = scene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, false, oricorioData); const oricorio = globalScene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, false, oricorioData);
// Adds a real Pokemon sprite to the field (required for the animation) // Adds a real Pokemon sprite to the field (required for the animation)
scene.getEnemyParty().forEach(enemyPokemon => { globalScene.getEnemyParty().forEach(enemyPokemon => {
scene.field.remove(enemyPokemon, true); globalScene.field.remove(enemyPokemon, true);
}); });
scene.currentBattle.enemyParty = [ oricorio ]; globalScene.currentBattle.enemyParty = [ oricorio ];
scene.field.add(oricorio); globalScene.field.add(oricorio);
// Spawns on offscreen field // Spawns on offscreen field
oricorio.x -= 300; oricorio.x -= 300;
encounter.loadAssets.push(oricorio.loadAssets()); encounter.loadAssets.push(oricorio.loadAssets());
@ -160,8 +164,8 @@ export const DancingLessonsEncounter: MysteryEncounter =
// Gets +1 to all stats except SPD on battle start // Gets +1 to all stats except SPD on battle start
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.boss_enraged`); queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF ], 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF ], 1));
} }
}], }],
}; };
@ -186,9 +190,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.startOfBattleEffects.push({ encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY, sourceBattlerIndex: BattlerIndex.ENEMY,
@ -197,9 +201,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
ignorePp: true ignorePp: true
}); });
await hideOricorioPokemon(scene); await hideOricorioPokemon();
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.BATON ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.BATON ], fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
}) })
.build() .build()
) )
@ -215,25 +219,25 @@ export const DancingLessonsEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Learn its Dance // Learn its Dance
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
scene.unshiftPhase(new LearnMovePhase(scene, scene.getPlayerParty().indexOf(pokemon), Moves.REVELATION_DANCE)); globalScene.unshiftPhase(new LearnMovePhase(globalScene.getPlayerParty().indexOf(pokemon), Moves.REVELATION_DANCE));
// Play animation again to "learn" the dance // Play animation again to "learn" the dance
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon()); const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, globalScene.getEnemyPokemon()!, globalScene.getPlayerPokemon());
danceAnim.play(scene); danceAnim.play();
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Learn its Dance // Learn its Dance
await hideOricorioPokemon(scene); await hideOricorioPokemon();
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -252,9 +256,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Open menu for selecting pokemon with a Dancing move // Open menu for selecting pokemon with a Dancing move
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for nature selection // Return the options for nature selection
return pokemon.moveset return pokemon.moveset
@ -281,20 +285,20 @@ export const DancingLessonsEncounter: MysteryEncounter =
if (!pokemon.isAllowedInBattle()) { if (!pokemon.isAllowedInBattle()) {
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null; return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
} }
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null; return getEncounterText(`${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Show the Oricorio a dance, and recruit it // Show the Oricorio a dance, and recruit it
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const oricorio = encounter.misc.oricorioData.toPokemon(scene); const oricorio = encounter.misc.oricorioData.toPokemon();
oricorio.passive = true; oricorio.passive = true;
// Ensure the Oricorio's moveset gains the Dance move the player used // Ensure the Oricorio's moveset gains the Dance move the player used
@ -307,18 +311,18 @@ export const DancingLessonsEncounter: MysteryEncounter =
} }
} }
await hideOricorioPokemon(scene); await hideOricorioPokemon();
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false); await catchPokemon(oricorio, null, PokeballType.POKEBALL, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
.build(); .build();
function hideOricorioPokemon(scene: BattleScene) { function hideOricorioPokemon() {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const oricorioSprite = scene.getEnemyParty()[0]; const oricorioSprite = globalScene.getEnemyParty()[0];
scene.tweens.add({ globalScene.tweens.add({
targets: oricorioSprite, targets: oricorioSprite,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
@ -326,7 +330,7 @@ function hideOricorioPokemon(scene: BattleScene) {
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
duration: 750, duration: 750,
onComplete: () => { onComplete: () => {
scene.field.remove(oricorioSprite, true); globalScene.field.remove(oricorioSprite, true);
resolve(); resolve();
} }
}); });

View File

@ -1,18 +1,21 @@
import { Type } from "#enums/type"; import type { Type } from "#enums/type";
import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils"; import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../utils/encounter-phase-utils";
import { getRandomPlayerPokemon, getRandomSpeciesByStarterCost } 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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { PokemonFormChangeItemModifier } from "#app/modifier/modifier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
@ -138,16 +141,16 @@ export const DarkDealEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Removes random pokemon (including fainted) from party and adds name to dialogue data tokens // Removes random pokemon (including fainted) from party and adds name to dialogue data tokens
// Will never return last battle able mon and instead pick fainted/unable to battle // Will never return last battle able mon and instead pick fainted/unable to battle
const removedPokemon = getRandomPlayerPokemon(scene, true, false, true); const removedPokemon = getRandomPlayerPokemon(true, false, true);
// Get all the pokemon's held items // Get all the pokemon's held items
const modifiers = removedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier)); const modifiers = removedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier));
scene.removePokemonFromPlayerParty(removedPokemon); globalScene.removePokemonFromPlayerParty(removedPokemon);
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("pokeName", removedPokemon.getNameToRender()); encounter.setDialogueToken("pokeName", removedPokemon.getNameToRender());
// Store removed pokemon types // Store removed pokemon types
@ -156,16 +159,16 @@ export const DarkDealEncounter: MysteryEncounter =
modifiers modifiers
}; };
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Give the player 5 Rogue Balls // Give the player 5 Rogue Balls
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ROGUE_BALL)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.ROGUE_BALL));
// Start encounter with random legendary (7-10 starter strength) that has level additive // Start encounter with random legendary (7-10 starter strength) that has level additive
// If this is a mono-type challenge, always ensure the required type is filtered for // If this is a mono-type challenge, always ensure the required type is filtered for
let bossTypes: Type[] = encounter.misc.removedTypes; let bossTypes: Type[] = encounter.misc.removedTypes;
const singleTypeChallenges = scene.gameMode.challenges.filter(c => c.value && c.id === Challenges.SINGLE_TYPE); const singleTypeChallenges = globalScene.gameMode.challenges.filter(c => c.value && c.id === Challenges.SINGLE_TYPE);
if (scene.gameMode.isChallenge && singleTypeChallenges.length > 0) { if (globalScene.gameMode.isChallenge && singleTypeChallenges.length > 0) {
bossTypes = singleTypeChallenges.map(c => (c.value - 1) as Type); bossTypes = singleTypeChallenges.map(c => (c.value - 1) as Type);
} }
@ -191,7 +194,7 @@ export const DarkDealEncounter: MysteryEncounter =
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [ pokemonConfig ], pokemonConfigs: [ pokemonConfig ],
}; };
await initBattleWithEnemyConfig(scene, config); await initBattleWithEnemyConfig(config);
}) })
.build() .build()
) )
@ -206,9 +209,9 @@ export const DarkDealEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,18 +1,22 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PreserveBerryModifier } from "#app/modifier/modifier"; import type { PokemonHeldItemModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier";
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PreserveBerryModifier } from "#app/modifier/modifier";
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { randSeedItem } from "#app/utils"; import { randSeedItem } from "#app/utils";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -36,19 +40,19 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2; const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
const doEventReward = (scene: BattleScene) => { const doEventReward = () => {
const event_buff = scene.eventManager.activeEvent()?.delibirdyBuff ?? []; const event_buff = globalScene.eventManager.getDelibirdyBuff();
if (event_buff.length > 0) { if (event_buff.length > 0) {
const candidates = event_buff.filter((c => { const candidates = event_buff.filter((c => {
const mtype = generateModifierType(scene, modifierTypes[c]); const mtype = generateModifierType(modifierTypes[c]);
const existingCharm = scene.findModifier(m => m.type.id === mtype?.id); const existingCharm = globalScene.findModifier(m => m.type.id === mtype?.id);
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount(scene)); return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount());
})); }));
if (candidates.length > 0) { if (candidates.length > 0) {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes[randSeedItem(candidates)])); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)]));
} else { } else {
// At max stacks, give a Voucher instead // At max stacks, give a Voucher instead
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.VOUCHER)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.VOUCHER));
} }
} }
}; };
@ -114,15 +118,15 @@ export const DelibirdyEncounter: MysteryEncounter =
text: `${namespace}:outro`, text: `${namespace}:outro`,
} }
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName()); encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName());
scene.loadBgm("mystery_encounter_delibirdy", "mystery_encounter_delibirdy.mp3"); globalScene.loadBgm("mystery_encounter_delibirdy", "mystery_encounter_delibirdy.mp3");
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
scene.fadeAndSwitchBgm("mystery_encounter_delibirdy"); globalScene.fadeAndSwitchBgm("mystery_encounter_delibirdy");
return true; return true;
}) })
.withOption( .withOption(
@ -138,29 +142,29 @@ export const DelibirdyEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false); updatePlayerMoney(-(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false);
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Give the player an Amulet Coin // Give the player an Amulet Coin
// Check if the player has max stacks of that item already // Check if the player has max stacks of that item already
const existing = scene.findModifier(m => m instanceof MoneyMultiplierModifier) as MoneyMultiplierModifier; const existing = globalScene.findModifier(m => m instanceof MoneyMultiplierModifier) as MoneyMultiplierModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
doEventReward(scene); doEventReward();
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.AMULET_COIN)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN));
doEventReward(scene); doEventReward();
} }
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -178,8 +182,8 @@ export const DelibirdyEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter((it) => { const validItems = pokemon.getHeldItems().filter((it) => {
@ -205,57 +209,57 @@ export const DelibirdyEncounter: MysteryEncounter =
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
// If pokemon has valid item, it can be selected // If pokemon has valid item, it can be selected
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null; return getEncounterText(`${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const modifier: BerryModifier | PokemonInstantReviveModifier = encounter.misc.chosenModifier; const modifier: BerryModifier | PokemonInstantReviveModifier = encounter.misc.chosenModifier;
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Give the player a Candy Jar if they gave a Berry, and a Berry Pouch for Reviver Seed // Give the player a Candy Jar if they gave a Berry, and a Berry Pouch for Reviver Seed
if (modifier instanceof BerryModifier) { if (modifier instanceof BerryModifier) {
// Check if the player has max stacks of that Candy Jar already // Check if the player has max stacks of that Candy Jar already
const existing = scene.findModifier(m => m instanceof LevelIncrementBoosterModifier) as LevelIncrementBoosterModifier; const existing = globalScene.findModifier(m => m instanceof LevelIncrementBoosterModifier) as LevelIncrementBoosterModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
doEventReward(scene); doEventReward();
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR));
doEventReward(scene); doEventReward();
} }
} else { } else {
// Check if the player has max stacks of that Berry Pouch already // Check if the player has max stacks of that Berry Pouch already
const existing = scene.findModifier(m => m instanceof PreserveBerryModifier) as PreserveBerryModifier; const existing = globalScene.findModifier(m => m instanceof PreserveBerryModifier) as PreserveBerryModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
doEventReward(scene); doEventReward();
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH));
doEventReward(scene); doEventReward();
} }
} }
chosenPokemon.loseHeldItem(modifier, false); chosenPokemon.loseHeldItem(modifier, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -273,8 +277,8 @@ export const DelibirdyEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter((it) => { const validItems = pokemon.getHeldItems().filter((it) => {
@ -300,39 +304,39 @@ export const DelibirdyEncounter: MysteryEncounter =
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
// If pokemon has valid item, it can be selected // If pokemon has valid item, it can be selected
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon); const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(pokemon);
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null; return getEncounterText(`${namespace}:invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier; const modifier = encounter.misc.chosenModifier;
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Check if the player has max stacks of Healing Charm already // Check if the player has max stacks of Healing Charm already
const existing = scene.findModifier(m => m instanceof HealingBoosterModifier) as HealingBoosterModifier; const existing = globalScene.findModifier(m => m instanceof HealingBoosterModifier) as HealingBoosterModifier;
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) { if (existing && existing.getStackCount() >= existing.getMaxStackCount()) {
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType; const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerParty()[0], shellBell); await applyModifierTypeToPlayerPokemon(globalScene.getPlayerParty()[0], shellBell);
scene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
doEventReward(scene); doEventReward();
} else { } else {
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM)); globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM));
doEventReward(scene); doEventReward();
} }
chosenPokemon.loseHeldItem(modifier, false); chosenPokemon.loseHeldItem(modifier, false);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )

View File

@ -2,12 +2,13 @@ import {
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
setEncounterRewards, setEncounterRewards,
} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { ModifierTypeFunc, modifierTypes } from "#app/modifier/modifier-type"; import type { ModifierTypeFunc } from "#app/modifier/modifier-type";
import { modifierTypes } from "#app/modifier/modifier-type";
import { randSeedInt } from "#app/utils"; import { randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import BattleScene from "#app/battle-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import MysteryEncounter, { import {
MysteryEncounterBuilder, MysteryEncounterBuilder,
} from "#app/data/mystery-encounters/mystery-encounter"; } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -60,7 +61,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.1.label`, buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
}, },
async (scene: BattleScene) => { async () => {
// Choose TMs // Choose TMs
const modifiers: ModifierTypeFunc[] = []; const modifiers: ModifierTypeFunc[] = [];
let i = 0; let i = 0;
@ -77,8 +78,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
i++; i++;
} }
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withSimpleOption( .withSimpleOption(
@ -86,7 +87,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
}, },
async (scene: BattleScene) => { async () => {
// Choose Vitamins // Choose Vitamins
const modifiers: ModifierTypeFunc[] = []; const modifiers: ModifierTypeFunc[] = [];
let i = 0; let i = 0;
@ -101,8 +102,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
i++; i++;
} }
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withSimpleOption( .withSimpleOption(
@ -110,7 +111,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.3.label`, buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
}, },
async (scene: BattleScene) => { async () => {
// Choose X Items // Choose X Items
const modifiers: ModifierTypeFunc[] = []; const modifiers: ModifierTypeFunc[] = [];
let i = 0; let i = 0;
@ -125,8 +126,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
i++; i++;
} }
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withSimpleOption( .withSimpleOption(
@ -134,7 +135,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.4.label`, buttonLabel: `${namespace}:option.4.label`,
buttonTooltip: `${namespace}:option.4.tooltip`, buttonTooltip: `${namespace}:option.4.tooltip`,
}, },
async (scene: BattleScene) => { async () => {
// Choose Pokeballs // Choose Pokeballs
const modifiers: ModifierTypeFunc[] = []; const modifiers: ModifierTypeFunc[] = [];
let i = 0; let i = 0;
@ -153,8 +154,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
i++; i++;
} }
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, }); setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} }
) )
.withOutroDialogue([ .withOutroDialogue([

View File

@ -1,12 +1,13 @@
import { MoveCategory } from "#app/data/move"; import { MoveCategory } from "#app/data/move";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
@ -64,8 +65,8 @@ export const FieldTripEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
secondOptionPrompt: `${namespace}:second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option // Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => { return pokemon.moveset.map((move: PokemonMove) => {
@ -74,7 +75,7 @@ export const FieldTripEncounter: MysteryEncounter =
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:physical`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:physical`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.PHYSICAL); pokemonAndMoveChosen(pokemon, move, MoveCategory.PHYSICAL);
return true; return true;
}, },
}; };
@ -82,23 +83,23 @@ export const FieldTripEncounter: MysteryEncounter =
}); });
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ATK ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ATK ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.DEF ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.DEF ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!, generateModifierTypeOption(modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!, generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
]; ];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
} }
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); leaveEncounterWithoutBattle(!encounter.misc.correctMove);
}) })
.build() .build()
) )
@ -110,8 +111,8 @@ export const FieldTripEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
secondOptionPrompt: `${namespace}:second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option // Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => { return pokemon.moveset.map((move: PokemonMove) => {
@ -120,7 +121,7 @@ export const FieldTripEncounter: MysteryEncounter =
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:special`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:special`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.SPECIAL); pokemonAndMoveChosen(pokemon, move, MoveCategory.SPECIAL);
return true; return true;
}, },
}; };
@ -128,23 +129,23 @@ export const FieldTripEncounter: MysteryEncounter =
}); });
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPATK ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPATK ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPDEF ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPDEF ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!, generateModifierTypeOption(modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!, generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
]; ];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
} }
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); leaveEncounterWithoutBattle(!encounter.misc.correctMove);
}) })
.build() .build()
) )
@ -156,8 +157,8 @@ export const FieldTripEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}:second_option_prompt`, secondOptionPrompt: `${namespace}:second_option_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Return the options for Pokemon move valid for this option // Return the options for Pokemon move valid for this option
return pokemon.moveset.map((move: PokemonMove) => { return pokemon.moveset.map((move: PokemonMove) => {
@ -166,7 +167,7 @@ export const FieldTripEncounter: MysteryEncounter =
handler: () => { handler: () => {
// Pokemon and move selected // Pokemon and move selected
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:status`)); encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:status`));
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.STATUS); pokemonAndMoveChosen(pokemon, move, MoveCategory.STATUS);
return true; return true;
}, },
}; };
@ -174,30 +175,30 @@ export const FieldTripEncounter: MysteryEncounter =
}); });
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ACC ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ACC ])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!, generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
generateModifierTypeOption(scene, modifierTypes.GREAT_BALL)!, generateModifierTypeOption(modifierTypes.GREAT_BALL)!,
generateModifierTypeOption(scene, modifierTypes.IV_SCANNER)!, generateModifierTypeOption(modifierTypes.IV_SCANNER)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!, generateModifierTypeOption(modifierTypes.RARER_CANDY)!,
]; ];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
} }
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove); leaveEncounterWithoutBattle(!encounter.misc.correctMove);
}) })
.build() .build()
) )
.build(); .build();
function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) { function pokemonAndMoveChosen(pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const correctMove = move.getMove().category === correctMoveCategory; const correctMove = move.getMove().category === correctMoveCategory;
encounter.setDialogueToken("pokeName", pokemon.getNameToRender()); encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
encounter.setDialogueToken("move", move.getName()); encounter.setDialogueToken("move", move.getName());
@ -214,7 +215,7 @@ function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move:
text: `${namespace}:incorrect_exp`, text: `${namespace}:incorrect_exp`,
}, },
]; ];
setEncounterExp(scene, scene.getPlayerParty().map((p) => p.id), 50); setEncounterExp(globalScene.getPlayerParty().map((p) => p.id), 50);
} else { } else {
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
{ {
@ -228,7 +229,7 @@ function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move:
text: `${namespace}:correct_exp`, text: `${namespace}:correct_exp`,
}, },
]; ];
setEncounterExp(scene, [ pokemon.id ], 100); setEncounterExp([ pokemon.id ], 100);
} }
encounter.misc = { encounter.misc = {
correctMove: correctMove, correctMove: correctMove,

View File

@ -1,16 +1,20 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type"; import { initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
import { modifierTypes, } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { AbilityRequirement, CombinationPokemonRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { AbilityRequirement, CombinationPokemonRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import { Type } from "#enums/type"; import { Type } from "#enums/type";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import Pokemon, { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { PokemonMove } from "#app/field/pokemon";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { EncounterBattleAnim } from "#app/data/battle-anims"; import { EncounterBattleAnim } from "#app/data/battle-anims";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
@ -58,8 +62,8 @@ export const FieryFalloutEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mons // Calculate boss mons
const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA); const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA);
@ -71,7 +75,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
gender: Gender.MALE, gender: Gender.MALE,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPDEF, Stat.SPD ], 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPDEF, Stat.SPD ], 1));
} }
}, },
{ {
@ -80,7 +84,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
gender: Gender.FEMALE, gender: Gender.FEMALE,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPDEF, Stat.SPD ], 1)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPDEF, Stat.SPD ], 1));
} }
} }
], ],
@ -113,25 +117,25 @@ export const FieryFalloutEncounter: MysteryEncounter =
]; ];
// Load animations/sfx for Volcarona moves // Load animations/sfx for Volcarona moves
loadCustomMovesForEncounter(scene, [ Moves.FIRE_SPIN, Moves.QUIVER_DANCE ]); loadCustomMovesForEncounter([ Moves.FIRE_SPIN, Moves.QUIVER_DANCE ]);
scene.arena.trySetWeather(WeatherType.SUNNY, true); globalScene.arena.trySetWeather(WeatherType.SUNNY, true);
encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName()); encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName());
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
// Play animations // Play animations
const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
background.playWithoutTargets(scene, 200, 70, 2, 3); background.playWithoutTargets(200, 70, 2, 3);
const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon()!, scene.getPlayerPokemon()); const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
animation.playWithoutTargets(scene, 80, 100, 2); animation.playWithoutTargets(80, 100, 2);
scene.time.delayedCall(600, () => { globalScene.time.delayedCall(600, () => {
animation.playWithoutTargets(scene, -20, 100, 2); animation.playWithoutTargets(-20, 100, 2);
}); });
scene.time.delayedCall(1200, () => { globalScene.time.delayedCall(1200, () => {
animation.playWithoutTargets(scene, 140, 150, 2); animation.playWithoutTargets(140, 150, 2);
}); });
return true; return true;
@ -150,10 +154,10 @@ export const FieryFalloutEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { fillRemaining: true }, undefined, () => giveLeadPokemonAttackTypeBoostItem(scene)); setEncounterRewards({ fillRemaining: true }, undefined, () => giveLeadPokemonAttackTypeBoostItem());
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(
{ {
@ -168,7 +172,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
move: new PokemonMove(Moves.FIRE_SPIN), move: new PokemonMove(Moves.FIRE_SPIN),
ignorePp: true ignorePp: true
}); });
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
} }
) )
.withSimpleOption( .withSimpleOption(
@ -181,15 +185,15 @@ export const FieryFalloutEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Damage non-fire types and burn 1 random non-fire type member + give it Heatproof // Damage non-fire types and burn 1 random non-fire type member + give it Heatproof
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const nonFireTypes = scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE)); const nonFireTypes = globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE));
for (const pkm of nonFireTypes) { for (const pkm of nonFireTypes) {
const percentage = DAMAGE_PERCENTAGE / 100; const percentage = DAMAGE_PERCENTAGE / 100;
const damage = Math.floor(pkm.getMaxHp() * percentage); const damage = Math.floor(pkm.getMaxHp() * percentage);
applyDamageToPokemon(scene, pkm, damage); applyDamageToPokemon(pkm, damage);
} }
// Burn random member // Burn random member
@ -201,7 +205,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
// Burn applied // Burn applied
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender()); encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
encounter.setDialogueToken("abilityName", new Ability(Abilities.HEATPROOF, 3).name); encounter.setDialogueToken("abilityName", new Ability(Abilities.HEATPROOF, 3).name);
queueEncounterMessage(scene, `${namespace}:option.2.target_burned`); queueEncounterMessage(`${namespace}:option.2.target_burned`);
// Also permanently change the burned Pokemon's ability to Heatproof // Also permanently change the burned Pokemon's ability to Heatproof
applyAbilityOverrideToPokemon(chosenPokemon, Abilities.HEATPROOF); applyAbilityOverrideToPokemon(chosenPokemon, Abilities.HEATPROOF);
@ -209,7 +213,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
} }
// No rewards // No rewards
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
} }
) )
.withOption( .withOption(
@ -231,44 +235,44 @@ export const FieryFalloutEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Do NOT await this, to prevent player from repeatedly pressing options // Do NOT await this, to prevent player from repeatedly pressing options
transitionMysteryEncounterIntroVisuals(scene, false, false, 2000); transitionMysteryEncounterIntroVisuals(false, false, 2000);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Fire types help calm the Volcarona // Fire types help calm the Volcarona
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
await transitionMysteryEncounterIntroVisuals(scene); await transitionMysteryEncounterIntroVisuals();
setEncounterRewards(scene, setEncounterRewards(
{ fillRemaining: true }, { fillRemaining: true },
undefined, undefined,
() => { () => {
giveLeadPokemonAttackTypeBoostItem(scene); giveLeadPokemonAttackTypeBoostItem();
}); });
const primary = encounter.options[2].primaryPokemon!; const primary = encounter.options[2].primaryPokemon!;
setEncounterExp(scene, [ primary.id ], getPokemonSpecies(Species.VOLCARONA).baseExp * 2); setEncounterExp([ primary.id ], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
.build(); .build();
function giveLeadPokemonAttackTypeBoostItem(scene: BattleScene) { function giveLeadPokemonAttackTypeBoostItem() {
// Give first party pokemon attack type boost item for free at end of battle // Give first party pokemon attack type boost item for free at end of battle
const leadPokemon = scene.getPlayerParty()?.[0]; const leadPokemon = globalScene.getPlayerParty()?.[0];
if (leadPokemon) { if (leadPokemon) {
// Generate type booster held item, default to Charcoal if item fails to generate // Generate type booster held item, default to Charcoal if item fails to generate
let boosterModifierType = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER) as AttackTypeBoosterModifierType; let boosterModifierType = generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER) as AttackTypeBoosterModifierType;
if (!boosterModifierType) { if (!boosterModifierType) {
boosterModifierType = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FIRE ]) as AttackTypeBoosterModifierType; boosterModifierType = generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FIRE ]) as AttackTypeBoosterModifierType;
} }
applyModifierTypeToPlayerPokemon(scene, leadPokemon, boosterModifierType); applyModifierTypeToPlayerPokemon(leadPokemon, boosterModifierType);
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("itemName", boosterModifierType.name); encounter.setDialogueToken("itemName", boosterModifierType.name);
encounter.setDialogueToken("leadPokemon", leadPokemon.getNameToRender()); encounter.setDialogueToken("leadPokemon", leadPokemon.getNameToRender());
queueEncounterMessage(scene, `${namespace}:found_item`); queueEncounterMessage(`${namespace}:found_item`);
} }
} }

View File

@ -1,35 +1,37 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import type {
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { import {
EnemyPartyConfig, getRandomEncounterSpecies,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, setEncounterExp, leaveEncounterWithoutBattle,
setEncounterExp,
setEncounterRewards setEncounterRewards
} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import Pokemon, { EnemyPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import type {
ModifierTypeOption } from "#app/modifier/modifier-type";
import { import {
getPartyLuckValue,
getPlayerModifierTypeOptions, getPlayerModifierTypeOptions,
ModifierPoolType, ModifierPoolType,
ModifierTypeOption,
regenerateModifierPoolThresholds, regenerateModifierPoolThresholds,
} from "#app/modifier/modifier-type"; } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { TrainerSlot } from "#app/data/trainer-config";
import { getEncounterPokemonLevelForWave, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getEncounterPokemonLevelForWave, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { randSeedInt, randSeedItem } from "#app/utils"; import { randSeedInt } from "#app/utils";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
/** the i18n namespace for the encounter */ /** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/fightOrFlight"; const namespace = "mysteryEncounters/fightOrFlight";
@ -52,33 +54,25 @@ export const FightOrFlightEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon // Calculate boss mon
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
let bossSpecies: PokemonSpecies; const bossPokemon = getRandomEncounterSpecies(level, true);
if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!);
const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, scene.gameMode);
bossSpecies = getPokemonSpecies( levelSpecies );
} else {
bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
}
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender()); encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender());
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
pokemonConfigs: [{ pokemonConfigs: [{
level: level, level: level,
species: bossSpecies, species: bossPokemon.species,
dataSource: new PokemonData(bossPokemon), dataSource: new PokemonData(bossPokemon),
isBoss: true, isBoss: true,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ], tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => { mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.stat_boost`); queueEncounterMessage(`${namespace}:option.1.stat_boost`);
// Randomly boost 1 stat 2 stages // Randomly boost 1 stat 2 stages
// Cannot boost Spd, Acc, or Evasion // Cannot boost Spd, Acc, or Evasion
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randSeedInt(4, 1) ], 2)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ randSeedInt(4, 1) ], 2));
} }
}], }],
}; };
@ -87,18 +81,18 @@ export const FightOrFlightEncounter: MysteryEncounter =
// Calculate item // Calculate item
// Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER // Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER
const tier = const tier =
scene.currentBattle.waveIndex > 160 globalScene.currentBattle.waveIndex > 160
? ModifierTier.MASTER ? ModifierTier.MASTER
: scene.currentBattle.waveIndex > 120 : globalScene.currentBattle.waveIndex > 120
? ModifierTier.ROGUE ? ModifierTier.ROGUE
: scene.currentBattle.waveIndex > 40 : globalScene.currentBattle.waveIndex > 40
? ModifierTier.ULTRA ? ModifierTier.ULTRA
: ModifierTier.GREAT; : ModifierTier.GREAT;
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0); regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
let item: ModifierTypeOption | null = null; let item: ModifierTypeOption | null = null;
// TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward // TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward
while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") { while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") {
item = getPlayerModifierTypeOptions(1, scene.getPlayerParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0]; item = getPlayerModifierTypeOptions(1, globalScene.getPlayerParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0];
} }
encounter.setDialogueToken("itemName", item.type.name); encounter.setDialogueToken("itemName", item.type.name);
encounter.misc = item; encounter.misc = item;
@ -144,12 +138,12 @@ export const FightOrFlightEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Pick battle // Pick battle
// Pokemon will randomly boost 1 stat by 2 stages // Pokemon will randomly boost 1 stat by 2 stages
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption; const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
} }
) )
.withOption( .withOption(
@ -166,16 +160,16 @@ export const FightOrFlightEncounter: MysteryEncounter =
} }
] ]
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick steal // Pick steal
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption; const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
// Use primaryPokemon to execute the thievery // Use primaryPokemon to execute the thievery
const primaryPokemon = encounter.options[1].primaryPokemon!; const primaryPokemon = encounter.options[1].primaryPokemon!;
setEncounterExp(scene, primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); setEncounterExp(primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -189,9 +183,9 @@ export const FightOrFlightEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,10 +1,13 @@
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { FieldPosition } from "#app/field/pokemon";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
@ -80,14 +83,14 @@ export const FunAndGamesEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
scene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3"); globalScene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3");
encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(Species.WOBBUFFET).getName()); encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(Species.WOBBUFFET).getName());
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
scene.fadeAndSwitchBgm("mystery_encounter_fun_and_games"); globalScene.fadeAndSwitchBgm("mystery_encounter_fun_and_games");
return true; return true;
}) })
.withOption(MysteryEncounterOptionBuilder .withOption(MysteryEncounterOptionBuilder
@ -102,9 +105,9 @@ export const FunAndGamesEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Select Pokemon for minigame // Select Pokemon for minigame
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.misc = { encounter.misc = {
playerPokemon: pokemon, playerPokemon: pokemon,
@ -113,28 +116,28 @@ export const FunAndGamesEncounter: MysteryEncounter =
// Only Pokemon that are not KOed/legal can be selected // Only Pokemon that are not KOed/legal can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Start minigame // Start minigame
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.misc.turnsRemaining = 3; encounter.misc.turnsRemaining = 3;
// Update money // Update money
const moneyCost = (encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney; const moneyCost = (encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney;
updatePlayerMoney(scene, -moneyCost, true, false); updatePlayerMoney(-moneyCost, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:paid_money", { amount: moneyCost })); await showEncounterText(i18next.t("mysteryEncounterMessages:paid_money", { amount: moneyCost }));
// Handlers for battle events // Handlers for battle events
encounter.onTurnStart = handleNextTurn; // triggered during TurnInitPhase encounter.onTurnStart = handleNextTurn; // triggered during TurnInitPhase
encounter.doContinueEncounter = handleLoseMinigame; // triggered during MysteryEncounterRewardsPhase, post VictoryPhase if the player KOs Wobbuffet encounter.doContinueEncounter = handleLoseMinigame; // triggered during MysteryEncounterRewardsPhase, post VictoryPhase if the player KOs Wobbuffet
hideShowmanIntroSprite(scene); hideShowmanIntroSprite();
await summonPlayerPokemon(scene); await summonPlayerPokemon();
await showWobbuffetHealthBar(scene); await showWobbuffetHealthBar();
return true; return true;
}) })
@ -150,22 +153,22 @@ export const FunAndGamesEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
await transitionMysteryEncounterIntroVisuals(scene, true, true); await transitionMysteryEncounterIntroVisuals(true, true);
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
.build(); .build();
async function summonPlayerPokemon(scene: BattleScene) { async function summonPlayerPokemon() {
return new Promise<void>(async resolve => { return new Promise<void>(async resolve => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const playerPokemon = encounter.misc.playerPokemon; const playerPokemon = encounter.misc.playerPokemon;
// Swaps the chosen Pokemon and the first player's lead Pokemon in the party // Swaps the chosen Pokemon and the first player's lead Pokemon in the party
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
const chosenIndex = party.indexOf(playerPokemon); const chosenIndex = party.indexOf(playerPokemon);
if (chosenIndex !== 0) { if (chosenIndex !== 0) {
const leadPokemon = party[0]; const leadPokemon = party[0];
@ -175,36 +178,36 @@ async function summonPlayerPokemon(scene: BattleScene) {
// Do trainer summon animation // Do trainer summon animation
let playerAnimationPromise: Promise<void> | undefined; let playerAnimationPromise: Promise<void> | undefined;
scene.ui.showText(i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(playerPokemon) })); globalScene.ui.showText(i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(playerPokemon) }));
scene.pbTray.hide(); globalScene.pbTray.hide();
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`); globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
scene.time.delayedCall(562, () => { globalScene.time.delayedCall(562, () => {
scene.trainer.setFrame("2"); globalScene.trainer.setFrame("2");
scene.time.delayedCall(64, () => { globalScene.time.delayedCall(64, () => {
scene.trainer.setFrame("3"); globalScene.trainer.setFrame("3");
}); });
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: scene.trainer, targets: globalScene.trainer,
x: -36, x: -36,
duration: 1000, duration: 1000,
onComplete: () => scene.trainer.setVisible(false) onComplete: () => globalScene.trainer.setVisible(false)
}); });
scene.time.delayedCall(750, () => { globalScene.time.delayedCall(750, () => {
playerAnimationPromise = summonPlayerPokemonAnimation(scene, playerPokemon); playerAnimationPromise = summonPlayerPokemonAnimation(playerPokemon);
}); });
// Also loads Wobbuffet data (cannot be shiny) // Also loads Wobbuffet data (cannot be shiny)
const enemySpecies = getPokemonSpecies(Species.WOBBUFFET); const enemySpecies = getPokemonSpecies(Species.WOBBUFFET);
scene.currentBattle.enemyParty = []; globalScene.currentBattle.enemyParty = [];
const wobbuffet = scene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false, true); const wobbuffet = globalScene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false, true);
wobbuffet.ivs = [ 0, 0, 0, 0, 0, 0 ]; wobbuffet.ivs = [ 0, 0, 0, 0, 0, 0 ];
wobbuffet.setNature(Nature.MILD); wobbuffet.setNature(Nature.MILD);
wobbuffet.setAlpha(0); wobbuffet.setAlpha(0);
wobbuffet.setVisible(false); wobbuffet.setVisible(false);
wobbuffet.calculateStats(); wobbuffet.calculateStats();
scene.currentBattle.enemyParty[0] = wobbuffet; globalScene.currentBattle.enemyParty[0] = wobbuffet;
scene.gameData.setPokemonSeen(wobbuffet, true); globalScene.gameData.setPokemonSeen(wobbuffet, true);
await wobbuffet.loadAssets(); await wobbuffet.loadAssets();
const id = setInterval(checkPlayerAnimationPromise, 500); const id = setInterval(checkPlayerAnimationPromise, 500);
async function checkPlayerAnimationPromise() { async function checkPlayerAnimationPromise() {
@ -217,37 +220,37 @@ async function summonPlayerPokemon(scene: BattleScene) {
}); });
} }
function handleLoseMinigame(scene: BattleScene) { function handleLoseMinigame() {
return new Promise<void>(async resolve => { return new Promise<void>(async resolve => {
// Check Wobbuffet is still alive // Check Wobbuffet is still alive
const wobbuffet = scene.getEnemyPokemon(); const wobbuffet = globalScene.getEnemyPokemon();
if (!wobbuffet || wobbuffet.isFainted(true) || wobbuffet.hp === 0) { if (!wobbuffet || wobbuffet.isFainted(true) || wobbuffet.hp === 0) {
// Player loses // Player loses
// End the battle // End the battle
if (wobbuffet) { if (wobbuffet) {
wobbuffet.hideInfo(); wobbuffet.hideInfo();
scene.field.remove(wobbuffet); globalScene.field.remove(wobbuffet);
} }
transitionMysteryEncounterIntroVisuals(scene, true, true); transitionMysteryEncounterIntroVisuals(true, true);
scene.currentBattle.enemyParty = []; globalScene.currentBattle.enemyParty = [];
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined; globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
await showEncounterText(scene, `${namespace}:ko`); await showEncounterText(`${namespace}:ko`);
const reviveCost = scene.getWaveMoneyAmount(1.5); const reviveCost = globalScene.getWaveMoneyAmount(1.5);
updatePlayerMoney(scene, -reviveCost, true, false); updatePlayerMoney(-reviveCost, true, false);
} }
resolve(); resolve();
}); });
} }
function handleNextTurn(scene: BattleScene) { function handleNextTurn() {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const wobbuffet = scene.getEnemyPokemon(); const wobbuffet = globalScene.getEnemyPokemon();
if (!wobbuffet) { if (!wobbuffet) {
// Should never be triggered, just handling the edge case // Should never be triggered, just handling the edge case
handleLoseMinigame(scene); handleLoseMinigame();
return true; return true;
} }
if (encounter.misc.turnsRemaining <= 0) { if (encounter.misc.turnsRemaining <= 0) {
@ -257,15 +260,15 @@ function handleNextTurn(scene: BattleScene) {
let isHealPhase = false; let isHealPhase = false;
if (healthRatio < 0.03) { if (healthRatio < 0.03) {
// Grand prize // Grand prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:best_result`; resultMessageKey = `${namespace}:best_result`;
} else if (healthRatio < 0.15) { } else if (healthRatio < 0.15) {
// 2nd prize // 2nd prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:great_result`; resultMessageKey = `${namespace}:great_result`;
} else if (healthRatio < 0.33) { } else if (healthRatio < 0.33) {
// 3rd prize // 3rd prize
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false });
resultMessageKey = `${namespace}:good_result`; resultMessageKey = `${namespace}:good_result`;
} else { } else {
// No prize // No prize
@ -275,22 +278,22 @@ function handleNextTurn(scene: BattleScene) {
// End the battle // End the battle
wobbuffet.hideInfo(); wobbuffet.hideInfo();
scene.field.remove(wobbuffet); globalScene.field.remove(wobbuffet);
scene.currentBattle.enemyParty = []; globalScene.currentBattle.enemyParty = [];
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined; globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
leaveEncounterWithoutBattle(scene, isHealPhase); leaveEncounterWithoutBattle(isHealPhase);
// Must end the TurnInit phase prematurely so battle phases aren't added to queue // Must end the TurnInit phase prematurely so battle phases aren't added to queue
queueEncounterMessage(scene, `${namespace}:end_game`); queueEncounterMessage(`${namespace}:end_game`);
queueEncounterMessage(scene, resultMessageKey); queueEncounterMessage(resultMessageKey);
// Skip remainder of TurnInitPhase // Skip remainder of TurnInitPhase
return true; return true;
} else { } else {
if (encounter.misc.turnsRemaining < 3) { if (encounter.misc.turnsRemaining < 3) {
// Display charging messages on turns that aren't the initial turn // Display charging messages on turns that aren't the initial turn
queueEncounterMessage(scene, `${namespace}:charging_continue`); queueEncounterMessage(`${namespace}:charging_continue`);
} }
queueEncounterMessage(scene, `${namespace}:turn_remaining_${encounter.misc.turnsRemaining}`); queueEncounterMessage(`${namespace}:turn_remaining_${encounter.misc.turnsRemaining}`);
encounter.misc.turnsRemaining--; encounter.misc.turnsRemaining--;
} }
@ -298,33 +301,33 @@ function handleNextTurn(scene: BattleScene) {
return false; return false;
} }
async function showWobbuffetHealthBar(scene: BattleScene) { async function showWobbuffetHealthBar() {
const wobbuffet = scene.getEnemyPokemon()!; const wobbuffet = globalScene.getEnemyPokemon()!;
scene.add.existing(wobbuffet); globalScene.add.existing(wobbuffet);
scene.field.add(wobbuffet); globalScene.field.add(wobbuffet);
const playerPokemon = scene.getPlayerPokemon() as Pokemon; const playerPokemon = globalScene.getPlayerPokemon() as Pokemon;
if (playerPokemon?.isOnField()) { if (playerPokemon?.isOnField()) {
scene.field.moveBelow(wobbuffet, playerPokemon); globalScene.field.moveBelow(wobbuffet, playerPokemon);
} }
// Show health bar and trigger cry // Show health bar and trigger cry
wobbuffet.showInfo(); wobbuffet.showInfo();
scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
wobbuffet.cry(); wobbuffet.cry();
}); });
wobbuffet.resetSummonData(); wobbuffet.resetSummonData();
// Track the HP change across turns // Track the HP change across turns
scene.currentBattle.mysteryEncounter!.misc.wobbuffetHealth = wobbuffet.hp; globalScene.currentBattle.mysteryEncounter!.misc.wobbuffetHealth = wobbuffet.hp;
} }
function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon): Promise<void> { function summonPlayerPokemonAnimation(pokemon: PlayerPokemon): Promise<void> {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const pokeball = scene.addFieldSprite(36, 80, "pb", getPokeballAtlasKey(pokemon.pokeball)); const pokeball = globalScene.addFieldSprite(36, 80, "pb", getPokeballAtlasKey(pokemon.pokeball));
pokeball.setVisible(false); pokeball.setVisible(false);
pokeball.setOrigin(0.5, 0.625); pokeball.setOrigin(0.5, 0.625);
scene.field.add(pokeball); globalScene.field.add(pokeball);
pokemon.setFieldPosition(FieldPosition.CENTER, 0); pokemon.setFieldPosition(FieldPosition.CENTER, 0);
@ -332,32 +335,32 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
pokeball.setVisible(true); pokeball.setVisible(true);
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
duration: 650, duration: 650,
x: 100 + fpOffset[0] x: 100 + fpOffset[0]
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
duration: 150, duration: 150,
ease: "Cubic.easeOut", ease: "Cubic.easeOut",
y: 70 + fpOffset[1], y: 70 + fpOffset[1],
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: pokeball, targets: pokeball,
duration: 500, duration: 500,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
angle: 1440, angle: 1440,
y: 132 + fpOffset[1], y: 132 + fpOffset[1],
onComplete: () => { onComplete: () => {
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
pokeball.destroy(); pokeball.destroy();
scene.add.existing(pokemon); globalScene.add.existing(pokemon);
scene.field.add(pokemon); globalScene.field.add(pokemon);
addPokeballOpenParticles(scene, pokemon.x, pokemon.y - 16, pokemon.pokeball); addPokeballOpenParticles(pokemon.x, pokemon.y - 16, pokemon.pokeball);
scene.updateModifiers(true); globalScene.updateModifiers(true);
scene.updateFieldScale(); globalScene.updateFieldScale();
pokemon.showInfo(); pokemon.showInfo();
pokemon.playAnim(); pokemon.playAnim();
pokemon.setVisible(true); pokemon.setVisible(true);
@ -365,8 +368,8 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
pokemon.setScale(0.5); pokemon.setScale(0.5);
pokemon.tint(getPokeballTintColor(pokemon.pokeball)); pokemon.tint(getPokeballTintColor(pokemon.pokeball));
pokemon.untint(250, "Sine.easeIn"); pokemon.untint(250, "Sine.easeIn");
scene.updateFieldScale(); globalScene.updateFieldScale();
scene.tweens.add({ globalScene.tweens.add({
targets: pokemon, targets: pokemon,
duration: 250, duration: 250,
ease: "Sine.easeIn", ease: "Sine.easeIn",
@ -375,15 +378,15 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
pokemon.getSprite().clearTint(); pokemon.getSprite().clearTint();
pokemon.resetSummonData(); pokemon.resetSummonData();
scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
if (pokemon.isShiny()) { if (pokemon.isShiny()) {
scene.unshiftPhase(new ShinySparklePhase(scene, pokemon.getBattlerIndex())); globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
} }
pokemon.resetTurnData(); pokemon.resetTurnData();
scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
scene.pushPhase(new PostSummonPhase(scene, pokemon.getBattlerIndex())); globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex()));
resolve(); resolve();
}); });
} }
@ -395,13 +398,13 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
}); });
} }
function hideShowmanIntroSprite(scene: BattleScene) { function hideShowmanIntroSprite() {
const carnivalGame = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(0)[0]; const carnivalGame = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(0)[0];
const wobbuffet = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1)[0]; const wobbuffet = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1)[0];
const showMan = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(2)[0]; const showMan = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(2)[0];
// Hide the showman // Hide the showman
scene.tweens.add({ globalScene.tweens.add({
targets: showMan, targets: showMan,
x: "+=16", x: "+=16",
y: "-=16", y: "-=16",
@ -411,7 +414,7 @@ function hideShowmanIntroSprite(scene: BattleScene) {
}); });
// Slide the Wobbuffet and Game over slightly // Slide the Wobbuffet and Game over slightly
scene.tweens.add({ globalScene.tweens.add({
targets: [ wobbuffet, carnivalGame ], targets: [ wobbuffet, carnivalGame ],
x: "+=16", x: "+=16",
ease: "Sine.easeInOut", ease: "Sine.easeInOut",

View File

@ -2,20 +2,26 @@ import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterReward
import { TrainerSlot, } from "#app/data/trainer-config"; import { TrainerSlot, } from "#app/data/trainer-config";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { MusicPreference } from "#app/system/settings/settings"; import { MusicPreference } from "#app/system/settings/settings";
import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import type { ModifierTypeOption } from "#app/modifier/modifier-type";
import { getPlayerModifierTypeOptions, ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species";
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
import { getTypeRgb } from "#app/data/type"; import { getTypeRgb } from "#app/data/type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils"; import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import type Pokemon from "#app/field/pokemon";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import i18next from "i18next"; import i18next from "i18next";
import { Gender, getGenderSymbol } from "#app/data/gender"; import { Gender, getGenderSymbol } from "#app/data/gender";
@ -102,24 +108,24 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Load bgm // Load bgm
let bgmKey: string; let bgmKey: string;
if (scene.musicPreference === MusicPreference.GENFIVE) { if (globalScene.musicPreference === MusicPreference.GENFIVE) {
bgmKey = "mystery_encounter_gen_5_gts"; bgmKey = "mystery_encounter_gen_5_gts";
scene.loadBgm(bgmKey, `${bgmKey}.mp3`); globalScene.loadBgm(bgmKey, `${bgmKey}.mp3`);
} else { } else {
// Mixed option // Mixed option
bgmKey = "mystery_encounter_gen_6_gts"; bgmKey = "mystery_encounter_gen_6_gts";
scene.loadBgm(bgmKey, `${bgmKey}.mp3`); globalScene.loadBgm(bgmKey, `${bgmKey}.mp3`);
} }
// Load possible trade options // Load possible trade options
// Maps current party member's id to 3 EnemyPokemon objects // Maps current party member's id to 3 EnemyPokemon objects
// None of the trade options can be the same species // None of the trade options can be the same species
const tradeOptionsMap: Map<number, EnemyPokemon[]> = getPokemonTradeOptions(scene); const tradeOptionsMap: Map<number, EnemyPokemon[]> = getPokemonTradeOptions();
encounter.misc = { encounter.misc = {
tradeOptionsMap, tradeOptionsMap,
bgmKey bgmKey
@ -127,8 +133,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart(() => {
scene.fadeAndSwitchBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey); globalScene.fadeAndSwitchBgm(globalScene.currentBattle.mysteryEncounter!.misc.bgmKey);
return true; return true;
}) })
.withOption( .withOption(
@ -140,8 +146,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.1.tooltip`, buttonTooltip: `${namespace}:option.1.tooltip`,
secondOptionPrompt: `${namespace}:option.1.trade_options_prompt`, secondOptionPrompt: `${namespace}:option.1.trade_options_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get the trade species options for the selected pokemon // Get the trade species options for the selected pokemon
const tradeOptionsMap: Map<number, EnemyPokemon[]> = encounter.misc.tradeOptionsMap; const tradeOptionsMap: Map<number, EnemyPokemon[]> = encounter.misc.tradeOptionsMap;
@ -165,17 +171,17 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[tradePokemon.formIndex].formName : null; const formName = tradePokemon.species.forms && tradePokemon.species.forms.length > tradePokemon.formIndex ? tradePokemon.species.forms[tradePokemon.formIndex].formName : null;
const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : ""); const line1 = i18next.t("pokemonInfoContainer:ability") + " " + tradePokemon.getAbility().name + (tradePokemon.getGender() !== Gender.GENDERLESS ? " | " + i18next.t("pokemonInfoContainer:gender") + " " + getGenderSymbol(tradePokemon.getGender()) : "");
const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : ""); const line2 = i18next.t("pokemonInfoContainer:nature") + " " + getNatureName(tradePokemon.getNature()) + (formName ? " | " + i18next.t("pokemonInfoContainer:form") + " " + formName : "");
showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false); showEncounterText(`${line1}\n${line2}`, 0, 0, false);
}, },
}; };
return option; return option;
}); });
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon; const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon;
const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon; const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon;
const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier)); const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier));
@ -185,32 +191,32 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
encounter.setDialogueToken("tradeTrainerName", traderName.trim()); encounter.setDialogueToken("tradeTrainerName", traderName.trim());
// Remove the original party member from party // Remove the original party member from party
scene.removePokemonFromPlayerParty(tradedPokemon, false); globalScene.removePokemonFromPlayerParty(tradedPokemon, false);
// Set data properly, then generate the new Pokemon's assets // Set data properly, then generate the new Pokemon's assets
receivedPokemonData.passive = tradedPokemon.passive; receivedPokemonData.passive = tradedPokemon.passive;
// Pokeball to Ultra ball, randomly // Pokeball to Ultra ball, randomly
receivedPokemonData.pokeball = randInt(4) as PokeballType; receivedPokemonData.pokeball = randInt(4) as PokeballType;
const dataSource = new PokemonData(receivedPokemonData); const dataSource = new PokemonData(receivedPokemonData);
const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource); const newPlayerPokemon = globalScene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
scene.getPlayerParty().push(newPlayerPokemon); globalScene.getPlayerParty().push(newPlayerPokemon);
await newPlayerPokemon.loadAssets(); await newPlayerPokemon.loadAssets();
for (const mod of modifiers) { for (const mod of modifiers) {
mod.pokemonId = newPlayerPokemon.id; mod.pokemonId = newPlayerPokemon.id;
scene.addModifier(mod, true, false, false, true); globalScene.addModifier(mod, true, false, false, true);
} }
// Show the trade animation // Show the trade animation
await showTradeBackground(scene); await showTradeBackground();
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon); await doPokemonTradeSequence(tradedPokemon, newPlayerPokemon);
await showEncounterText(scene, `${namespace}:trade_received`, null, 0, true, 4000); await showEncounterText(`${namespace}:trade_received`, null, 0, true, 4000);
scene.playBgm(encounter.misc.bgmKey); globalScene.playBgm(encounter.misc.bgmKey);
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon); await addPokemonDataToDexAndValidateAchievements(newPlayerPokemon);
await hideTradeBackground(scene); await hideTradeBackground();
tradedPokemon.destroy(); tradedPokemon.destroy();
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -222,19 +228,19 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
buttonLabel: `${namespace}:option.2.label`, buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`, buttonTooltip: `${namespace}:option.2.tooltip`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Randomly generate a Wonder Trade pokemon // Randomly generate a Wonder Trade pokemon
const randomTradeOption = generateTradeOption(scene.getPlayerParty().map(p => p.species)); const randomTradeOption = generateTradeOption(globalScene.getPlayerParty().map(p => p.species));
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false); const tradePokemon = new EnemyPokemon(randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
// Extra shiny roll at 1/128 odds (boosted by events and charms) // Extra shiny roll at 1/128 odds (boosted by events and charms)
if (!tradePokemon.shiny) { if (!tradePokemon.shiny) {
const shinyThreshold = new NumberHolder(WONDER_TRADE_SHINY_CHANCE); const shinyThreshold = new NumberHolder(WONDER_TRADE_SHINY_CHANCE);
if (scene.eventManager.isEventActive()) { if (globalScene.eventManager.isEventActive()) {
shinyThreshold.value *= scene.eventManager.getShinyMultiplier(); shinyThreshold.value *= globalScene.eventManager.getShinyMultiplier();
} }
scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold); globalScene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
// Base shiny chance of 512/65536 -> 1/128, affected by events and Shiny Charms // Base shiny chance of 512/65536 -> 1/128, affected by events and Shiny Charms
// Maximum shiny chance of 4096/65536 -> 1/16, cannot improve further after that // Maximum shiny chance of 4096/65536 -> 1/16, cannot improve further after that
@ -248,7 +254,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
if (tradePokemon.species.abilityHidden) { if (tradePokemon.species.abilityHidden) {
if (tradePokemon.abilityIndex < hiddenIndex) { if (tradePokemon.abilityIndex < hiddenIndex) {
const hiddenAbilityChance = new NumberHolder(64); const hiddenAbilityChance = new NumberHolder(64);
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
@ -281,10 +287,10 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
encounter.misc.receivedPokemon = tradePokemon; encounter.misc.receivedPokemon = tradePokemon;
}; };
return selectPokemonForOption(scene, onPokemonSelected); return selectPokemonForOption(onPokemonSelected);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon; const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon;
const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon; const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon;
const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier)); const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier));
@ -294,31 +300,31 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
encounter.setDialogueToken("tradeTrainerName", traderName.trim()); encounter.setDialogueToken("tradeTrainerName", traderName.trim());
// Remove the original party member from party // Remove the original party member from party
scene.removePokemonFromPlayerParty(tradedPokemon, false); globalScene.removePokemonFromPlayerParty(tradedPokemon, false);
// Set data properly, then generate the new Pokemon's assets // Set data properly, then generate the new Pokemon's assets
receivedPokemonData.passive = tradedPokemon.passive; receivedPokemonData.passive = tradedPokemon.passive;
receivedPokemonData.pokeball = randInt(4) as PokeballType; receivedPokemonData.pokeball = randInt(4) as PokeballType;
const dataSource = new PokemonData(receivedPokemonData); const dataSource = new PokemonData(receivedPokemonData);
const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource); const newPlayerPokemon = globalScene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
scene.getPlayerParty().push(newPlayerPokemon); globalScene.getPlayerParty().push(newPlayerPokemon);
await newPlayerPokemon.loadAssets(); await newPlayerPokemon.loadAssets();
for (const mod of modifiers) { for (const mod of modifiers) {
mod.pokemonId = newPlayerPokemon.id; mod.pokemonId = newPlayerPokemon.id;
scene.addModifier(mod, true, false, false, true); globalScene.addModifier(mod, true, false, false, true);
} }
// Show the trade animation // Show the trade animation
await showTradeBackground(scene); await showTradeBackground();
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon); await doPokemonTradeSequence(tradedPokemon, newPlayerPokemon);
await showEncounterText(scene, `${namespace}:trade_received`, null, 0, true, 4000); await showEncounterText(`${namespace}:trade_received`, null, 0, true, 4000);
scene.playBgm(encounter.misc.bgmKey); globalScene.playBgm(encounter.misc.bgmKey);
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon); await addPokemonDataToDexAndValidateAchievements(newPlayerPokemon);
await hideTradeBackground(scene); await hideTradeBackground();
tradedPokemon.destroy(); tradedPokemon.destroy();
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
}) })
.build() .build()
) )
@ -330,8 +336,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
buttonTooltip: `${namespace}:option.3.tooltip`, buttonTooltip: `${namespace}:option.3.tooltip`,
secondOptionPrompt: `${namespace}:option.3.trade_options_prompt`, secondOptionPrompt: `${namespace}:option.3.trade_options_prompt`,
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter((it) => { const validItems = pokemon.getHeldItems().filter((it) => {
@ -359,18 +365,18 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
return it.isTransferable; return it.isTransferable;
}).length > 0; }).length > 0;
if (!meetsReqs) { if (!meetsReqs) {
return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null; return getEncounterText(`${namespace}:option.3.invalid_selection`) ?? null;
} }
return null; return null;
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const modifier = encounter.misc.chosenModifier as PokemonHeldItemModifier; const modifier = encounter.misc.chosenModifier as PokemonHeldItemModifier;
const party = scene.getPlayerParty(); const party = globalScene.getPlayerParty();
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Check tier of the traded item, the received item will be one tier up // Check tier of the traded item, the received item will be one tier up
@ -397,16 +403,16 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
} }
encounter.setDialogueToken("itemName", item.type.name); encounter.setDialogueToken("itemName", item.type.name);
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false }); setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
chosenPokemon.loseHeldItem(modifier, false); chosenPokemon.loseHeldItem(modifier, false);
await scene.updateModifiers(true, true); await globalScene.updateModifiers(true, true);
// Generate a trainer name // Generate a trainer name
const traderName = generateRandomTraderName(); const traderName = generateRandomTraderName();
encounter.setDialogueToken("tradeTrainerName", traderName.trim()); encounter.setDialogueToken("tradeTrainerName", traderName.trim());
await showEncounterText(scene, `${namespace}:item_trade_selected`); await showEncounterText(`${namespace}:item_trade_selected`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -420,26 +426,26 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )
.build(); .build();
function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]> { function getPokemonTradeOptions(): Map<number, EnemyPokemon[]> {
const tradeOptionsMap: Map<number, EnemyPokemon[]> = new Map<number, EnemyPokemon[]>(); const tradeOptionsMap: Map<number, EnemyPokemon[]> = new Map<number, EnemyPokemon[]>();
// Starts by filtering out any current party members as valid resulting species // Starts by filtering out any current party members as valid resulting species
const alreadyUsedSpecies: PokemonSpecies[] = scene.getPlayerParty().map(p => p.species); const alreadyUsedSpecies: PokemonSpecies[] = globalScene.getPlayerParty().map(p => p.species);
scene.getPlayerParty().forEach(pokemon => { globalScene.getPlayerParty().forEach(pokemon => {
// If the party member is legendary/mythical, the only trade options available are always pulled from generation-specific legendary trade pools // If the party member is legendary/mythical, the only trade options available are always pulled from generation-specific legendary trade pools
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) { if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
const generation = pokemon.species.generation; const generation = pokemon.species.generation;
const tradeOptions: EnemyPokemon[] = LEGENDARY_TRADE_POOLS[generation].map(s => { const tradeOptions: EnemyPokemon[] = LEGENDARY_TRADE_POOLS[generation].map(s => {
const pokemonSpecies = getPokemonSpecies(s); const pokemonSpecies = getPokemonSpecies(s);
return new EnemyPokemon(scene, pokemonSpecies, 5, TrainerSlot.NONE, false); return new EnemyPokemon(pokemonSpecies, 5, TrainerSlot.NONE, false);
}); });
tradeOptionsMap.set(pokemon.id, tradeOptions); tradeOptionsMap.set(pokemon.id, tradeOptions);
} else { } else {
@ -454,7 +460,7 @@ function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]>
// Add trade options to map // Add trade options to map
tradeOptionsMap.set(pokemon.id, tradeOptions.map(s => { tradeOptionsMap.set(pokemon.id, tradeOptions.map(s => {
return new EnemyPokemon(scene, s, pokemon.level, TrainerSlot.NONE, false); return new EnemyPokemon(s, pokemon.level, TrainerSlot.NONE, false);
})); }));
} }
}); });
@ -497,28 +503,28 @@ function generateTradeOption(alreadyUsedSpecies: PokemonSpecies[], originalBst?:
return newSpecies!; return newSpecies!;
} }
function showTradeBackground(scene: BattleScene) { function showTradeBackground() {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const tradeContainer = scene.add.container(0, -scene.game.canvas.height / 6); const tradeContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6);
tradeContainer.setName("Trade Background"); tradeContainer.setName("Trade Background");
const flyByStaticBg = scene.add.rectangle(0, 0, scene.game.canvas.width / 6, scene.game.canvas.height / 6, 0); const flyByStaticBg = globalScene.add.rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0);
flyByStaticBg.setName("Black Background"); flyByStaticBg.setName("Black Background");
flyByStaticBg.setOrigin(0, 0); flyByStaticBg.setOrigin(0, 0);
flyByStaticBg.setVisible(false); flyByStaticBg.setVisible(false);
tradeContainer.add(flyByStaticBg); tradeContainer.add(flyByStaticBg);
const tradeBaseBg = scene.add.image(0, 0, "default_bg"); const tradeBaseBg = globalScene.add.image(0, 0, "default_bg");
tradeBaseBg.setName("Trade Background Image"); tradeBaseBg.setName("Trade Background Image");
tradeBaseBg.setOrigin(0, 0); tradeBaseBg.setOrigin(0, 0);
tradeContainer.add(tradeBaseBg); tradeContainer.add(tradeBaseBg);
scene.fieldUI.add(tradeContainer); globalScene.fieldUI.add(tradeContainer);
scene.fieldUI.bringToTop(tradeContainer); globalScene.fieldUI.bringToTop(tradeContainer);
tradeContainer.setVisible(true); tradeContainer.setVisible(true);
tradeContainer.alpha = 0; tradeContainer.alpha = 0;
scene.tweens.add({ globalScene.tweens.add({
targets: tradeContainer, targets: tradeContainer,
alpha: 1, alpha: 1,
duration: 500, duration: 500,
@ -530,17 +536,17 @@ function showTradeBackground(scene: BattleScene) {
}); });
} }
function hideTradeBackground(scene: BattleScene) { function hideTradeBackground() {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const transformationContainer = scene.fieldUI.getByName("Trade Background"); const transformationContainer = globalScene.fieldUI.getByName("Trade Background");
scene.tweens.add({ globalScene.tweens.add({
targets: transformationContainer, targets: transformationContainer,
alpha: 0, alpha: 0,
duration: 1000, duration: 1000,
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
onComplete: () => { onComplete: () => {
scene.fieldUI.remove(transformationContainer, true); globalScene.fieldUI.remove(transformationContainer, true);
resolve(); resolve();
} }
}); });
@ -549,13 +555,12 @@ function hideTradeBackground(scene: BattleScene) {
/** /**
* Initiates an "evolution-like" animation to transform a previousPokemon (presumably from the player's party) into a new one, not necessarily an evolution species. * Initiates an "evolution-like" animation to transform a previousPokemon (presumably from the player's party) into a new one, not necessarily an evolution species.
* @param scene
* @param tradedPokemon * @param tradedPokemon
* @param receivedPokemon * @param receivedPokemon
*/ */
function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon, receivedPokemon: PlayerPokemon) { function doPokemonTradeSequence(tradedPokemon: PlayerPokemon, receivedPokemon: PlayerPokemon) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const tradeContainer = scene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container; const tradeContainer = globalScene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container;
const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image; const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image;
let tradedPokemonSprite: Phaser.GameObjects.Sprite; let tradedPokemonSprite: Phaser.GameObjects.Sprite;
@ -564,8 +569,8 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
let receivedPokemonTintSprite: Phaser.GameObjects.Sprite; let receivedPokemonTintSprite: Phaser.GameObjects.Sprite;
const getPokemonSprite = () => { const getPokemonSprite = () => {
const ret = scene.addPokemonSprite(tradedPokemon, tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pkmn__sub"); const ret = globalScene.addPokemonSprite(tradedPokemon, tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pkmn__sub");
ret.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true }); ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
return ret; return ret;
}; };
@ -589,7 +594,7 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", tradedPokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", tradedPokemon.getSpriteKey());
sprite.setPipelineData("shiny", tradedPokemon.shiny); sprite.setPipelineData("shiny", tradedPokemon.shiny);
@ -610,7 +615,7 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
console.error(`Failed to play animation for ${spriteKey}`, err); console.error(`Failed to play animation for ${spriteKey}`, err);
} }
sprite.setPipeline(scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) }); sprite.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(tradedPokemon.getTeraType()) });
sprite.setPipelineData("ignoreTimeTint", true); sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", receivedPokemon.getSpriteKey()); sprite.setPipelineData("spriteKey", receivedPokemon.getSpriteKey());
sprite.setPipelineData("shiny", receivedPokemon.shiny); sprite.setPipelineData("shiny", receivedPokemon.shiny);
@ -625,45 +630,45 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
// Traded pokemon pokeball // Traded pokemon pokeball
const tradedPbAtlasKey = getPokeballAtlasKey(tradedPokemon.pokeball); const tradedPbAtlasKey = getPokeballAtlasKey(tradedPokemon.pokeball);
const tradedPokeball: Phaser.GameObjects.Sprite = scene.add.sprite(tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pb", tradedPbAtlasKey); const tradedPokeball: Phaser.GameObjects.Sprite = globalScene.add.sprite(tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pb", tradedPbAtlasKey);
tradedPokeball.setVisible(false); tradedPokeball.setVisible(false);
tradeContainer.add(tradedPokeball); tradeContainer.add(tradedPokeball);
// Received pokemon pokeball // Received pokemon pokeball
const receivedPbAtlasKey = getPokeballAtlasKey(receivedPokemon.pokeball); const receivedPbAtlasKey = getPokeballAtlasKey(receivedPokemon.pokeball);
const receivedPokeball: Phaser.GameObjects.Sprite = scene.add.sprite(tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pb", receivedPbAtlasKey); const receivedPokeball: Phaser.GameObjects.Sprite = globalScene.add.sprite(tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pb", receivedPbAtlasKey);
receivedPokeball.setVisible(false); receivedPokeball.setVisible(false);
tradeContainer.add(receivedPokeball); tradeContainer.add(receivedPokeball);
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokemonSprite, targets: tradedPokemonSprite,
alpha: 1, alpha: 1,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: 500, duration: 500,
onComplete: async () => { onComplete: async () => {
scene.fadeOutBgm(1000, false); globalScene.fadeOutBgm(1000, false);
await showEncounterText(scene, `${namespace}:pokemon_trade_selected`); await showEncounterText(`${namespace}:pokemon_trade_selected`);
tradedPokemon.cry(); tradedPokemon.cry();
scene.playBgm("evolution"); globalScene.playBgm("evolution");
await showEncounterText(scene, `${namespace}:pokemon_trade_goodbye`); await showEncounterText(`${namespace}:pokemon_trade_goodbye`);
tradedPokeball.setAlpha(0); tradedPokeball.setAlpha(0);
tradedPokeball.setVisible(true); tradedPokeball.setVisible(true);
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokeball, targets: tradedPokeball,
alpha: 1, alpha: 1,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: 250, duration: 250,
onComplete: () => { onComplete: () => {
tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`); tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`);
scene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_open`)); globalScene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_open`));
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
tradedPokemonTintSprite.setVisible(true); tradedPokemonTintSprite.setVisible(true);
// TODO: need to add particles to fieldUI instead of field // TODO: need to add particles to fieldUI instead of field
// addPokeballOpenParticles(scene, tradedPokemon.x, tradedPokemon.y, tradedPokemon.pokeball); // addPokeballOpenParticles(tradedPokemon.x, tradedPokemon.y, tradedPokemon.pokeball);
scene.tweens.add({ globalScene.tweens.add({
targets: [ tradedPokemonTintSprite, tradedPokemonSprite ], targets: [ tradedPokemonTintSprite, tradedPokemonSprite ],
duration: 500, duration: 500,
ease: "Sine.easeIn", ease: "Sine.easeIn",
@ -672,30 +677,30 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
tradedPokemonSprite.setVisible(false); tradedPokemonSprite.setVisible(false);
tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`); tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`);
tradedPokemonTintSprite.setVisible(false); tradedPokemonTintSprite.setVisible(false);
scene.playSound("se/pb_catch"); globalScene.playSound("se/pb_catch");
scene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}`)); globalScene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}`));
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokeball, targets: tradedPokeball,
y: "+=10", y: "+=10",
duration: 200, duration: 200,
delay: 250, delay: 250,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
onComplete: () => { onComplete: () => {
scene.playSound("se/pb_bounce_1"); globalScene.playSound("se/pb_bounce_1");
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokeball, targets: tradedPokeball,
y: "-=100", y: "-=100",
duration: 200, duration: 200,
delay: 1000, delay: 1000,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
onStart: () => { onStart: () => {
scene.playSound("se/pb_throw"); globalScene.playSound("se/pb_throw");
}, },
onComplete: async () => { onComplete: async () => {
await doPokemonTradeFlyBySequence(scene, tradedPokemonSprite, receivedPokemonSprite); await doPokemonTradeFlyBySequence(tradedPokemonSprite, receivedPokemonSprite);
await doTradeReceivedSequence(scene, receivedPokemon, receivedPokemonSprite, receivedPokemonTintSprite, receivedPokeball, receivedPbAtlasKey); await doTradeReceivedSequence(receivedPokemon, receivedPokemonSprite, receivedPokemonTintSprite, receivedPokeball, receivedPbAtlasKey);
resolve(); resolve();
} }
}); });
@ -710,9 +715,9 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
}); });
} }
function doPokemonTradeFlyBySequence(scene: BattleScene, tradedPokemonSprite: Phaser.GameObjects.Sprite, receivedPokemonSprite: Phaser.GameObjects.Sprite) { function doPokemonTradeFlyBySequence(tradedPokemonSprite: Phaser.GameObjects.Sprite, receivedPokemonSprite: Phaser.GameObjects.Sprite) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const tradeContainer = scene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container; const tradeContainer = globalScene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container;
const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image; const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image;
const flyByStaticBg = tradeContainer.getByName("Black Background") as Phaser.GameObjects.Rectangle; const flyByStaticBg = tradeContainer.getByName("Black Background") as Phaser.GameObjects.Rectangle;
flyByStaticBg.setVisible(true); flyByStaticBg.setVisible(true);
@ -733,47 +738,47 @@ function doPokemonTradeFlyBySequence(scene: BattleScene, tradedPokemonSprite: Ph
const BASE_ANIM_DURATION = 1000; const BASE_ANIM_DURATION = 1000;
// Fade out trade background // Fade out trade background
scene.tweens.add({ globalScene.tweens.add({
targets: tradeBaseBg, targets: tradeBaseBg,
alpha: 0, alpha: 0,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: FADE_DELAY, duration: FADE_DELAY,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: [ receivedPokemonSprite, tradedPokemonSprite ], targets: [ receivedPokemonSprite, tradedPokemonSprite ],
y: tradeBaseBg.displayWidth / 2 - 100, y: tradeBaseBg.displayWidth / 2 - 100,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION * 3, duration: BASE_ANIM_DURATION * 3,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokemonSprite, targets: receivedPokemonSprite,
x: tradeBaseBg.displayWidth / 4, x: tradeBaseBg.displayWidth / 4,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION / 2, duration: BASE_ANIM_DURATION / 2,
delay: ANIM_DELAY delay: ANIM_DELAY
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokemonSprite, targets: tradedPokemonSprite,
x: tradeBaseBg.displayWidth * 3 / 4, x: tradeBaseBg.displayWidth * 3 / 4,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION / 2, duration: BASE_ANIM_DURATION / 2,
delay: ANIM_DELAY, delay: ANIM_DELAY,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokemonSprite, targets: receivedPokemonSprite,
y: "+=200", y: "+=200",
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION * 2, duration: BASE_ANIM_DURATION * 2,
delay: ANIM_DELAY, delay: ANIM_DELAY,
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: tradedPokemonSprite, targets: tradedPokemonSprite,
y: "-=200", y: "-=200",
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION * 2, duration: BASE_ANIM_DURATION * 2,
delay: ANIM_DELAY, delay: ANIM_DELAY,
onComplete: () => { onComplete: () => {
scene.tweens.add({ globalScene.tweens.add({
targets: tradeBaseBg, targets: tradeBaseBg,
alpha: 1, alpha: 1,
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
@ -793,9 +798,9 @@ function doPokemonTradeFlyBySequence(scene: BattleScene, tradedPokemonSprite: Ph
}); });
} }
function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPokemon, receivedPokemonSprite: Phaser.GameObjects.Sprite, receivedPokemonTintSprite: Phaser.GameObjects.Sprite, receivedPokeballSprite: Phaser.GameObjects.Sprite, receivedPbAtlasKey: string) { function doTradeReceivedSequence(receivedPokemon: PlayerPokemon, receivedPokemonSprite: Phaser.GameObjects.Sprite, receivedPokemonTintSprite: Phaser.GameObjects.Sprite, receivedPokeballSprite: Phaser.GameObjects.Sprite, receivedPbAtlasKey: string) {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const tradeContainer = scene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container; const tradeContainer = globalScene.fieldUI.getByName("Trade Background") as Phaser.GameObjects.Container;
const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image; const tradeBaseBg = tradeContainer.getByName("Trade Background Image") as Phaser.GameObjects.Image;
receivedPokemonSprite.setVisible(false); receivedPokemonSprite.setVisible(false);
@ -812,7 +817,7 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
// Received pokemon sparkles // Received pokemon sparkles
let pokemonShinySparkle: Phaser.GameObjects.Sprite; let pokemonShinySparkle: Phaser.GameObjects.Sprite;
if (receivedPokemon.shiny) { if (receivedPokemon.shiny) {
pokemonShinySparkle = scene.add.sprite(receivedPokemonSprite.x, receivedPokemonSprite.y, "shiny"); pokemonShinySparkle = globalScene.add.sprite(receivedPokemonSprite.x, receivedPokemonSprite.y, "shiny");
pokemonShinySparkle.setVisible(false); pokemonShinySparkle.setVisible(false);
tradeContainer.add(pokemonShinySparkle); tradeContainer.add(pokemonShinySparkle);
} }
@ -820,19 +825,19 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
const BASE_ANIM_DURATION = 1000; const BASE_ANIM_DURATION = 1000;
// Pokeball falls to the screen // Pokeball falls to the screen
scene.playSound("se/pb_throw"); globalScene.playSound("se/pb_throw");
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokeballSprite, targets: receivedPokeballSprite,
y: "+=100", y: "+=100",
ease: "Cubic.easeInOut", ease: "Cubic.easeInOut",
duration: BASE_ANIM_DURATION, duration: BASE_ANIM_DURATION,
onComplete: () => { onComplete: () => {
scene.playSound("se/pb_bounce_1"); globalScene.playSound("se/pb_bounce_1");
scene.time.delayedCall(100, () => scene.playSound("se/pb_bounce_1")); globalScene.time.delayedCall(100, () => globalScene.playSound("se/pb_bounce_1"));
scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
scene.playSound("se/pb_rel"); globalScene.playSound("se/pb_rel");
scene.fadeOutBgm(500, false); globalScene.fadeOutBgm(500, false);
receivedPokemon.cry(); receivedPokemon.cry();
receivedPokemonTintSprite.scale = 0.25; receivedPokemonTintSprite.scale = 0.25;
receivedPokemonTintSprite.alpha = 1; receivedPokemonTintSprite.alpha = 1;
@ -841,14 +846,14 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
receivedPokemonTintSprite.alpha = 1; receivedPokemonTintSprite.alpha = 1;
receivedPokemonTintSprite.setVisible(true); receivedPokemonTintSprite.setVisible(true);
receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_opening`); receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_opening`);
scene.time.delayedCall(17, () => receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_open`)); globalScene.time.delayedCall(17, () => receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_open`));
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokemonSprite, targets: receivedPokemonSprite,
duration: 250, duration: 250,
ease: "Sine.easeOut", ease: "Sine.easeOut",
scale: 1 scale: 1
}); });
scene.tweens.add({ globalScene.tweens.add({
targets: receivedPokemonTintSprite, targets: receivedPokemonTintSprite,
duration: 250, duration: 250,
ease: "Sine.easeOut", ease: "Sine.easeOut",
@ -856,12 +861,12 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
alpha: 0, alpha: 0,
onComplete: () => { onComplete: () => {
if (receivedPokemon.shiny) { if (receivedPokemon.shiny) {
scene.time.delayedCall(500, () => { globalScene.time.delayedCall(500, () => {
doShinySparkleAnim(scene, pokemonShinySparkle, receivedPokemon.variant); doShinySparkleAnim(pokemonShinySparkle, receivedPokemon.variant);
}); });
} }
receivedPokeballSprite.destroy(); receivedPokeballSprite.destroy();
scene.time.delayedCall(2000, () => resolve()); globalScene.time.delayedCall(2000, () => resolve());
} }
}); });
}); });

View File

@ -2,8 +2,9 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Moves } from "#app/enums/moves"; import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species"; import { Species } from "#app/enums/species";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils"; import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-utils";
import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
@ -41,8 +42,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
}, },
]) ])
.withIntroDialogue([{ text: `${namespace}:intro` }]) .withIntroDialogue([{ text: `${namespace}:intro` }])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE)); encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE));
encounter.setDialogueToken("option1RequiredMove", new PokemonMove(OPTION_1_REQUIRED_MOVE).getName()); encounter.setDialogueToken("option1RequiredMove", new PokemonMove(OPTION_1_REQUIRED_MOVE).getName());
@ -70,7 +71,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene)) .withOptionPhase(async () => handlePokemonGuidingYouPhase())
.build() .build()
) )
.withOption( .withOption(
@ -89,7 +90,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
}, },
], ],
}) })
.withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene)) .withOptionPhase(async () => handlePokemonGuidingYouPhase())
.build() .build()
) )
.withSimpleOption( .withSimpleOption(
@ -103,16 +104,16 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
const allowedPokemon = scene.getPlayerParty().filter((p) => p.isAllowedInBattle()); const allowedPokemon = globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle());
for (const pkm of allowedPokemon) { for (const pkm of allowedPokemon) {
const percentage = DAMAGE_PERCENTAGE / 100; const percentage = DAMAGE_PERCENTAGE / 100;
const damage = Math.floor(pkm.getMaxHp() * percentage); const damage = Math.floor(pkm.getMaxHp() * percentage);
applyDamageToPokemon(scene, pkm, damage); applyDamageToPokemon(pkm, damage);
} }
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
return true; return true;
} }
@ -126,19 +127,17 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
/** /**
* Generic handler for using a guiding pokemon to guide you back. * Generic handler for using a guiding pokemon to guide you back.
*
* @param scene Battle scene
*/ */
function handlePokemonGuidingYouPhase(scene: BattleScene) { function handlePokemonGuidingYouPhase() {
const laprasSpecies = getPokemonSpecies(Species.LAPRAS); const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
const { mysteryEncounter } = scene.currentBattle; const { mysteryEncounter } = globalScene.currentBattle;
if (mysteryEncounter?.selectedOption?.primaryPokemon?.id) { if (mysteryEncounter?.selectedOption?.primaryPokemon?.id) {
setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true); setEncounterExp(mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true);
} else { } else {
console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?"); console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?");
} }
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
return true; return true;
} }

View File

@ -1,5 +1,6 @@
import type {
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { import {
EnemyPartyConfig,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
setEncounterRewards, setEncounterRewards,
} from "#app/data/mystery-encounters/utils/encounter-phase-utils"; } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
@ -13,9 +14,10 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
import { modifierTypes } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
@ -37,12 +39,12 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
text: `${namespace}:intro`, text: `${namespace}:intro`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculates what trainers are available for battle in the encounter // Calculates what trainers are available for battle in the encounter
// Normal difficulty trainer is randomly pulled from biome // Normal difficulty trainer is randomly pulled from biome
const normalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); const normalTrainerType = globalScene.arena.randomTrainerType(globalScene.currentBattle.waveIndex);
const normalConfig = trainerConfigs[normalTrainerType].clone(); const normalConfig = trainerConfigs[normalTrainerType].clone();
let female = false; let female = false;
if (normalConfig.hasGenders) { if (normalConfig.hasGenders) {
@ -57,16 +59,16 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
// Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config // Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config
// Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100 // Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100
let retries = 0; let retries = 0;
let hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); let hardTrainerType = globalScene.arena.randomTrainerType(globalScene.currentBattle.waveIndex);
while (retries < 5 && hardTrainerType === normalTrainerType) { while (retries < 5 && hardTrainerType === normalTrainerType) {
// Will try to use a different trainer from the normal trainer type // Will try to use a different trainer from the normal trainer type
hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex); hardTrainerType = globalScene.arena.randomTrainerType(globalScene.currentBattle.waveIndex);
retries++; retries++;
} }
const hardTemplate = new TrainerPartyCompoundTemplate( const hardTemplate = new TrainerPartyCompoundTemplate(
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true), new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true),
new TrainerPartyTemplate( new TrainerPartyTemplate(
Math.min(Math.ceil(scene.currentBattle.waveIndex / 20), 5), Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 20), 5),
PartyMemberStrength.AVERAGE, PartyMemberStrength.AVERAGE,
false, false,
true true
@ -87,8 +89,8 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
// Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome // Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome
// They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons // They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons
const brutalTrainerType = scene.arena.randomTrainerType( const brutalTrainerType = globalScene.arena.randomTrainerType(
scene.currentBattle.waveIndex, globalScene.currentBattle.waveIndex,
true true
); );
const e4Template = trainerPartyTemplates.ELITE_FOUR; const e4Template = trainerPartyTemplates.ELITE_FOUR;
@ -145,18 +147,18 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn standard trainer battle with memory mushroom reward // Spawn standard trainer battle with memory mushroom reward
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let initBattlePromise: Promise<void>; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
initBattlePromise = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(config);
}, scene.currentBattle.waveIndex * 10); }, globalScene.currentBattle.waveIndex * 10);
await initBattlePromise!; await initBattlePromise!;
} }
) )
@ -170,18 +172,18 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn hard fight // Spawn hard fight
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let initBattlePromise: Promise<void>; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
initBattlePromise = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(config);
}, scene.currentBattle.waveIndex * 100); }, globalScene.currentBattle.waveIndex * 100);
await initBattlePromise!; await initBattlePromise!;
} }
) )
@ -195,21 +197,21 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Spawn brutal fight // Spawn brutal fight
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2];
// To avoid player level snowballing from picking this option // To avoid player level snowballing from picking this option
encounter.expMultiplier = 0.9; encounter.expMultiplier = 0.9;
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true }); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT ], fillRemaining: true });
// Seed offsets to remove possibility of different trainers having exact same teams // Seed offsets to remove possibility of different trainers having exact same teams
let initBattlePromise: Promise<void>; let initBattlePromise: Promise<void>;
scene.executeWithSeedOffset(() => { globalScene.executeWithSeedOffset(() => {
initBattlePromise = initBattleWithEnemyConfig(scene, config); initBattlePromise = initBattleWithEnemyConfig(config);
}, scene.currentBattle.waveIndex * 1000); }, globalScene.currentBattle.waveIndex * 1000);
await initBattlePromise!; await initBattlePromise!;
} }
) )

View File

@ -1,8 +1,10 @@
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { getPokemonSpecies } from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
@ -66,8 +68,8 @@ export const MysteriousChestEncounter: MysteryEncounter =
.withTitle(`${namespace}:title`) .withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`) .withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`) .withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
// Calculate boss mon // Calculate boss mon
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
@ -106,9 +108,9 @@ export const MysteriousChestEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
// Play animation // Play animation
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const introVisuals = encounter.introVisuals!; const introVisuals = encounter.introVisuals!;
// Determine roll first // Determine roll first
@ -128,13 +130,13 @@ export const MysteriousChestEncounter: MysteryEncounter =
introVisuals.spriteConfigs[1].disableAnimation = false; introVisuals.spriteConfigs[1].disableAnimation = false;
introVisuals.playAnim(); introVisuals.playAnim();
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Open the chest // Open the chest
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const roll = encounter.misc.roll; const roll = encounter.misc.roll;
if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT) { if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT) {
// Choose between 2 COMMON / 2 GREAT tier items (20%) // Choose between 2 COMMON / 2 GREAT tier items (20%)
setEncounterRewards(scene, { setEncounterRewards({
guaranteedModifierTiers: [ guaranteedModifierTiers: [
ModifierTier.COMMON, ModifierTier.COMMON,
ModifierTier.COMMON, ModifierTier.COMMON,
@ -143,11 +145,11 @@ export const MysteriousChestEncounter: MysteryEncounter =
], ],
}); });
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}:option.1.normal`); queueEncounterMessage(`${namespace}:option.1.normal`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) {
// Choose between 3 ULTRA tier items (30%) // Choose between 3 ULTRA tier items (30%)
setEncounterRewards(scene, { setEncounterRewards({
guaranteedModifierTiers: [ guaranteedModifierTiers: [
ModifierTier.ULTRA, ModifierTier.ULTRA,
ModifierTier.ULTRA, ModifierTier.ULTRA,
@ -155,39 +157,39 @@ export const MysteriousChestEncounter: MysteryEncounter =
], ],
}); });
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}:option.1.good`); queueEncounterMessage(`${namespace}:option.1.good`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) {
// Choose between 2 ROGUE tier items (10%) // Choose between 2 ROGUE tier items (10%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE ]}); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE ]});
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}:option.1.great`); queueEncounterMessage(`${namespace}:option.1.great`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) {
// Choose 1 MASTER tier item (5%) // Choose 1 MASTER tier item (5%)
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.MASTER ]}); setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.MASTER ]});
// Display result message then proceed to rewards // Display result message then proceed to rewards
queueEncounterMessage(scene, `${namespace}:option.1.amazing`); queueEncounterMessage(`${namespace}:option.1.amazing`);
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
} else { } else {
// Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%) // Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%)
const highestLevelPokemon = getHighestLevelPlayerPokemon(scene, true, false); const highestLevelPokemon = getHighestLevelPlayerPokemon(true, false);
koPlayerPokemon(scene, highestLevelPokemon); koPlayerPokemon(highestLevelPokemon);
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender()); encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
await showEncounterText(scene, `${namespace}:option.1.bad`); await showEncounterText(`${namespace}:option.1.bad`);
// Handle game over edge case // Handle game over edge case
const allowedPokemon = scene.getPokemonAllowedInBattle(); const allowedPokemon = globalScene.getPokemonAllowedInBattle();
if (allowedPokemon.length === 0) { if (allowedPokemon.length === 0) {
// If there are no longer any legal pokemon in the party, game over. // If there are no longer any legal pokemon in the party, game over.
scene.clearPhaseQueue(); globalScene.clearPhaseQueue();
scene.unshiftPhase(new GameOverPhase(scene)); globalScene.unshiftPhase(new GameOverPhase());
} else { } else {
// Show which Pokemon was KOed, then start battle against Gimmighoul // Show which Pokemon was KOed, then start battle against Gimmighoul
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500); await transitionMysteryEncounterIntroVisuals(true, true, 500);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
} }
} }
}) })
@ -203,9 +205,9 @@ export const MysteriousChestEncounter: MysteryEncounter =
}, },
], ],
}, },
async (scene: BattleScene) => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true); leaveEncounterWithoutBattle(true);
return true; return true;
} }
) )

View File

@ -1,8 +1,9 @@
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -10,7 +11,8 @@ import { Stat } from "#enums/stat";
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import i18next from "i18next"; import i18next from "i18next";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon"; import type { PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
@ -52,20 +54,20 @@ export const PartTimerEncounter: MysteryEncounter =
text: `${namespace}:intro_dialogue`, text: `${namespace}:intro_dialogue`,
}, },
]) ])
.withOnInit((scene: BattleScene) => { .withOnInit(() => {
// Load sfx // Load sfx
scene.loadSe("PRSFX- Horn Drill1", "battle_anims", "PRSFX- Horn Drill1.wav"); globalScene.loadSe("PRSFX- Horn Drill1", "battle_anims", "PRSFX- Horn Drill1.wav");
scene.loadSe("PRSFX- Horn Drill3", "battle_anims", "PRSFX- Horn Drill3.wav"); globalScene.loadSe("PRSFX- Horn Drill3", "battle_anims", "PRSFX- Horn Drill3.wav");
scene.loadSe("PRSFX- Guillotine2", "battle_anims", "PRSFX- Guillotine2.wav"); globalScene.loadSe("PRSFX- Guillotine2", "battle_anims", "PRSFX- Guillotine2.wav");
scene.loadSe("PRSFX- Heavy Slam2", "battle_anims", "PRSFX- Heavy Slam2.wav"); globalScene.loadSe("PRSFX- Heavy Slam2", "battle_anims", "PRSFX- Heavy Slam2.wav");
scene.loadSe("PRSFX- Agility", "battle_anims", "PRSFX- Agility.wav"); globalScene.loadSe("PRSFX- Agility", "battle_anims", "PRSFX- Agility.wav");
scene.loadSe("PRSFX- Extremespeed1", "battle_anims", "PRSFX- Extremespeed1.wav"); globalScene.loadSe("PRSFX- Extremespeed1", "battle_anims", "PRSFX- Extremespeed1.wav");
scene.loadSe("PRSFX- Accelerock1", "battle_anims", "PRSFX- Accelerock1.wav"); globalScene.loadSe("PRSFX- Accelerock1", "battle_anims", "PRSFX- Accelerock1.wav");
scene.loadSe("PRSFX- Captivate", "battle_anims", "PRSFX- Captivate.wav"); globalScene.loadSe("PRSFX- Captivate", "battle_anims", "PRSFX- Captivate.wav");
scene.loadSe("PRSFX- Attract2", "battle_anims", "PRSFX- Attract2.wav"); globalScene.loadSe("PRSFX- Attract2", "battle_anims", "PRSFX- Attract2.wav");
scene.loadSe("PRSFX- Aurora Veil2", "battle_anims", "PRSFX- Aurora Veil2.wav"); globalScene.loadSe("PRSFX- Aurora Veil2", "battle_anims", "PRSFX- Aurora Veil2.wav");
return true; return true;
}) })
@ -84,8 +86,8 @@ export const PartTimerEncounter: MysteryEncounter =
} }
] ]
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
@ -109,41 +111,41 @@ export const PartTimerEncounter: MysteryEncounter =
} }
}); });
setEncounterExp(scene, pokemon.id, 100); setEncounterExp(pokemon.id, 100);
// Hide intro visuals // Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false); transitionMysteryEncounterIntroVisuals(true, false);
// Play sfx for "working" // Play sfx for "working"
doDeliverySfx(scene); doDeliverySfx();
}; };
// Only Pokemon non-KOd pokemon can be selected // Only Pokemon non-KOd pokemon can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick Deliveries // Pick Deliveries
// Bring visuals back in // Bring visuals back in
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(false, false);
const moneyMultiplier = scene.currentBattle.mysteryEncounter!.misc.moneyMultiplier; const moneyMultiplier = globalScene.currentBattle.mysteryEncounter!.misc.moneyMultiplier;
// Give money and do dialogue // Give money and do dialogue
if (moneyMultiplier > 2.5) { if (moneyMultiplier > 2.5) {
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
} else { } else {
await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_bad`, `${namespace}:speaker`);
} }
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier); const moneyChange = globalScene.getWaveMoneyAmount(moneyMultiplier);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}:pokemon_tired`); await showEncounterText(`${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -158,8 +160,8 @@ export const PartTimerEncounter: MysteryEncounter =
} }
] ]
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
@ -186,41 +188,41 @@ export const PartTimerEncounter: MysteryEncounter =
} }
}); });
setEncounterExp(scene, pokemon.id, 100); setEncounterExp(pokemon.id, 100);
// Hide intro visuals // Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false); transitionMysteryEncounterIntroVisuals(true, false);
// Play sfx for "working" // Play sfx for "working"
doStrongWorkSfx(scene); doStrongWorkSfx();
}; };
// Only Pokemon non-KOd pokemon can be selected // Only Pokemon non-KOd pokemon can be selected
const selectableFilter = (pokemon: Pokemon) => { const selectableFilter = (pokemon: Pokemon) => {
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}:invalid_selection`); return isPokemonValidForEncounterOptionSelection(pokemon, `${namespace}:invalid_selection`);
}; };
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter); return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Pick Move Warehouse items // Pick Move Warehouse items
// Bring visuals back in // Bring visuals back in
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(false, false);
const moneyMultiplier = scene.currentBattle.mysteryEncounter!.misc.moneyMultiplier; const moneyMultiplier = globalScene.currentBattle.mysteryEncounter!.misc.moneyMultiplier;
// Give money and do dialogue // Give money and do dialogue
if (moneyMultiplier > 2.5) { if (moneyMultiplier > 2.5) {
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
} else { } else {
await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_bad`, `${namespace}:speaker`);
} }
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier); const moneyChange = globalScene.getWaveMoneyAmount(moneyMultiplier);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}:pokemon_tired`); await showEncounterText(`${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -238,8 +240,8 @@ export const PartTimerEncounter: MysteryEncounter =
}, },
], ],
}) })
.withPreOptionPhase(async (scene: BattleScene) => { .withPreOptionPhase(async () => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const selectedPokemon = encounter.selectedOption?.primaryPokemon!; const selectedPokemon = encounter.selectedOption?.primaryPokemon!;
encounter.setDialogueToken("selectedPokemon", selectedPokemon.getNameToRender()); encounter.setDialogueToken("selectedPokemon", selectedPokemon.getNameToRender());
@ -251,28 +253,28 @@ export const PartTimerEncounter: MysteryEncounter =
} }
}); });
setEncounterExp(scene, selectedPokemon.id, 100); setEncounterExp(selectedPokemon.id, 100);
// Hide intro visuals // Hide intro visuals
transitionMysteryEncounterIntroVisuals(scene, true, false); transitionMysteryEncounterIntroVisuals(true, false);
// Play sfx for "working" // Play sfx for "working"
doSalesSfx(scene); doSalesSfx();
return true; return true;
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async () => {
// Assist with Sales // Assist with Sales
// Bring visuals back in // Bring visuals back in
await transitionMysteryEncounterIntroVisuals(scene, false, false); await transitionMysteryEncounterIntroVisuals(false, false);
// Give money and do dialogue // Give money and do dialogue
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
const moneyChange = scene.getWaveMoneyAmount(2.5); const moneyChange = globalScene.getWaveMoneyAmount(2.5);
updatePlayerMoney(scene, moneyChange, true, false); updatePlayerMoney(moneyChange, true, false);
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange })); await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
await showEncounterText(scene, `${namespace}:pokemon_tired`); await showEncounterText(`${namespace}:pokemon_tired`);
setEncounterRewards(scene, { fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
leaveEncounterWithoutBattle(scene); leaveEncounterWithoutBattle();
}) })
.build() .build()
) )
@ -284,51 +286,51 @@ export const PartTimerEncounter: MysteryEncounter =
]) ])
.build(); .build();
function doStrongWorkSfx(scene: BattleScene) { function doStrongWorkSfx() {
scene.playSound("battle_anims/PRSFX- Horn Drill1"); globalScene.playSound("battle_anims/PRSFX- Horn Drill1");
scene.playSound("battle_anims/PRSFX- Horn Drill1"); globalScene.playSound("battle_anims/PRSFX- Horn Drill1");
scene.time.delayedCall(1000, () => { globalScene.time.delayedCall(1000, () => {
scene.playSound("battle_anims/PRSFX- Guillotine2"); globalScene.playSound("battle_anims/PRSFX- Guillotine2");
}); });
scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
scene.playSound("battle_anims/PRSFX- Heavy Slam2"); globalScene.playSound("battle_anims/PRSFX- Heavy Slam2");
}); });
scene.time.delayedCall(2500, () => { globalScene.time.delayedCall(2500, () => {
scene.playSound("battle_anims/PRSFX- Guillotine2"); globalScene.playSound("battle_anims/PRSFX- Guillotine2");
}); });
} }
function doDeliverySfx(scene: BattleScene) { function doDeliverySfx() {
scene.playSound("battle_anims/PRSFX- Accelerock1"); globalScene.playSound("battle_anims/PRSFX- Accelerock1");
scene.time.delayedCall(1500, () => { globalScene.time.delayedCall(1500, () => {
scene.playSound("battle_anims/PRSFX- Extremespeed1"); globalScene.playSound("battle_anims/PRSFX- Extremespeed1");
}); });
scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
scene.playSound("battle_anims/PRSFX- Extremespeed1"); globalScene.playSound("battle_anims/PRSFX- Extremespeed1");
}); });
scene.time.delayedCall(2250, () => { globalScene.time.delayedCall(2250, () => {
scene.playSound("battle_anims/PRSFX- Agility"); globalScene.playSound("battle_anims/PRSFX- Agility");
}); });
} }
function doSalesSfx(scene: BattleScene) { function doSalesSfx() {
scene.playSound("battle_anims/PRSFX- Captivate"); globalScene.playSound("battle_anims/PRSFX- Captivate");
scene.time.delayedCall(1500, () => { globalScene.time.delayedCall(1500, () => {
scene.playSound("battle_anims/PRSFX- Attract2"); globalScene.playSound("battle_anims/PRSFX- Attract2");
}); });
scene.time.delayedCall(2000, () => { globalScene.time.delayedCall(2000, () => {
scene.playSound("battle_anims/PRSFX- Aurora Veil2"); globalScene.playSound("battle_anims/PRSFX- Aurora Veil2");
}); });
scene.time.delayedCall(3000, () => { globalScene.time.delayedCall(3000, () => {
scene.playSound("battle_anims/PRSFX- Attract2"); globalScene.playSound("battle_anims/PRSFX- Attract2");
}); });
} }

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