Merge branch 'beta' into hebrew-pr
@ -46,6 +46,7 @@ export default [
|
||||
"computed-property-spacing": ["error", "never" ], // Enforces consistent spacing inside computed property brackets
|
||||
"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
|
||||
"@typescript-eslint/consistent-type-imports": "error", // Enforces type-only imports wherever possible
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
BIN
public/audio/se/crit_throw.wav
Normal file
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 712 B After Width: | Height: | Size: 748 B |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 713 B After Width: | Height: | Size: 748 B |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 34 KiB |
@ -4,6 +4,8 @@
|
||||
"ffee52": "37d6de",
|
||||
"debd29": "078a8f",
|
||||
"833100": "002112",
|
||||
"830009": "23033b",
|
||||
"189d87": "c2247b",
|
||||
"ff7b73": "712f8f",
|
||||
"de4141": "3f1375",
|
||||
"ffbdbd": "a266b0",
|
||||
@ -11,6 +13,7 @@
|
||||
"107b6a": "9e1976",
|
||||
"105241": "4f2800",
|
||||
"83de7b": "a37707",
|
||||
"2e5529": "38001c",
|
||||
"5a9c39": "705207",
|
||||
"20b49c": "de3592",
|
||||
"fdfdfd": "fdfdfd",
|
||||
@ -21,14 +24,17 @@
|
||||
"ffee52": "f75ea8",
|
||||
"debd29": "a30a66",
|
||||
"833100": "0b2e01",
|
||||
"830009": "154205",
|
||||
"189d87": "f17f05",
|
||||
"ff7b73": "9db042",
|
||||
"de4141": "3c8227",
|
||||
"ffbdbd": "e7e385",
|
||||
"101010": "101010",
|
||||
"107b6a": "d44300",
|
||||
"105241": "030129",
|
||||
"83de7b": "433d99",
|
||||
"5a9c39": "19164f",
|
||||
"105241": "381601",
|
||||
"83de7b": "80ced9",
|
||||
"2e5519": "011c38",
|
||||
"5a9c39": "446b94",
|
||||
"20b49c": "fa8405",
|
||||
"fdfdfd": "fdfdfd",
|
||||
"5ad5c5": "faa405"
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@ -1,6 +1,7 @@
|
||||
{
|
||||
"1": {
|
||||
"843100": "033b22",
|
||||
"830009": "23033b",
|
||||
"ff7b73": "712f8f",
|
||||
"ffbdbd": "a266b0",
|
||||
"debd29": "078a8f",
|
||||
@ -13,11 +14,13 @@
|
||||
"5a9c3a": "b34952",
|
||||
"84de7b": "ff745e",
|
||||
"5ad6c5": "f062a4",
|
||||
"2e5519": "38001c",
|
||||
"21b59c": "de3592",
|
||||
"ffffff": "ffffff"
|
||||
},
|
||||
"2": {
|
||||
"843100": "420514",
|
||||
"830009": "154205",
|
||||
"ff7b73": "9db042",
|
||||
"ffbdbd": "e7e385",
|
||||
"debd29": "a30a66",
|
||||
@ -30,6 +33,7 @@
|
||||
"5a9c3a": "446b94",
|
||||
"84de7b": "80ced9",
|
||||
"5ad6c5": "faa405",
|
||||
"2e5519": "011c38",
|
||||
"21b59c": "fa8405",
|
||||
"ffffff": "ffffff"
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"3f4447": "466698",
|
||||
"de3431": "3fca9f",
|
||||
"f8f8f8": "a1e9f0",
|
||||
"f4f4f4": "d7eff4",
|
||||
"7b282e": "0e3e81",
|
||||
"6b1d1d": "206d74",
|
||||
"4ebdd9": "41a7b0",
|
||||
@ -11,7 +12,7 @@
|
||||
"bfbfbf": "8cc7d4",
|
||||
"ffb2bf": "b7e9ff",
|
||||
"bf4c60": "4386df",
|
||||
"fff0a6": "271f4c",
|
||||
"fff0a6": "208698",
|
||||
"3e7acc": "6b4592",
|
||||
"18335c": "170738",
|
||||
"f2798d": "8dcfff",
|
||||
@ -25,6 +26,7 @@
|
||||
"3f4447": "466698",
|
||||
"de3431": "9ceec6",
|
||||
"f8f8f8": "89d2b8",
|
||||
"f4f4f4": "d7eff4",
|
||||
"7b282e": "152a5c",
|
||||
"6b1d1d": "356e8d",
|
||||
"4ebdd9": "2f6e74",
|
||||
|
||||
@ -835,7 +835,7 @@
|
||||
"6713": [0, 1, 1],
|
||||
"8901": [1, 1, 1],
|
||||
"female": {
|
||||
"3": [0, 2, 1],
|
||||
"3": [0, 1, 1],
|
||||
"19": [0, 1, 1],
|
||||
"20": [0, 1, 1],
|
||||
"25": [0, 1, 1],
|
||||
@ -869,6 +869,7 @@
|
||||
"198": [0, 1, 1],
|
||||
"203": [0, 1, 1],
|
||||
"207": [0, 1, 1],
|
||||
"212": [1, 1, 1],
|
||||
"215": [0, 1, 1],
|
||||
"217": [1, 1, 1],
|
||||
"229": [0, 1, 1],
|
||||
@ -1778,6 +1779,7 @@
|
||||
"198": [0, 1, 1],
|
||||
"203": [0, 1, 1],
|
||||
"207": [0, 1, 1],
|
||||
"212": [1, 1, 1],
|
||||
"215": [0, 1, 1],
|
||||
"217": [1, 1, 1],
|
||||
"229": [0, 1, 1],
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"1": {
|
||||
"833100": "180136",
|
||||
"830009": "23033b",
|
||||
"bd6a31": "012729",
|
||||
"ffee52": "37d6de",
|
||||
"debd29": "078a8f",
|
||||
@ -8,15 +9,18 @@
|
||||
"de4141": "3f1375",
|
||||
"ff7b73": "712f8f",
|
||||
"ffbdbd": "a266b0",
|
||||
"5a9c39": "705207",
|
||||
"105241": "4f2800",
|
||||
"83de7b": "a37707",
|
||||
"e8a3a3": "91579e",
|
||||
"5a9c39": "b34952",
|
||||
"105241": "190038",
|
||||
"2e5519": "38001c",
|
||||
"83de7b": "ff745e",
|
||||
"107b6a": "b80479",
|
||||
"20b49c": "de3592",
|
||||
"fdfdfd": "fdfdfd"
|
||||
},
|
||||
"2": {
|
||||
"833100": "0b2e01",
|
||||
"830009": "154205",
|
||||
"bd6a31": "420514",
|
||||
"ffee52": "f75ea8",
|
||||
"debd29": "a30a66",
|
||||
@ -24,9 +28,11 @@
|
||||
"de4141": "3c8227",
|
||||
"ff7b73": "9db042",
|
||||
"ffbdbd": "e7e385",
|
||||
"5a9c39": "19164f",
|
||||
"105241": "030129",
|
||||
"83de7b": "433d99",
|
||||
"e8a3a3": "ced76f",
|
||||
"5a9c39": "446b94",
|
||||
"105241": "381601",
|
||||
"2e5519": "011c38",
|
||||
"83de7b": "80ced9",
|
||||
"107b6a": "d15d04",
|
||||
"20b49c": "fa8405",
|
||||
"fdfdfd": "fdfdfd"
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -1,6 +1,7 @@
|
||||
{
|
||||
"1": {
|
||||
"843100": "033b22",
|
||||
"830009": "23033b",
|
||||
"ffbdbd": "a266b0",
|
||||
"ff7b73": "712f8f",
|
||||
"debd29": "078a8f",
|
||||
@ -11,6 +12,7 @@
|
||||
"105242": "190038",
|
||||
"107b6b": "c21f7e",
|
||||
"5a9c3a": "b34952",
|
||||
"2e5519": "38001c",
|
||||
"5ad6c5": "f062a4",
|
||||
"21b59c": "de3592",
|
||||
"84de7b": "ff745e",
|
||||
@ -18,6 +20,7 @@
|
||||
},
|
||||
"2": {
|
||||
"843100": "420514",
|
||||
"830009": "154205",
|
||||
"ffbdbd": "e7e385",
|
||||
"ff7b73": "9db042",
|
||||
"debd29": "a30a66",
|
||||
@ -25,7 +28,8 @@
|
||||
"de4242": "3c8227",
|
||||
"101010": "101010",
|
||||
"ffef52": "f75ea8",
|
||||
"105242": "001a33",
|
||||
"105242": "381601",
|
||||
"2e5519": "011c38",
|
||||
"107b6b": "d15d04",
|
||||
"5a9c3a": "446b94",
|
||||
"5ad6c5": "faa405",
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
"de62a4": "ffc668",
|
||||
"4a83a4": "387fa7",
|
||||
"314a62": "244260",
|
||||
"70bbb4": "f8d371",
|
||||
"548e88": "2d60bb",
|
||||
"a4295a": "cc762f"
|
||||
},
|
||||
"1": {
|
||||
@ -22,7 +22,7 @@
|
||||
"de62a4": "ffdf90",
|
||||
"4a83a4": "a1c8db",
|
||||
"314a62": "7396b4",
|
||||
"70bbb4": "70bbb4",
|
||||
"548e88": "a9c0c6",
|
||||
"a4295a": "e28c27"
|
||||
},
|
||||
"2": {
|
||||
@ -35,7 +35,7 @@
|
||||
"de62a4": "e25038",
|
||||
"4a83a4": "e6aa47",
|
||||
"314a62": "b56f2a",
|
||||
"70bbb4": "f8d371",
|
||||
"548e88": "e0b544",
|
||||
"a4295a": "a62a21"
|
||||
}
|
||||
}
|
||||
41
public/images/pokemon/variant/back/female/212.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"1": {
|
||||
"843100": "033b22",
|
||||
"830009": "23033b",
|
||||
"ffbdbd": "a266b0",
|
||||
"ffef52": "37d6de",
|
||||
"debd29": "078a8f",
|
||||
@ -10,6 +11,7 @@
|
||||
"101010": "101010",
|
||||
"105242": "190038",
|
||||
"107b6b": "9e1976",
|
||||
"2e5519": "38001c",
|
||||
"5a9c3a": "b34952",
|
||||
"5ad6c5": "f062a4",
|
||||
"21b59c": "de3592",
|
||||
@ -18,6 +20,7 @@
|
||||
},
|
||||
"2": {
|
||||
"843100": "420514",
|
||||
"830009": "154205",
|
||||
"ffbdbd": "e7e385",
|
||||
"ffef52": "f75ea8",
|
||||
"debd29": "a30a66",
|
||||
@ -27,6 +30,7 @@
|
||||
"101010": "101010",
|
||||
"105242": "381601",
|
||||
"107b6b": "d15d04",
|
||||
"2e5519": "011c38",
|
||||
"5a9c3a": "446b94",
|
||||
"5ad6c5": "faa405",
|
||||
"21b59c": "fa8405",
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"3f4447": "466698",
|
||||
"de3431": "3fca9f",
|
||||
"f8f8f8": "a1e9f0",
|
||||
"f4f4f4": "d7effa",
|
||||
"7b282e": "0e3e81",
|
||||
"6b1d1d": "206d74",
|
||||
"4ebdd9": "41a7b0",
|
||||
@ -25,6 +26,7 @@
|
||||
"3f4447": "466698",
|
||||
"de3431": "9ceec6",
|
||||
"f8f8f8": "89d2b8",
|
||||
"f4f4f4": "d7effa",
|
||||
"7b282e": "152a5c",
|
||||
"6b1d1d": "356e8d",
|
||||
"4ebdd9": "2f6e74",
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"0f0f0f": "0f0f0f",
|
||||
"314a62": "244260",
|
||||
"621841": "71370f",
|
||||
"70bbb4": "f8d371",
|
||||
"548e88": "2d60bb",
|
||||
"de62a4": "ffc668",
|
||||
"a4295a": "cc762f"
|
||||
},
|
||||
@ -21,7 +21,7 @@
|
||||
"0f0f0f": "0f0f0f",
|
||||
"314a62": "7396b4",
|
||||
"621841": "7b3c08",
|
||||
"70bbb4": "70bbb4",
|
||||
"548e88": "a9c0c6",
|
||||
"de62a4": "ffdf90",
|
||||
"a4295a": "e28c27"
|
||||
},
|
||||
@ -34,7 +34,7 @@
|
||||
"0f0f0f": "0f0f0f",
|
||||
"314a62": "b56f2a",
|
||||
"621841": "5a0a05",
|
||||
"70bbb4": "f8d371",
|
||||
"548e88": "e0b544",
|
||||
"de62a4": "e25038",
|
||||
"a4295a": "a62a21"
|
||||
}
|
||||
|
||||
41
public/images/pokemon/variant/female/212.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@ -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": {
|
||||
"843100": "420514",
|
||||
"ff7b73": "9db042",
|
||||
"830009": "154205",
|
||||
"ffbdbd": "e7e385",
|
||||
"ffef52": "f75ea8",
|
||||
"debd29": "a30a66",
|
||||
"ff7b73": "9db042",
|
||||
"bd6b31": "852a41",
|
||||
"de4242": "3c8227",
|
||||
"101010": "101010",
|
||||
"105242": "381601",
|
||||
"107b6b": "d44300",
|
||||
"107b6b": "d15d04",
|
||||
"2e5519": "011c38",
|
||||
"5a9c3a": "446b94",
|
||||
"84de7b": "80ced9",
|
||||
"5ad6c5": "faa405",
|
||||
"21b59c": "fa8405",
|
||||
"ffffff": "ffffff"
|
||||
"84de7b": "80ced9",
|
||||
"ffffff": "ffffff",
|
||||
"2f561a": "011b34"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 985 B |
@ -1 +1 @@
|
||||
Subproject commit 2e03bc8f2736269bfa365faad587c3ec54a37621
|
||||
Subproject commit e07ab625f2080afe36b61fad291b0ec5eff4000c
|
||||
@ -1,3 +1 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
|
||||
export type ConditionFn = (scene: BattleScene, args?: any[]) => boolean;
|
||||
export type ConditionFn = (args?: any[]) => boolean;
|
||||
|
||||
2
src/@types/i18next.d.ts
vendored
@ -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.
|
||||
declare module "i18next" {
|
||||
|
||||
@ -1,13 +1,18 @@
|
||||
import Phaser from "phaser";
|
||||
import UI from "#app/ui/ui";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
||||
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
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 { 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 { 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 { Arena, ArenaBase } from "#app/field/arena";
|
||||
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 AbilityBar from "#app/ui/ability-bar";
|
||||
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr, PostItemLostAbAttr } from "#app/data/ability";
|
||||
import Battle, { BattleType, FixedBattleConfig } from "#app/battle";
|
||||
import { GameMode, GameModes, getGameMode } from "#app/game-mode";
|
||||
import type { FixedBattleConfig } from "#app/battle";
|
||||
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 SpritePipeline from "#app/pipelines/sprite";
|
||||
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 TrainerData from "#app/system/trainer-data";
|
||||
import type TrainerData from "#app/system/trainer-data";
|
||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import PokeballTray from "#app/ui/pokeball-tray";
|
||||
import InvertPostFX from "#app/pipelines/invert";
|
||||
import { Achv, achvs, ModifierAchv, MoneyAchv } from "#app/system/achv";
|
||||
import { Voucher, vouchers } from "#app/system/voucher";
|
||||
import type { Achv } from "#app/system/achv";
|
||||
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 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 PokemonData from "#app/system/pokemon-data";
|
||||
import type PokemonData from "#app/system/pokemon-data";
|
||||
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 { getTypeRgb } from "#app/data/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 { SceneBase } from "#app/scene-base";
|
||||
import CandyBar from "#app/ui/candy-bar";
|
||||
import { Variant, variantColorCache, variantData, VariantSet } from "#app/data/variant";
|
||||
import { Localizable } from "#app/interfaces/locales";
|
||||
import type { Variant, VariantSet } from "#app/data/variant";
|
||||
import { variantColorCache, variantData } from "#app/data/variant";
|
||||
import type { Localizable } from "#app/interfaces/locales";
|
||||
import Overrides from "#app/overrides";
|
||||
import { InputsController } from "#app/inputs-controller";
|
||||
import { UiInputs } from "#app/ui-inputs";
|
||||
@ -58,14 +70,14 @@ import { EaseType } from "#enums/ease-type";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
import { BattleStyle } from "#enums/battle-style";
|
||||
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 { Moves } from "#enums/moves";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { Species } from "#enums/species";
|
||||
import { UiTheme } from "#enums/ui-theme";
|
||||
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 { TrainerType } from "#enums/trainer-type";
|
||||
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 { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
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 { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
|
||||
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 { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { initGlobalScene } from "#app/global-scene";
|
||||
|
||||
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
||||
|
||||
@ -330,6 +343,7 @@ export default class BattleScene extends SceneBase {
|
||||
this.nextCommandPhaseQueue = [];
|
||||
this.eventManager = new TimedEventManager();
|
||||
this.updateGameInfo();
|
||||
initGlobalScene(this);
|
||||
}
|
||||
|
||||
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}
|
||||
*/
|
||||
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);
|
||||
if (useExpSprite) {
|
||||
fileRoot = `exp/${fileRoot}`;
|
||||
}
|
||||
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;
|
||||
if (variantSet && (variant !== undefined && variantSet[variant] === 1)) {
|
||||
const populateVariantColors = (key: string): Promise<void> => {
|
||||
return new Promise(resolve => {
|
||||
if (variantColorCache.hasOwnProperty(key)) {
|
||||
return resolve();
|
||||
}
|
||||
this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`).then(res => res.json()).then(c => {
|
||||
variantColorCache[key] = c;
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
if (variantSet && variant !== undefined && variantSet[variant] === 1) {
|
||||
if (variantColorCache.hasOwnProperty(spriteKey)) {
|
||||
return resolve();
|
||||
}
|
||||
this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`)
|
||||
.then((res) => res.json())
|
||||
.then((c) => {
|
||||
variantColorCache[spriteKey] = c;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
populateVariantColors(spriteKey);
|
||||
}
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async preload() {
|
||||
if (DEBUG_RNG) {
|
||||
const scene = this;
|
||||
const originalRealInRange = Phaser.Math.RND.realInRange;
|
||||
Phaser.Math.RND.realInRange = function (min: number, max: number): number {
|
||||
const ret = originalRealInRange.apply(this, [ min, max ]);
|
||||
const args = [ "RNG", ++scene.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ];
|
||||
args.push(`seed: ${scene.rngSeedOverride || scene.waveSeed || scene.seed}`);
|
||||
if (scene.rngOffset) {
|
||||
args.push(`offset: ${scene.rngOffset}`);
|
||||
const args = [ "RNG", ++this.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ];
|
||||
args.push(`seed: ${this.rngSeedOverride || this.waveSeed || this.seed}`);
|
||||
if (this.rngOffset) {
|
||||
args.push(`offset: ${this.rngOffset}`);
|
||||
}
|
||||
console.log(...args);
|
||||
return ret;
|
||||
@ -397,12 +412,12 @@ export default class BattleScene extends SceneBase {
|
||||
create() {
|
||||
this.scene.remove(LoadingScene.KEY);
|
||||
initGameSpeed.apply(this);
|
||||
this.inputController = new InputsController(this);
|
||||
this.uiInputs = new UiInputs(this, this.inputController);
|
||||
this.inputController = new InputsController();
|
||||
this.uiInputs = new UiInputs(this.inputController);
|
||||
|
||||
this.gameData = new GameData(this);
|
||||
this.gameData = new GameData();
|
||||
|
||||
addUiThemeOverrides(this);
|
||||
addUiThemeOverrides();
|
||||
|
||||
this.load.setBaseURL();
|
||||
|
||||
@ -489,76 +504,76 @@ export default class BattleScene extends SceneBase {
|
||||
this.modifiers = [];
|
||||
this.enemyModifiers = [];
|
||||
|
||||
this.modifierBar = new ModifierBar(this);
|
||||
this.modifierBar = new ModifierBar();
|
||||
this.modifierBar.setName("modifier-bar");
|
||||
this.add.existing(this.modifierBar);
|
||||
uiContainer.add(this.modifierBar);
|
||||
|
||||
this.enemyModifierBar = new ModifierBar(this, true);
|
||||
this.enemyModifierBar = new ModifierBar(true);
|
||||
this.enemyModifierBar.setName("enemy-modifier-bar");
|
||||
this.add.existing(this.enemyModifierBar);
|
||||
uiContainer.add(this.enemyModifierBar);
|
||||
|
||||
this.charSprite = new CharSprite(this);
|
||||
this.charSprite = new CharSprite();
|
||||
this.charSprite.setName("sprite-char");
|
||||
this.charSprite.setup();
|
||||
|
||||
this.fieldUI.add(this.charSprite);
|
||||
|
||||
this.pbTray = new PokeballTray(this, true);
|
||||
this.pbTray = new PokeballTray(true);
|
||||
this.pbTray.setName("pb-tray");
|
||||
this.pbTray.setup();
|
||||
|
||||
this.pbTrayEnemy = new PokeballTray(this, false);
|
||||
this.pbTrayEnemy = new PokeballTray(false);
|
||||
this.pbTrayEnemy.setName("enemy-pb-tray");
|
||||
this.pbTrayEnemy.setup();
|
||||
|
||||
this.fieldUI.add(this.pbTray);
|
||||
this.fieldUI.add(this.pbTrayEnemy);
|
||||
|
||||
this.abilityBar = new AbilityBar(this);
|
||||
this.abilityBar = new AbilityBar();
|
||||
this.abilityBar.setName("ability-bar");
|
||||
this.abilityBar.setup();
|
||||
this.fieldUI.add(this.abilityBar);
|
||||
|
||||
this.partyExpBar = new PartyExpBar(this);
|
||||
this.partyExpBar = new PartyExpBar();
|
||||
this.partyExpBar.setName("party-exp-bar");
|
||||
this.partyExpBar.setup();
|
||||
this.fieldUI.add(this.partyExpBar);
|
||||
|
||||
this.candyBar = new CandyBar(this);
|
||||
this.candyBar = new CandyBar();
|
||||
this.candyBar.setName("candy-bar");
|
||||
this.candyBar.setup();
|
||||
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.setOrigin(1, 0.5);
|
||||
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.setOrigin(1, 0.5);
|
||||
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.setOrigin(1, 0.5);
|
||||
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.setOrigin(1, 0.5);
|
||||
this.luckText.setVisible(false);
|
||||
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.setOrigin(1, 0.5);
|
||||
this.luckLabelText.setVisible(false);
|
||||
this.fieldUI.add(this.luckLabelText);
|
||||
|
||||
this.arenaFlyout = new ArenaFlyout(this);
|
||||
this.arenaFlyout = new ArenaFlyout();
|
||||
this.fieldUI.add(this.arenaFlyout);
|
||||
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.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.fieldUI.add(this.pokemonInfoContainer);
|
||||
@ -578,13 +593,13 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
const loadPokemonAssets = [];
|
||||
|
||||
this.arenaPlayer = new ArenaBase(this, true);
|
||||
this.arenaPlayer = new ArenaBase(true);
|
||||
this.arenaPlayer.setName("arena-player");
|
||||
this.arenaPlayerTransition = new ArenaBase(this, true);
|
||||
this.arenaPlayerTransition = new ArenaBase(true);
|
||||
this.arenaPlayerTransition.setName("arena-player-transition");
|
||||
this.arenaEnemy = new ArenaBase(this, false);
|
||||
this.arenaEnemy = new ArenaBase(false);
|
||||
this.arenaEnemy.setName("arena-enemy");
|
||||
this.arenaNextEnemy = new ArenaBase(this, false);
|
||||
this.arenaNextEnemy = new ArenaBase(false);
|
||||
this.arenaNextEnemy.setName("arena-next-enemy");
|
||||
|
||||
this.arenaBgTransition.setVisible(false);
|
||||
@ -625,7 +640,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
this.reset(false, false, true);
|
||||
|
||||
const ui = new UI(this);
|
||||
const ui = new UI();
|
||||
this.uiContainer.add(ui);
|
||||
|
||||
this.ui = ui;
|
||||
@ -636,12 +651,12 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
Promise.all([
|
||||
Promise.all(loadPokemonAssets),
|
||||
initCommonAnims(this).then(() => loadCommonAnimAssets(this, true)),
|
||||
Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(this, m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)),
|
||||
initCommonAnims().then(() => loadCommonAnimAssets(true)),
|
||||
Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(defaultMoves, true)),
|
||||
this.initStarterColors()
|
||||
]).then(() => {
|
||||
this.pushPhase(new LoginPhase(this));
|
||||
this.pushPhase(new TitlePhase(this));
|
||||
this.pushPhase(new LoginPhase());
|
||||
this.pushPhase(new TitlePhase());
|
||||
|
||||
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 {
|
||||
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) {
|
||||
postProcess(pokemon);
|
||||
}
|
||||
@ -929,7 +944,7 @@ export default class BattleScene extends SceneBase {
|
||||
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) {
|
||||
pokemon.generateFusionSpecies();
|
||||
}
|
||||
@ -1064,7 +1079,7 @@ export default class BattleScene extends SceneBase {
|
||||
/**
|
||||
* 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`
|
||||
*
|
||||
* @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)
|
||||
*/
|
||||
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 {
|
||||
if (clearData) {
|
||||
this.gameData = new GameData(this);
|
||||
this.gameData = new GameData();
|
||||
}
|
||||
|
||||
this.gameMode = getGameMode(GameModes.CLASSIC);
|
||||
@ -1178,6 +1193,9 @@ export default class BattleScene extends SceneBase {
|
||||
onComplete: () => {
|
||||
this.clearPhaseQueue();
|
||||
|
||||
this.ui.freeUIData();
|
||||
this.uiContainer.remove(this.ui, true);
|
||||
this.uiContainer.destroy();
|
||||
this.children.removeAll(true);
|
||||
this.game.domContainer.innerHTML = "";
|
||||
this.launchBattle();
|
||||
@ -1210,7 +1228,7 @@ export default class BattleScene extends SceneBase {
|
||||
battleConfig = this.gameMode.getFixedBattle(newWaveIndex);
|
||||
newDouble = battleConfig.double;
|
||||
newBattleType = battleConfig.battleType;
|
||||
this.executeWithSeedOffset(() => newTrainer = battleConfig?.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8);
|
||||
this.executeWithSeedOffset(() => newTrainer = battleConfig?.getTrainer(), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8);
|
||||
if (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);
|
||||
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant);
|
||||
newTrainer = trainerData !== undefined ? trainerData.toTrainer() : new Trainer(trainerType, variant);
|
||||
this.field.add(newTrainer);
|
||||
}
|
||||
|
||||
@ -1314,7 +1332,7 @@ export default class BattleScene extends SceneBase {
|
||||
this.executeWithSeedOffset(() => {
|
||||
this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble);
|
||||
}, newWaveIndex << 3, this.waveSeed);
|
||||
this.currentBattle.incrementTurn(this);
|
||||
this.currentBattle.incrementTurn();
|
||||
|
||||
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||
// 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) => {
|
||||
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) {
|
||||
this.pushPhase(new ShowTrainerPhase(this));
|
||||
this.pushPhase(new ShowTrainerPhase());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1361,14 +1379,14 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
if (!this.gameMode.hasRandomBiomes && !isNewBiome) {
|
||||
this.pushPhase(new NextEncounterPhase(this));
|
||||
this.pushPhase(new NextEncounterPhase());
|
||||
} else {
|
||||
this.pushPhase(new SelectBiomePhase(this));
|
||||
this.pushPhase(new NewBiomeEncounterPhase(this));
|
||||
this.pushPhase(new SelectBiomePhase());
|
||||
this.pushPhase(new NewBiomeEncounterPhase());
|
||||
|
||||
const newMaxExpLevel = this.getMaxExpLevel();
|
||||
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 {
|
||||
this.arena = new Arena(this, biome, Biome[biome].toLowerCase());
|
||||
this.arena = new Arena(biome, Biome[biome].toLowerCase());
|
||||
this.eventTarget.dispatchEvent(new NewArenaEvent());
|
||||
|
||||
this.arenaBg.pipelineData = { terrainColorRatio: this.arena.getBgTerrainColorRatioForBiome() };
|
||||
@ -1424,6 +1442,8 @@ export default class BattleScene extends SceneBase {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const isEggPhase: boolean = [ "EggLapsePhase", "EggHatchPhase" ].includes(this.getCurrentPhase()?.constructor.name ?? "");
|
||||
|
||||
switch (species.speciesId) {
|
||||
case Species.UNOWN:
|
||||
case Species.SHELLOS:
|
||||
@ -1455,7 +1475,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
return Utils.randSeedInt(8);
|
||||
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 Utils.randSeedInt(2);
|
||||
@ -1483,7 +1503,7 @@ export default class BattleScene extends SceneBase {
|
||||
return 0;
|
||||
case Species.GIMMIGHOUL:
|
||||
// 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
|
||||
} else {
|
||||
return Utils.randSeedInt(species.forms.length);
|
||||
@ -1791,7 +1811,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
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;
|
||||
this.biomeWaveText.setY(
|
||||
-(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0) + (biomeWaveTextHeight / 2)
|
||||
@ -1850,7 +1870,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
generateRandomBiome(waveIndex: integer): Biome {
|
||||
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 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);
|
||||
@ -1863,9 +1883,9 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
const randInt = Utils.randSeedInt(totalWeight);
|
||||
|
||||
for (const biome of biomes) {
|
||||
if (randInt < biomeThresholds[biome]) {
|
||||
return biome;
|
||||
for (let i = 0; i < biomes.length; i++) {
|
||||
if (randInt < biomeThresholds[i]) {
|
||||
return biomes[i];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1878,7 +1898,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
playBgm(bgmName?: string, fadeOut?: boolean): void {
|
||||
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.isPlaying) {
|
||||
@ -2497,7 +2517,7 @@ export default class BattleScene extends SceneBase {
|
||||
* @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) {
|
||||
const phase = new MessagePhase(this, message, callbackDelay, prompt, promptDelay);
|
||||
const phase = new MessagePhase(message, callbackDelay, prompt, promptDelay);
|
||||
if (!defer) {
|
||||
// adds to the end of PhaseQueuePrepend
|
||||
this.unshiftPhase(phase);
|
||||
@ -2515,7 +2535,7 @@ export default class BattleScene extends SceneBase {
|
||||
this.phaseQueue.push(...this.nextCommandPhaseQueue);
|
||||
this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length);
|
||||
}
|
||||
this.phaseQueue.push(new TurnInitPhase(this));
|
||||
this.phaseQueue.push(new TurnInitPhase());
|
||||
}
|
||||
|
||||
addMoney(amount: integer): void {
|
||||
@ -2546,7 +2566,7 @@ export default class BattleScene extends SceneBase {
|
||||
if (modifier instanceof TerastallizeModifier) {
|
||||
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) {
|
||||
const pokemon = this.getPokemonById(modifier.pokemonId);
|
||||
if (pokemon) {
|
||||
@ -2627,7 +2647,7 @@ export default class BattleScene extends SceneBase {
|
||||
if (modifier instanceof TerastallizeModifier) {
|
||||
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) {
|
||||
const pokemon = this.getPokemonById(modifier.pokemonId);
|
||||
if (pokemon) {
|
||||
@ -2662,7 +2682,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> {
|
||||
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);
|
||||
Utils.executeIf(!!source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source! /* checked in condition*/, cancelled)).then(() => {
|
||||
if (cancelled.value) {
|
||||
@ -2670,11 +2690,11 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier;
|
||||
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;
|
||||
let removeOld = true;
|
||||
if (matchingModifier) {
|
||||
const maxStackCount = matchingModifier.getMaxStackCount(target.scene);
|
||||
const maxStackCount = matchingModifier.getMaxStackCount();
|
||||
if (matchingModifier.stackCount >= maxStackCount) {
|
||||
return resolve(false);
|
||||
}
|
||||
@ -2791,7 +2811,7 @@ export default class BattleScene extends SceneBase {
|
||||
count = Math.max(count, Math.floor(chances / 2));
|
||||
}
|
||||
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;
|
||||
});
|
||||
@ -2815,7 +2835,7 @@ export default class BattleScene extends SceneBase {
|
||||
* @param pokemon - If specified, only removes held items from that {@linkcode Pokemon}
|
||||
*/
|
||||
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) {
|
||||
this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1);
|
||||
}
|
||||
@ -2864,9 +2884,7 @@ export default class BattleScene extends SceneBase {
|
||||
updatePartyForModifiers(party: Pokemon[], instant?: boolean): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
Promise.allSettled(party.map(p => {
|
||||
if (p.scene) {
|
||||
p.calculateStats();
|
||||
}
|
||||
p.calculateStats();
|
||||
return p.updateInfo(instant);
|
||||
})).then(() => resolve());
|
||||
});
|
||||
@ -2929,15 +2947,14 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
/**
|
||||
* 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 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`
|
||||
* @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));
|
||||
scene.executeWithSeedOffset(() => {
|
||||
this.executeWithSeedOffset(() => {
|
||||
const shuffleModifiers = mods => {
|
||||
if (mods.length < 1) {
|
||||
return mods;
|
||||
@ -2946,7 +2963,7 @@ export default class BattleScene extends SceneBase {
|
||||
return [ mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand)) ];
|
||||
};
|
||||
modifiers = shuffleModifiers(modifiers);
|
||||
}, scene.currentBattle.turn << 4, scene.waveSeed);
|
||||
}, this.currentBattle.turn << 4, this.waveSeed);
|
||||
return this.applyModifiersInternal(modifiers, player, args);
|
||||
}
|
||||
|
||||
@ -3016,9 +3033,9 @@ export default class BattleScene extends SceneBase {
|
||||
if (matchingFormChange) {
|
||||
let phase: Phase;
|
||||
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) {
|
||||
phase = new FormChangePhase(this, pokemon, matchingFormChange, modal);
|
||||
phase = new FormChangePhase(pokemon, matchingFormChange, modal);
|
||||
} else {
|
||||
phase = new QuietFormChangePhase(this, pokemon, matchingFormChange);
|
||||
phase = new QuietFormChangePhase(pokemon, matchingFormChange);
|
||||
}
|
||||
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) {
|
||||
this.overridePhase(phase);
|
||||
@ -3035,7 +3052,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
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) {
|
||||
this.pushPhase(phase);
|
||||
} else {
|
||||
@ -3053,7 +3070,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
validateAchv(achv: Achv, args?: unknown[]): boolean {
|
||||
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.ui.achvBar.showAchv(achv);
|
||||
if (vouchers.hasOwnProperty(achv.id)) {
|
||||
@ -3066,7 +3083,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
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.ui.achvBar.showAchv(voucher);
|
||||
this.gameData.voucherCounts[voucher.voucherType]++;
|
||||
@ -3139,9 +3156,9 @@ export default class BattleScene extends SceneBase {
|
||||
this.currentBattle.double = true;
|
||||
const availablePartyMembers = this.getPlayerParty().filter((p) => p.isAllowedInBattle());
|
||||
if (availablePartyMembers.length > 1) {
|
||||
this.pushPhase(new ToggleDoublePositionPhase(this, true));
|
||||
this.pushPhase(new ToggleDoublePositionPhase(true));
|
||||
if (!availablePartyMembers[1].isOnField()) {
|
||||
this.pushPhase(new SummonPhase(this, 1));
|
||||
this.pushPhase(new SummonPhase(1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3186,7 +3203,7 @@ export default class BattleScene extends SceneBase {
|
||||
if (participated && pokemonDefeated) {
|
||||
partyMember.addFriendship(FRIENDSHIP_GAIN_FROM_BATTLE);
|
||||
const machoBraceModifier = partyMember.getHeldItems().find(m => m instanceof PokemonIncrementingStatModifier);
|
||||
if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount(this)) {
|
||||
if (machoBraceModifier && machoBraceModifier.stackCount < machoBraceModifier.getMaxStackCount()) {
|
||||
machoBraceModifier.stackCount++;
|
||||
this.updateModifiers(true, true);
|
||||
partyMember.updateInfo();
|
||||
@ -3248,7 +3265,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
if (exp) {
|
||||
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 +3357,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
if (encounter) {
|
||||
encounter = new MysteryEncounter(encounter);
|
||||
encounter.populateDialogueTokensFromRequirements(this);
|
||||
encounter.populateDialogueTokensFromRequirements();
|
||||
return encounter;
|
||||
}
|
||||
|
||||
@ -3368,8 +3385,9 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
let availableEncounters: MysteryEncounter[] = [];
|
||||
// New encounter should never be the same as the most recent encounter
|
||||
const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.length > 0 ? this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1].type : null;
|
||||
const previousEncounter = this.mysteryEncounterSaveData.encounteredEvents.length > 0 ?
|
||||
this.mysteryEncounterSaveData.encounteredEvents[this.mysteryEncounterSaveData.encounteredEvents.length - 1].type
|
||||
: null;
|
||||
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType) ?? [];
|
||||
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
|
||||
while (availableEncounters.length === 0 && tier !== null) {
|
||||
@ -3379,27 +3397,27 @@ export default class BattleScene extends SceneBase {
|
||||
if (!encounterCandidate) {
|
||||
return false;
|
||||
}
|
||||
if (encounterCandidate.encounterTier !== tier) { // Encounter is in tier
|
||||
if (encounterCandidate.encounterTier !== tier) {
|
||||
return false;
|
||||
}
|
||||
const disallowedGameModes = encounterCandidate.disallowedGameModes;
|
||||
if (disallowedGameModes && disallowedGameModes.length > 0
|
||||
&& disallowedGameModes.includes(this.gameMode.modeId)) { // Encounter is enabled for game mode
|
||||
&& disallowedGameModes.includes(this.gameMode.modeId)) {
|
||||
return false;
|
||||
}
|
||||
if (this.gameMode.modeId === GameModes.CHALLENGE) { // Encounter is enabled for challenges
|
||||
if (this.gameMode.modeId === GameModes.CHALLENGE) {
|
||||
const disallowedChallenges = encounterCandidate.disallowedChallenges;
|
||||
if (disallowedChallenges && disallowedChallenges.length > 0 && this.gameMode.challenges.some(challenge => disallowedChallenges.includes(challenge.id))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!encounterCandidate.meetsRequirements(this)) { // Meets encounter requirements
|
||||
if (!encounterCandidate.meetsRequirements()) {
|
||||
return false;
|
||||
}
|
||||
if (previousEncounter !== null && encounterType === previousEncounter) { // Previous encounter was not this one
|
||||
if (previousEncounter !== null && encounterType === previousEncounter) {
|
||||
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)
|
||||
&& this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters) {
|
||||
return false;
|
||||
@ -3427,7 +3445,7 @@ export default class BattleScene extends SceneBase {
|
||||
encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)];
|
||||
// New encounter object to not dirty flags
|
||||
encounter = new MysteryEncounter(encounter);
|
||||
encounter.populateDialogueTokensFromRequirements(this);
|
||||
encounter.populateDialogueTokensFromRequirements();
|
||||
return encounter;
|
||||
}
|
||||
}
|
||||
|
||||
121
src/battle.ts
@ -1,26 +1,27 @@
|
||||
import BattleScene from "./battle-scene";
|
||||
import { Command } from "./ui/command-ui-handler";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { Command } from "./ui/command-ui-handler";
|
||||
import * as Utils from "./utils";
|
||||
import Trainer, { TrainerVariant } from "./field/trainer";
|
||||
import { GameMode } from "./game-mode";
|
||||
import type { GameMode } from "./game-mode";
|
||||
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 { 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 { BattleSpec } from "#enums/battle-spec";
|
||||
import { Moves } from "#enums/moves";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { MusicPreference } from "#app/system/settings/settings";
|
||||
import { Species } from "#enums/species";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
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 { CustomModifierSettings } from "#app/modifier/modifier-type";
|
||||
import type { CustomModifierSettings } from "#app/modifier/modifier-type";
|
||||
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 {
|
||||
// TODO: other fixed wave battles should be added here
|
||||
@ -44,12 +45,12 @@ export enum BattlerIndex {
|
||||
}
|
||||
|
||||
export interface TurnCommand {
|
||||
command: Command;
|
||||
cursor?: number;
|
||||
move?: QueuedMove;
|
||||
targets?: BattlerIndex[];
|
||||
skip?: boolean;
|
||||
args?: any[];
|
||||
command: Command;
|
||||
cursor?: number;
|
||||
move?: TurnMove;
|
||||
targets?: BattlerIndex[];
|
||||
skip?: boolean;
|
||||
args?: any[];
|
||||
}
|
||||
|
||||
export interface FaintLogEntry {
|
||||
@ -154,7 +155,7 @@ export default class Battle {
|
||||
return this.double ? 2 : 1;
|
||||
}
|
||||
|
||||
incrementTurn(scene: BattleScene): void {
|
||||
incrementTurn(): void {
|
||||
this.turn++;
|
||||
this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ]));
|
||||
this.battleSeedState = null;
|
||||
@ -169,7 +170,7 @@ export default class Battle {
|
||||
}
|
||||
|
||||
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;
|
||||
//@ts-ignore - this is awful to fix/change
|
||||
ret.pokemonId = null;
|
||||
@ -177,43 +178,43 @@ export default class Battle {
|
||||
}));
|
||||
}
|
||||
|
||||
pickUpScatteredMoney(scene: BattleScene): void {
|
||||
const moneyAmount = new Utils.IntegerHolder(scene.currentBattle.moneyScattered);
|
||||
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
|
||||
pickUpScatteredMoney(): void {
|
||||
const moneyAmount = new Utils.IntegerHolder(globalScene.currentBattle.moneyScattered);
|
||||
globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
|
||||
|
||||
if (scene.arena.getTag(ArenaTagType.HAPPY_HOUR)) {
|
||||
if (globalScene.arena.getTag(ArenaTagType.HAPPY_HOUR)) {
|
||||
moneyAmount.value *= 2;
|
||||
}
|
||||
|
||||
scene.addMoney(moneyAmount.value);
|
||||
globalScene.addMoney(moneyAmount.value);
|
||||
|
||||
const userLocale = navigator.language || "en-US";
|
||||
const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale);
|
||||
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 {
|
||||
let partyMemberTurnMultiplier = scene.getEnemyParty().length / 2 + 0.5;
|
||||
addBattleScore(): void {
|
||||
let partyMemberTurnMultiplier = globalScene.getEnemyParty().length / 2 + 0.5;
|
||||
if (this.double) {
|
||||
partyMemberTurnMultiplier /= 1.5;
|
||||
}
|
||||
for (const p of scene.getEnemyParty()) {
|
||||
for (const p of globalScene.getEnemyParty()) {
|
||||
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 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(`Total Score: ${scene.score}`);
|
||||
scene.updateScoreText();
|
||||
console.log(`Total Score: ${globalScene.score}`);
|
||||
globalScene.updateScoreText();
|
||||
}
|
||||
|
||||
getBgmOverride(scene: BattleScene): string | null {
|
||||
getBgmOverride(): string | null {
|
||||
if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
|
||||
// Music is overridden for MEs during ME onInit()
|
||||
// Should not use any BGM overrides before swapping from DEFAULT mode
|
||||
@ -222,7 +223,7 @@ export default class Battle {
|
||||
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
|
||||
return `encounter_${this.trainer?.getEncounterBgm()}`;
|
||||
}
|
||||
if (scene.musicPreference === MusicPreference.GENFIVE) {
|
||||
if (globalScene.musicPreference === MusicPreference.GENFIVE) {
|
||||
return this.trainer?.getBattleBgm() ?? null;
|
||||
} else {
|
||||
return this.trainer?.getMixedBattleBgm() ?? null;
|
||||
@ -230,7 +231,7 @@ export default class Battle {
|
||||
} else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) {
|
||||
return "end_summit";
|
||||
}
|
||||
const wildOpponents = scene.getEnemyParty();
|
||||
const wildOpponents = globalScene.getEnemyParty();
|
||||
for (const pokemon of wildOpponents) {
|
||||
if (this.battleSpec === BattleSpec.FINAL_BOSS) {
|
||||
if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ETERNAMAX) {
|
||||
@ -239,7 +240,7 @@ export default class Battle {
|
||||
return "battle_final_encounter";
|
||||
}
|
||||
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
||||
if (scene.musicPreference === MusicPreference.GENFIVE) {
|
||||
if (globalScene.musicPreference === MusicPreference.GENFIVE) {
|
||||
switch (pokemon.species.speciesId) {
|
||||
case Species.REGIROCK:
|
||||
case Species.REGICE:
|
||||
@ -256,7 +257,7 @@ export default class Battle {
|
||||
}
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
} else if (scene.musicPreference === MusicPreference.ALLGENS) {
|
||||
} else if (globalScene.musicPreference === MusicPreference.ALLGENS) {
|
||||
switch (pokemon.species.speciesId) {
|
||||
case Species.ARTICUNO:
|
||||
case Species.ZAPDOS:
|
||||
@ -396,7 +397,7 @@ export default class Battle {
|
||||
}
|
||||
}
|
||||
|
||||
if (scene.gameMode.isClassic && this.waveIndex <= 4) {
|
||||
if (globalScene.gameMode.isClassic && this.waveIndex <= 4) {
|
||||
return "battle_wild";
|
||||
}
|
||||
|
||||
@ -409,12 +410,12 @@ export default class Battle {
|
||||
* @param min The minimum integer to pick, default `0`
|
||||
* @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) {
|
||||
return min;
|
||||
}
|
||||
const tempRngCounter = scene.rngCounter;
|
||||
const tempSeedOverride = scene.rngSeedOverride;
|
||||
const tempRngCounter = globalScene.rngCounter;
|
||||
const tempSeedOverride = globalScene.rngSeedOverride;
|
||||
const state = Phaser.Math.RND.state();
|
||||
if (this.battleSeedState) {
|
||||
Phaser.Math.RND.state(this.battleSeedState);
|
||||
@ -422,13 +423,13 @@ export default class Battle {
|
||||
Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.battleSeed, this.turn << 6) ]);
|
||||
console.log("Battle Seed:", this.battleSeed);
|
||||
}
|
||||
scene.rngCounter = this.rngCounter++;
|
||||
scene.rngSeedOverride = this.battleSeed;
|
||||
globalScene.rngCounter = this.rngCounter++;
|
||||
globalScene.rngSeedOverride = this.battleSeed;
|
||||
const ret = Utils.randSeedInt(range, min);
|
||||
this.battleSeedState = Phaser.Math.RND.state();
|
||||
Phaser.Math.RND.state(state);
|
||||
scene.rngCounter = tempRngCounter;
|
||||
scene.rngSeedOverride = tempSeedOverride;
|
||||
globalScene.rngCounter = tempRngCounter;
|
||||
globalScene.rngSeedOverride = tempSeedOverride;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -441,16 +442,16 @@ export default class Battle {
|
||||
}
|
||||
|
||||
export class FixedBattle extends Battle {
|
||||
constructor(scene: BattleScene, waveIndex: number, config: FixedBattleConfig) {
|
||||
super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : undefined, config.double);
|
||||
constructor(waveIndex: number, config: FixedBattleConfig) {
|
||||
super(globalScene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer() : undefined, config.double);
|
||||
if (config.getEnemyParty) {
|
||||
this.enemyParty = config.getEnemyParty(scene);
|
||||
this.enemyParty = config.getEnemyParty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type GetTrainerFunc = (scene: BattleScene) => Trainer;
|
||||
type GetEnemyPartyFunc = (scene: BattleScene) => EnemyPokemon[];
|
||||
type GetTrainerFunc = () => Trainer;
|
||||
type GetEnemyPartyFunc = () => EnemyPokemon[];
|
||||
|
||||
export class FixedBattleConfig {
|
||||
public battleType: BattleType;
|
||||
@ -500,11 +501,11 @@ export class FixedBattleConfig {
|
||||
* @returns the generated trainer
|
||||
*/
|
||||
function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], randomGender: boolean = false, seedOffset: number = 0): GetTrainerFunc {
|
||||
return (scene: BattleScene) => {
|
||||
return () => {
|
||||
const rand = Utils.randSeedInt(trainerPool.length);
|
||||
const trainerTypes: TrainerType[] = [];
|
||||
|
||||
scene.executeWithSeedOffset(() => {
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
for (const trainerPoolEntry of trainerPool) {
|
||||
const trainerType = Array.isArray(trainerPoolEntry)
|
||||
? Utils.randSeedItem(trainerPoolEntry)
|
||||
@ -523,10 +524,10 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
|
||||
const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]);
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
@ -544,16 +545,16 @@ export interface FixedBattleConfigs {
|
||||
*/
|
||||
export const classicFixedBattles: FixedBattleConfigs = {
|
||||
[5]: 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)
|
||||
.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)
|
||||
.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 }),
|
||||
[35]: 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)),
|
||||
[55]: 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 }),
|
||||
[62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.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)),
|
||||
@ -562,7 +563,7 @@ export const classicFixedBattles: FixedBattleConfigs = {
|
||||
[66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.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)
|
||||
.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 }),
|
||||
[112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.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)),
|
||||
@ -572,7 +573,7 @@ export const classicFixedBattles: FixedBattleConfigs = {
|
||||
.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 }),
|
||||
[145]: 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 }),
|
||||
[ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.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 ]))
|
||||
@ -588,6 +589,6 @@ export const classicFixedBattles: FixedBattleConfigs = {
|
||||
[190]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182)
|
||||
.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)
|
||||
.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 })
|
||||
};
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { Arena } from "#app/field/arena";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { Arena } from "#app/field/arena";
|
||||
import { Type } from "#enums/type";
|
||||
import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils";
|
||||
import { MoveCategory, allMoves, MoveTarget } from "#app/data/move";
|
||||
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 { BattlerIndex } from "#app/battle";
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
|
||||
@ -44,7 +45,7 @@ export abstract class ArenaTag {
|
||||
|
||||
onRemove(arena: Arena, quiet: boolean = false): void {
|
||||
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
|
||||
* @param scene medium to retrieve the source Pokemon
|
||||
* @returns The source {@linkcode Pokemon} or `null` if none is found
|
||||
*/
|
||||
public getSourcePokemon(scene: BattleScene): Pokemon | null {
|
||||
return this.sourceId ? scene.getPokemonById(this.sourceId) : null;
|
||||
public getSourcePokemon(): Pokemon | null {
|
||||
return this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that retrieves the Pokemon affected
|
||||
* @param scene - medium to retrieve the involved Pokemon
|
||||
* @returns list of PlayerPokemon or EnemyPokemon on the field
|
||||
*/
|
||||
public getAffectedPokemon(scene: BattleScene): Pokemon[] {
|
||||
public getAffectedPokemon(): Pokemon[] {
|
||||
switch (this.side) {
|
||||
case ArenaTagSide.PLAYER:
|
||||
return scene.getPlayerField() ?? [];
|
||||
return globalScene.getPlayerField() ?? [];
|
||||
case ArenaTagSide.ENEMY:
|
||||
return scene.getEnemyField() ?? [];
|
||||
return globalScene.getEnemyField() ?? [];
|
||||
case ArenaTagSide.BOTH:
|
||||
default:
|
||||
return scene.getField(true) ?? [];
|
||||
return globalScene.getField(true) ?? [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,10 +111,10 @@ export class MistTag extends ArenaTag {
|
||||
super.onAdd(arena);
|
||||
|
||||
if (this.sourceId) {
|
||||
const source = arena.scene.getPokemonById(this.sourceId);
|
||||
const source = globalScene.getPokemonById(this.sourceId);
|
||||
|
||||
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) {
|
||||
console.warn("Failed to get source for MistTag onAdd");
|
||||
}
|
||||
@ -146,7 +145,7 @@ export class MistTag extends ArenaTag {
|
||||
cancelled.value = true;
|
||||
|
||||
if (!simulated) {
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:mistApply"));
|
||||
globalScene.queueMessage(i18next.t("arenaTag:mistApply"));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -193,7 +192,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
|
||||
if (bypassed.value) {
|
||||
return false;
|
||||
}
|
||||
damageMultiplier.value = arena.scene.currentBattle.double ? 2732 / 4096 : 0.5;
|
||||
damageMultiplier.value = globalScene.currentBattle.double ? 2732 / 4096 : 0.5;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -211,7 +210,7 @@ class ReflectTag extends WeakenMoveScreenTag {
|
||||
|
||||
onAdd(arena: Arena, quiet: boolean = false): void {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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
|
||||
@ -296,8 +295,8 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
if (!simulated) {
|
||||
attacker.stopMultiHit(defender);
|
||||
|
||||
new CommonBattleAnim(CommonAnim.PROTECT, defender).play(arena.scene);
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(defender) }));
|
||||
new CommonBattleAnim(CommonAnim.PROTECT, defender).play();
|
||||
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 move = allMoves[moveId];
|
||||
const effectPhase = arena.scene.getCurrentPhase();
|
||||
const effectPhase = globalScene.getCurrentPhase();
|
||||
|
||||
if (effectPhase instanceof MoveEffectPhase) {
|
||||
const attacker = effectPhase.getUserPokemon();
|
||||
@ -393,9 +392,9 @@ class MatBlockTag extends ConditionalProtectTag {
|
||||
|
||||
onAdd(arena: Arena) {
|
||||
if (this.sourceId) {
|
||||
const source = arena.scene.getPokemonById(this.sourceId);
|
||||
const source = globalScene.getPokemonById(this.sourceId);
|
||||
if (source) {
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
|
||||
globalScene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
|
||||
} else {
|
||||
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 */
|
||||
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()
|
||||
}));
|
||||
}
|
||||
|
||||
/** Queues a message upon removing this effect from the field */
|
||||
onRemove(arena: Arena): void {
|
||||
const source = arena.scene.getPokemonById(this.sourceId!); // TODO: is this bang correct?
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:noCritOnRemove", {
|
||||
const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct?
|
||||
globalScene.queueMessage(i18next.t("arenaTag:noCritOnRemove", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined),
|
||||
moveName: this.getMoveName()
|
||||
}));
|
||||
@ -478,7 +477,7 @@ class WishTag extends ArenaTag {
|
||||
|
||||
onAdd(arena: Arena): void {
|
||||
if (this.sourceId) {
|
||||
const user = arena.scene.getPokemonById(this.sourceId);
|
||||
const user = globalScene.getPokemonById(this.sourceId);
|
||||
if (user) {
|
||||
this.battlerIndex = user.getBattlerIndex();
|
||||
this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) });
|
||||
@ -490,10 +489,10 @@ class WishTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onRemove(arena: Arena): void {
|
||||
const target = arena.scene.getField()[this.battlerIndex];
|
||||
const target = globalScene.getField()[this.battlerIndex];
|
||||
if (target?.isActive(true)) {
|
||||
arena.scene.queueMessage(this.triggerMessage);
|
||||
arena.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), this.healHp, null, true, false));
|
||||
globalScene.queueMessage(this.triggerMessage);
|
||||
globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -546,11 +545,11 @@ class MudSportTag extends WeakenMoveTypeTag {
|
||||
}
|
||||
|
||||
onAdd(arena: Arena): void {
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
|
||||
globalScene.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
|
||||
}
|
||||
|
||||
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 {
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
|
||||
globalScene.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
|
||||
}
|
||||
|
||||
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 */
|
||||
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
|
||||
@ -679,9 +678,9 @@ class SpikesTag extends ArenaTrapTag {
|
||||
onAdd(arena: Arena, quiet: boolean = false): void {
|
||||
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) {
|
||||
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 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);
|
||||
if (pokemon.turnData) {
|
||||
pokemon.turnData.damageTaken += damage;
|
||||
@ -728,9 +727,9 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
||||
onAdd(arena: Arena, quiet: boolean = false): void {
|
||||
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) {
|
||||
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)) {
|
||||
this.neutralized = true;
|
||||
if (pokemon.scene.arena.removeTag(this.tagType)) {
|
||||
pokemon.scene.queueMessage(i18next.t("arenaTag:toxicSpikesActivateTrapPoison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() }));
|
||||
if (globalScene.arena.removeTag(this.tagType)) {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:toxicSpikesActivateTrapPoison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName() }));
|
||||
return true;
|
||||
}
|
||||
} else if (!pokemon.status) {
|
||||
@ -792,7 +791,7 @@ export class DelayedAttackTag extends ArenaTag {
|
||||
const ret = super.lapse(arena);
|
||||
|
||||
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;
|
||||
@ -814,9 +813,9 @@ class StealthRockTag extends ArenaTrapTag {
|
||||
onAdd(arena: Arena, quiet: boolean = false): void {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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);
|
||||
if (pokemon.turnData) {
|
||||
pokemon.turnData.damageTaken += damage;
|
||||
@ -893,9 +892,9 @@ class StickyWebTag extends ArenaTrapTag {
|
||||
|
||||
onAdd(arena: Arena, quiet: boolean = false): void {
|
||||
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) {
|
||||
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) {
|
||||
pokemon.scene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() }));
|
||||
globalScene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() }));
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -945,14 +944,14 @@ export class TrickRoomTag extends ArenaTag {
|
||||
}
|
||||
|
||||
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) {
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
|
||||
globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) }));
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
||||
arena.scene.getField(true).forEach((pokemon) => {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
||||
globalScene.getField(true).forEach((pokemon) => {
|
||||
if (pokemon !== null) {
|
||||
pokemon.removeTag(BattlerTagType.FLOATING);
|
||||
pokemon.removeTag(BattlerTagType.TELEKINESIS);
|
||||
@ -980,7 +979,7 @@ export class GravityTag extends ArenaTag {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 party = (source?.isPlayer() ? source.scene.getPlayerField() : source?.scene.getEnemyField()) ?? [];
|
||||
const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable!
|
||||
const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? [];
|
||||
|
||||
for (const pokemon of party) {
|
||||
// Apply the CHARGED tag to party members with the WIND_POWER ability
|
||||
if (pokemon.hasAbility(Abilities.WIND_POWER) && !pokemon.getTag(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
|
||||
if (pokemon.hasAbility(Abilities.WIND_RIDER)) {
|
||||
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.getBattlerIndex()));
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK ], 1, true));
|
||||
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.getBattlerIndex()));
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.ATK ], 1, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onRemove(arena: Arena, quiet: boolean = false): void {
|
||||
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 {
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
|
||||
globalScene.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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.
|
||||
* @param arena
|
||||
*/
|
||||
override onAdd({ scene }: Arena) {
|
||||
const source = this.getSourcePokemon(scene);
|
||||
override onAdd() {
|
||||
const source = this.getSourcePokemon();
|
||||
if (source) {
|
||||
const party = this.getAffectedPokemon(scene);
|
||||
const party = this.getAffectedPokemon();
|
||||
party?.forEach((p: Pokemon ) => {
|
||||
if (p.isAllowedInBattle()) {
|
||||
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
|
||||
* @returns `true` if the source of the tag is still active on the field | `false` if not
|
||||
*/
|
||||
override lapse({ scene }: Arena): boolean {
|
||||
const source = this.getSourcePokemon(scene);
|
||||
override lapse(): boolean {
|
||||
const source = this.getSourcePokemon();
|
||||
return source ? source.isActive(true) : false;
|
||||
}
|
||||
|
||||
@ -1103,7 +1102,7 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
* @returns `true`
|
||||
*/
|
||||
override activateTrap(pokemon: Pokemon): boolean {
|
||||
const source = this.getSourcePokemon(pokemon.scene);
|
||||
const source = this.getSourcePokemon();
|
||||
if (source && source.isActive(true) && pokemon.isAllowedInBattle()) {
|
||||
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
|
||||
* @param arena
|
||||
*/
|
||||
override onRemove({ scene }: Arena): void {
|
||||
const party = this.getAffectedPokemon(scene);
|
||||
override onRemove(): void {
|
||||
const party = this.getAffectedPokemon();
|
||||
party?.forEach((p: Pokemon) => {
|
||||
p.removeTag(BattlerTagType.IMPRISON);
|
||||
});
|
||||
@ -1136,19 +1135,19 @@ class FireGrassPledgeTag extends ArenaTag {
|
||||
|
||||
override onAdd(arena: Arena): void {
|
||||
// "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 {
|
||||
const field: Pokemon[] = (this.side === ArenaTagSide.PLAYER)
|
||||
? arena.scene.getPlayerField()
|
||||
: arena.scene.getEnemyField();
|
||||
? globalScene.getPlayerField()
|
||||
: globalScene.getEnemyField();
|
||||
|
||||
field.filter(pokemon => !pokemon.isOfType(Type.FIRE) && !pokemon.switchOutStatus).forEach(pokemon => {
|
||||
// "{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
|
||||
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));
|
||||
});
|
||||
|
||||
@ -1170,7 +1169,7 @@ class WaterFirePledgeTag extends ArenaTag {
|
||||
|
||||
override onAdd(arena: Arena): void {
|
||||
// "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 {
|
||||
// "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 {
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
||||
globalScene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Type } from "#enums/type";
|
||||
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 { Biome } from "#enums/biome";
|
||||
import { Species } from "#enums/species";
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { Gender } from "#app/data/gender";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import Pokemon from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { Type } from "#enums/type";
|
||||
import * as Utils from "#app/utils";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
@ -266,8 +267,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.ELECTRODE, 30, null, null)
|
||||
],
|
||||
[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.MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
|
||||
new SpeciesEvolution(Species.MAROWAK, 28, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.TYROGUE]: [
|
||||
/**
|
||||
@ -287,8 +288,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
)),
|
||||
],
|
||||
[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.WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
|
||||
new SpeciesEvolution(Species.WEEZING, 35, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.RHYHORN]: [
|
||||
new SpeciesEvolution(Species.RHYDON, 42, null, null)
|
||||
@ -333,8 +334,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.QUILAVA, 14, null, null)
|
||||
],
|
||||
[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.TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
|
||||
new SpeciesEvolution(Species.TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.TOTODILE]: [
|
||||
new SpeciesEvolution(Species.CROCONAW, 18, null, null)
|
||||
@ -436,8 +437,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.LINOONE, 20, null, null)
|
||||
],
|
||||
[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.CASCOON, 7, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
|
||||
new SpeciesEvolution(Species.SILCOON, 7, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY)),
|
||||
new SpeciesEvolution(Species.CASCOON, 7, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
|
||||
],
|
||||
[Species.SILCOON]: [
|
||||
new SpeciesEvolution(Species.BEAUTIFLY, 10, null, null)
|
||||
@ -478,7 +479,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
],
|
||||
[Species.NINCADA]: [
|
||||
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 SpeciesEvolutionCondition(p => globalScene.getPlayerParty().length < 6 && globalScene.pokeballCounts[PokeballType.POKEBALL] > 0))
|
||||
],
|
||||
[Species.WHISMUR]: [
|
||||
new SpeciesEvolution(Species.LOUDRED, 20, null, null)
|
||||
@ -660,7 +661,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.LUMINEON, 31, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => !!globalScene.gameData.dexData[Species.REMORAID].caughtAttr), SpeciesWildEvolutionDelay.MEDIUM)
|
||||
],
|
||||
[Species.SNOVER]: [
|
||||
new SpeciesEvolution(Species.ABOMASNOW, 40, null, null)
|
||||
@ -681,8 +682,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.DEWOTT, 17, null, null)
|
||||
],
|
||||
[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.SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
|
||||
new SpeciesEvolution(Species.SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.PATRAT]: [
|
||||
new SpeciesEvolution(Species.WATCHOG, 20, null, null)
|
||||
@ -832,8 +833,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
],
|
||||
[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.BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
|
||||
new SpeciesEvolution(Species.BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.VULLABY]: [
|
||||
new SpeciesEvolution(Species.MANDIBUZZ, 54, null, null)
|
||||
@ -890,7 +891,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.GOGOAT, 32, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => !!globalScene.getPlayerParty().find(p => p.getTypes(false, false, true).indexOf(Type.DARK) > -1)), SpeciesWildEvolutionDelay.MEDIUM)
|
||||
],
|
||||
[Species.ESPURR]: [
|
||||
new SpeciesFormEvolution(Species.MEOWSTIC, "", "female", 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)),
|
||||
@ -912,21 +913,21 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.CLAWITZER, 37, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
|
||||
],
|
||||
[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.SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
|
||||
new SpeciesEvolution(Species.SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(globalScene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[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.AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
|
||||
new SpeciesEvolution(Species.AVALUGG, 37, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.NOIBAT]: [
|
||||
new SpeciesEvolution(Species.NOIVERN, 48, null, null)
|
||||
@ -935,8 +936,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.DARTRIX, 17, null, null)
|
||||
],
|
||||
[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.DECIDUEYE, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
|
||||
new SpeciesEvolution(Species.DECIDUEYE, 34, null, new SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.LITTEN]: [
|
||||
new SpeciesEvolution(Species.TORRACAT, 17, null, null)
|
||||
@ -957,7 +958,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.TOUCANNON, 28, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.GRUBBIN]: [
|
||||
new SpeciesEvolution(Species.CHARJABUG, 20, null, null)
|
||||
@ -975,7 +976,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.ARAQUANID, 22, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY))
|
||||
],
|
||||
[Species.MORELULL]: [
|
||||
new SpeciesEvolution(Species.SHIINOTIC, 24, null, null)
|
||||
@ -1012,7 +1013,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.MELMETAL, 48, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
|
||||
],
|
||||
[Species.ALOLA_DIGLETT]: [
|
||||
new SpeciesEvolution(Species.ALOLA_DUGTRIO, 26, null, null)
|
||||
@ -1135,7 +1136,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.GALAR_LINOONE, 20, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
|
||||
],
|
||||
[Species.GALAR_YAMASK]: [
|
||||
new SpeciesEvolution(Species.RUNERIGUS, 34, null, null)
|
||||
@ -1144,7 +1145,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.HISUI_ZOROARK, 30, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(globalScene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[Species.SPRIGATITO]: [
|
||||
new SpeciesEvolution(Species.FLORAGATO, 16, null, null)
|
||||
@ -1183,7 +1184,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
[Species.TANDEMAUS]: [
|
||||
new SpeciesFormEvolution(Species.MAUSHOLD, "", "three", 25, null, new SpeciesEvolutionCondition(p => {
|
||||
let ret = false;
|
||||
p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
|
||||
globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
|
||||
return ret;
|
||||
})),
|
||||
new SpeciesEvolution(Species.MAUSHOLD, 25, null, null)
|
||||
@ -1243,7 +1244,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.GLIMMORA, 35, null, null)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
|
||||
],
|
||||
[Species.FRIGIBAX]: [
|
||||
new SpeciesEvolution(Species.ARCTIBAX, 35, null, null)
|
||||
@ -1311,10 +1312,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
[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, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === 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, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.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, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT), 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.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG),
|
||||
@ -1351,17 +1352,17 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => {
|
||||
let ret = false;
|
||||
if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) {
|
||||
p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
|
||||
globalScene.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]: [
|
||||
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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor claw at night*/), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
],
|
||||
[Species.URSARING]: [
|
||||
new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna
|
||||
@ -1391,8 +1392,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM)
|
||||
],
|
||||
[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.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.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), SpeciesWildEvolutionDelay.MEDIUM),
|
||||
new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY)), SpeciesWildEvolutionDelay.MEDIUM)
|
||||
],
|
||||
[Species.PANSAGE]: [
|
||||
new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
|
||||
@ -1442,9 +1443,9 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[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, "", "midday", 25, null, new SpeciesEvolutionCondition(p => (globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY) && (p.formIndex === 0))),
|
||||
new SpeciesFormEvolution(Species.LYCANROC, "own-tempo", "dusk", 25, null, new SpeciesEvolutionCondition(p => p.formIndex === 1)),
|
||||
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 SpeciesEvolutionCondition(p => (globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0)))
|
||||
],
|
||||
[Species.STEENEE]: [
|
||||
new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG)
|
||||
@ -1471,15 +1472,15 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesFormEvolution(Species.POLTEAGEIST, "antique", "antique", 1, EvolutionItem.CHIPPED_POT, null, SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[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, "", "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 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),
|
||||
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, "", "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 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),
|
||||
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, "", "caramel-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.TEMPLE || p.scene.arena.biomeType === Biome.ISLAND), SpeciesWildEvolutionDelay.LONG),
|
||||
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)
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "vanilla-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.TOWN || globalScene.arena.biomeType === Biome.PLAINS || globalScene.arena.biomeType === Biome.GRASS || globalScene.arena.biomeType === Biome.TALL_GRASS || globalScene.arena.biomeType === Biome.METROPOLIS), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.BADLANDS || globalScene.arena.biomeType === Biome.VOLCANO || globalScene.arena.biomeType === Biome.GRAVEYARD || globalScene.arena.biomeType === Biome.FACTORY || globalScene.arena.biomeType === Biome.SLUM), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "matcha-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.FOREST || globalScene.arena.biomeType === Biome.SWAMP || globalScene.arena.biomeType === Biome.MEADOW || globalScene.arena.biomeType === Biome.JUNGLE), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "mint-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.SEA || globalScene.arena.biomeType === Biome.BEACH || globalScene.arena.biomeType === Biome.LAKE || globalScene.arena.biomeType === Biome.SEABED), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "lemon-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.DESERT || globalScene.arena.biomeType === Biome.POWER_PLANT || globalScene.arena.biomeType === Biome.DOJO || globalScene.arena.biomeType === Biome.RUINS || globalScene.arena.biomeType === Biome.CONSTRUCTION_SITE), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "salted-cream", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.MOUNTAIN || globalScene.arena.biomeType === Biome.CAVE || globalScene.arena.biomeType === Biome.ICE_CAVE || globalScene.arena.biomeType === Biome.FAIRY_CAVE || globalScene.arena.biomeType === Biome.SNOWY_FOREST), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "ruby-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.WASTELAND || globalScene.arena.biomeType === Biome.LABORATORY), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "caramel-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.TEMPLE || globalScene.arena.biomeType === Biome.ISLAND), SpeciesWildEvolutionDelay.LONG),
|
||||
new SpeciesFormEvolution(Species.ALCREMIE, "", "rainbow-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, new SpeciesEvolutionCondition(p => globalScene.arena.biomeType === Biome.ABYSS || globalScene.arena.biomeType === Biome.SPACE || globalScene.arena.biomeType === Biome.END), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[Species.DURALUDON]: [
|
||||
new SpeciesFormEvolution(Species.ARCHALUDON, "", "", 1, EvolutionItem.METAL_ALLOY, null, SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
@ -1501,7 +1502,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
],
|
||||
[Species.CHARCADET]: [
|
||||
new SpeciesEvolution(Species.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG),
|
||||
@ -1581,10 +1582,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.CONKELDURR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => !!globalScene.gameData.dexData[Species.SHELMET].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
],
|
||||
[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 SpeciesEvolutionCondition(p => !!globalScene.gameData.dexData[Species.KARRABLAST].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
],
|
||||
[Species.SPRITZEE]: [
|
||||
new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.SACHET, null, SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
@ -1627,13 +1628,13 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.MARILL, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.SHORT)
|
||||
],
|
||||
[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 SpeciesFriendshipEvolutionCondition(70, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.SHORT)
|
||||
],
|
||||
[Species.BUNEARY]: [
|
||||
new SpeciesEvolution(Species.LOPUNNY, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.MEDIUM)
|
||||
],
|
||||
[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 SpeciesFriendshipEvolutionCondition(90, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM)
|
||||
],
|
||||
[Species.HAPPINY]: [
|
||||
new SpeciesEvolution(Species.CHANSEY, 1, null, new SpeciesFriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT)
|
||||
@ -1642,7 +1643,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.SNORLAX, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[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 SpeciesFriendshipEvolutionCondition(120, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[Species.WOOBAT]: [
|
||||
new SpeciesEvolution(Species.SWOOBAT, 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.MEDIUM)
|
||||
@ -1657,16 +1658,16 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
|
||||
],
|
||||
[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 SpeciesFriendshipEvolutionCondition(90, p => globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM)
|
||||
],
|
||||
[Species.GIMMIGHOUL]: [
|
||||
new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition(p => p.evoCounter
|
||||
+ p.getHeldItems().filter(m => m instanceof DamageMoneyRewardModifier).length
|
||||
+ p.scene.findModifiers(m => m instanceof MoneyMultiplierModifier
|
||||
+ globalScene.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
|
||||
+ globalScene.findModifiers(m => m instanceof MoneyMultiplierModifier
|
||||
|| m instanceof ExtraModifierModifier || m instanceof TempExtraModifierModifier).length > 9), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||
]
|
||||
};
|
||||
|
||||
@ -51,9 +51,7 @@ export const speciesStarterCosts = {
|
||||
[Species.SANDSHREW]: 2,
|
||||
[Species.NIDORAN_F]: 3,
|
||||
[Species.NIDORAN_M]: 3,
|
||||
[Species.CLEFAIRY]: 3,
|
||||
[Species.VULPIX]: 3,
|
||||
[Species.JIGGLYPUFF]: 2,
|
||||
[Species.ZUBAT]: 3,
|
||||
[Species.ODDISH]: 3,
|
||||
[Species.PARAS]: 2,
|
||||
@ -84,22 +82,15 @@ export const speciesStarterCosts = {
|
||||
[Species.VOLTORB]: 2,
|
||||
[Species.EXEGGCUTE]: 3,
|
||||
[Species.CUBONE]: 3,
|
||||
[Species.HITMONLEE]: 4,
|
||||
[Species.HITMONCHAN]: 4,
|
||||
[Species.LICKITUNG]: 3,
|
||||
[Species.KOFFING]: 2,
|
||||
[Species.RHYHORN]: 4,
|
||||
[Species.CHANSEY]: 3,
|
||||
[Species.TANGELA]: 3,
|
||||
[Species.KANGASKHAN]: 4,
|
||||
[Species.HORSEA]: 3,
|
||||
[Species.GOLDEEN]: 2,
|
||||
[Species.STARYU]: 3,
|
||||
[Species.MR_MIME]: 3,
|
||||
[Species.SCYTHER]: 5,
|
||||
[Species.JYNX]: 4,
|
||||
[Species.ELECTABUZZ]: 4,
|
||||
[Species.MAGMAR]: 4,
|
||||
[Species.PINSIR]: 4,
|
||||
[Species.TAUROS]: 4,
|
||||
[Species.MAGIKARP]: 4,
|
||||
@ -110,7 +101,6 @@ export const speciesStarterCosts = {
|
||||
[Species.OMANYTE]: 3,
|
||||
[Species.KABUTO]: 3,
|
||||
[Species.AERODACTYL]: 5,
|
||||
[Species.SNORLAX]: 5,
|
||||
[Species.ARTICUNO]: 5,
|
||||
[Species.ZAPDOS]: 6,
|
||||
[Species.MOLTRES]: 6,
|
||||
@ -132,8 +122,6 @@ export const speciesStarterCosts = {
|
||||
[Species.TOGEPI]: 3,
|
||||
[Species.NATU]: 2,
|
||||
[Species.MAREEP]: 2,
|
||||
[Species.MARILL]: 4,
|
||||
[Species.SUDOWOODO]: 3,
|
||||
[Species.HOPPIP]: 2,
|
||||
[Species.AIPOM]: 2,
|
||||
[Species.SUNKERN]: 1,
|
||||
@ -142,7 +130,6 @@ export const speciesStarterCosts = {
|
||||
[Species.MURKROW]: 3,
|
||||
[Species.MISDREAVUS]: 2,
|
||||
[Species.UNOWN]: 1,
|
||||
[Species.WOBBUFFET]: 2,
|
||||
[Species.GIRAFARIG]: 3,
|
||||
[Species.PINECO]: 2,
|
||||
[Species.DUNSPARCE]: 3,
|
||||
@ -158,7 +145,6 @@ export const speciesStarterCosts = {
|
||||
[Species.CORSOLA]: 2,
|
||||
[Species.REMORAID]: 2,
|
||||
[Species.DELIBIRD]: 2,
|
||||
[Species.MANTINE]: 3,
|
||||
[Species.SKARMORY]: 4,
|
||||
[Species.HOUNDOUR]: 3,
|
||||
[Species.PHANPY]: 3,
|
||||
@ -206,7 +192,6 @@ export const speciesStarterCosts = {
|
||||
[Species.MINUN]: 2,
|
||||
[Species.VOLBEAT]: 2,
|
||||
[Species.ILLUMISE]: 2,
|
||||
[Species.ROSELIA]: 3,
|
||||
[Species.GULPIN]: 1,
|
||||
[Species.CARVANHA]: 3,
|
||||
[Species.WAILMER]: 2,
|
||||
@ -232,7 +217,6 @@ export const speciesStarterCosts = {
|
||||
[Species.SHUPPET]: 2,
|
||||
[Species.DUSKULL]: 3,
|
||||
[Species.TROPIUS]: 3,
|
||||
[Species.CHIMECHO]: 3,
|
||||
[Species.ABSOL]: 4,
|
||||
[Species.WYNAUT]: 2,
|
||||
[Species.SNORUNT]: 2,
|
||||
@ -543,7 +527,6 @@ export const speciesStarterCosts = {
|
||||
[Species.GALAR_PONYTA]: 2,
|
||||
[Species.GALAR_SLOWPOKE]: 3,
|
||||
[Species.GALAR_FARFETCHD]: 3,
|
||||
[Species.GALAR_MR_MIME]: 3,
|
||||
[Species.GALAR_ARTICUNO]: 6,
|
||||
[Species.GALAR_ZAPDOS]: 6,
|
||||
[Species.GALAR_MOLTRES]: 6,
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
//import { battleAnimRawData } from "./battle-anim-raw-data";
|
||||
import BattleScene from "../battle-scene";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { BattlerIndex } from "../battle";
|
||||
import { Element } from "json-stable-stringify";
|
||||
import type { BattlerIndex } from "../battle";
|
||||
import type { Element } from "json-stable-stringify";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { SubstituteTag } from "./battler-tags";
|
||||
import { isNullOrUndefined } from "../utils";
|
||||
import Phaser from "phaser";
|
||||
import { EncounterAnim } from "#enums/encounter-anims";
|
||||
//import fs from 'vite-plugin-fs/browser';
|
||||
|
||||
export enum AnimFrameTarget {
|
||||
USER,
|
||||
@ -308,7 +306,7 @@ abstract class AnimTimedEvent {
|
||||
this.resourceName = resourceName;
|
||||
}
|
||||
|
||||
abstract execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer;
|
||||
abstract execute(battleAnim: BattleAnim, priority?: number): integer;
|
||||
|
||||
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) };
|
||||
if (this.resourceName) {
|
||||
try {
|
||||
scene.playSound(`battle_anims/${this.resourceName}`, soundConfig);
|
||||
globalScene.playSound(`battle_anims/${this.resourceName}`, soundConfig);
|
||||
} catch (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 {
|
||||
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);
|
||||
}
|
||||
|
||||
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer {
|
||||
execute(moveAnim: MoveAnim, priority?: number): integer {
|
||||
const tweenProps = {};
|
||||
if (this.bgX !== undefined) {
|
||||
tweenProps["x"] = (this.bgX * 0.5) - 320;
|
||||
@ -400,7 +398,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
|
||||
tweenProps["alpha"] = (this.opacity || 0) / 255;
|
||||
}
|
||||
if (Object.keys(tweenProps).length) {
|
||||
scene.tweens.add(Object.assign({
|
||||
globalScene.tweens.add(Object.assign({
|
||||
targets: moveAnim.bgSprite,
|
||||
duration: Utils.getFrameMs(this.duration * 3)
|
||||
}, tweenProps));
|
||||
@ -418,25 +416,25 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
||||
super(frameIndex, resourceName, source);
|
||||
}
|
||||
|
||||
execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer {
|
||||
execute(moveAnim: MoveAnim, priority?: number): integer {
|
||||
if (moveAnim.bgSprite) {
|
||||
moveAnim.bgSprite.destroy();
|
||||
}
|
||||
moveAnim.bgSprite = this.resourceName
|
||||
? scene.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.tileSprite(this.bgX - 320, this.bgY - 284, 896, 576, this.resourceName)
|
||||
: globalScene.add.rectangle(this.bgX - 320, this.bgY - 284, 896, 576, 0);
|
||||
moveAnim.bgSprite.setOrigin(0, 0);
|
||||
moveAnim.bgSprite.setScale(1.25);
|
||||
moveAnim.bgSprite.setAlpha(this.opacity / 255);
|
||||
scene.field.add(moveAnim.bgSprite);
|
||||
const fieldPokemon = scene.getEnemyPokemon(false) ?? scene.getPlayerPokemon(false);
|
||||
globalScene.field.add(moveAnim.bgSprite);
|
||||
const fieldPokemon = globalScene.getEnemyPokemon(false) ?? globalScene.getPlayerPokemon(false);
|
||||
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()) {
|
||||
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,
|
||||
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 encounterAnims = new Map<EncounterAnim, AnimConfig>();
|
||||
|
||||
export function initCommonAnims(scene: BattleScene): Promise<void> {
|
||||
export function initCommonAnims(): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
const commonAnimNames = Utils.getEnumKeys(CommonAnim);
|
||||
const commonAnimIds = Utils.getEnumValues(CommonAnim);
|
||||
const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = [];
|
||||
for (let ca = 0; ca < commonAnimIds.length; 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(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 => {
|
||||
if (moveAnims.has(move)) {
|
||||
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 fetchAnimAndResolve = (move: Moves) => {
|
||||
scene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`)
|
||||
globalScene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`)
|
||||
.then(response => {
|
||||
const contentType = response.headers.get("content-type");
|
||||
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(BeakBlastHeaderAttr)[0]);
|
||||
if (chargeAnimSource) {
|
||||
initMoveChargeAnim(scene, chargeAnimSource.chargeAnim).then(() => resolve());
|
||||
initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
@ -557,10 +555,9 @@ function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
|
||||
|
||||
/**
|
||||
* Fetches animation configs to be used in a Mystery Encounter
|
||||
* @param scene
|
||||
* @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 encounterAnimNames = Utils.getEnumKeys(EncounterAnim);
|
||||
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))) {
|
||||
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(cas => encounterAnims.set(anim, new AnimConfig(cas))));
|
||||
}
|
||||
await Promise.allSettled(encounterAnimFetches);
|
||||
}
|
||||
|
||||
export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim): Promise<void> {
|
||||
export function initMoveChargeAnim(chargeAnim: ChargeAnim): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
if (chargeAnims.has(chargeAnim)) {
|
||||
if (chargeAnims.get(chargeAnim) !== null) {
|
||||
@ -590,7 +587,7 @@ export function initMoveChargeAnim(scene: BattleScene, chargeAnim: ChargeAnim):
|
||||
}
|
||||
} else {
|
||||
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(ca => {
|
||||
if (Array.isArray(ca)) {
|
||||
@ -623,23 +620,22 @@ function populateMoveChargeAnim(chargeAnim: ChargeAnim, animSource: any) {
|
||||
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 => {
|
||||
loadAnimAssets(scene, Array.from(commonAnims.values()), startLoad).then(() => resolve());
|
||||
loadAnimAssets(Array.from(commonAnims.values()), startLoad).then(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads encounter animation assets to scene
|
||||
* MUST be called after {@linkcode initEncounterAnims()} to load all required animations properly
|
||||
* @param scene
|
||||
* @param startLoad
|
||||
*/
|
||||
export async function loadEncounterAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> {
|
||||
await loadAnimAssets(scene, Array.from(encounterAnims.values()), startLoad);
|
||||
export async function loadEncounterAnimAssets(startLoad?: boolean): Promise<void> {
|
||||
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 => {
|
||||
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
|
||||
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 => {
|
||||
const backgrounds = new Set<string>();
|
||||
const sounds = new Set<string>();
|
||||
@ -676,19 +672,19 @@ function loadAnimAssets(scene: BattleScene, anims: AnimConfig[], startLoad?: boo
|
||||
backgrounds.add(abg);
|
||||
}
|
||||
if (a.graphic) {
|
||||
scene.loadSpritesheet(a.graphic, "battle_anims", 96);
|
||||
globalScene.loadSpritesheet(a.graphic, "battle_anims", 96);
|
||||
}
|
||||
}
|
||||
for (const bg of backgrounds) {
|
||||
scene.loadImage(bg, "battle_anims");
|
||||
globalScene.loadImage(bg, "battle_anims");
|
||||
}
|
||||
for (const s of sounds) {
|
||||
scene.loadSe(s, "battle_anims", s);
|
||||
globalScene.loadSe(s, "battle_anims", s);
|
||||
}
|
||||
if (startLoad) {
|
||||
scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve());
|
||||
if (!scene.load.isLoading()) {
|
||||
scene.load.start();
|
||||
globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve());
|
||||
if (!globalScene.load.isLoading()) {
|
||||
globalScene.load.start();
|
||||
}
|
||||
} else {
|
||||
resolve();
|
||||
@ -778,7 +774,7 @@ export abstract class BattleAnim {
|
||||
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([
|
||||
[ AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
|
||||
[ AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
|
||||
@ -835,7 +831,7 @@ export abstract class BattleAnim {
|
||||
return ret;
|
||||
}
|
||||
|
||||
play(scene: BattleScene, onSubstitute?: boolean, callback?: Function) {
|
||||
play(onSubstitute?: boolean, callback?: Function) {
|
||||
const isOppAnim = this.isOppAnim();
|
||||
const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
|
||||
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();
|
||||
}
|
||||
|
||||
@ -924,7 +920,7 @@ export abstract class BattleAnim {
|
||||
let r = anim?.frames.length ?? 0;
|
||||
let f = 0;
|
||||
|
||||
scene.tweens.addCounter({
|
||||
globalScene.tweens.addCounter({
|
||||
duration: Utils.getFrameMs(3),
|
||||
repeat: anim?.frames.length ?? 0,
|
||||
onRepeat: () => {
|
||||
@ -934,7 +930,7 @@ export abstract class BattleAnim {
|
||||
}
|
||||
|
||||
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 t = 0;
|
||||
let g = 0;
|
||||
@ -950,19 +946,19 @@ export abstract class BattleAnim {
|
||||
const spriteSource = isUser ? userSprite : targetSprite;
|
||||
if ((isUser ? u : t) === sprites.length) {
|
||||
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?
|
||||
sprite.setPipelineData("spriteKey", (isUser ? user! : target).getBattleSpriteKey());
|
||||
sprite.setPipelineData("shiny", (isUser ? user : target).shiny);
|
||||
sprite.setPipelineData("variant", (isUser ? user : target).variant);
|
||||
sprite.setPipelineData("ignoreFieldPos", true);
|
||||
spriteSource.on("animationupdate", (_anim, frame) => sprite.setFrame(frame.textureFrame));
|
||||
scene.field.add(sprite);
|
||||
globalScene.field.add(sprite);
|
||||
sprites.push(sprite);
|
||||
} 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));
|
||||
scene.field.add(sprite);
|
||||
globalScene.field.add(sprite);
|
||||
sprites.push(sprite);
|
||||
}
|
||||
}
|
||||
@ -987,9 +983,9 @@ export abstract class BattleAnim {
|
||||
} else {
|
||||
const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
|
||||
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);
|
||||
scene.field.add(newSprite);
|
||||
globalScene.field.add(newSprite);
|
||||
spritePriorities.push(1);
|
||||
}
|
||||
|
||||
@ -1000,22 +996,22 @@ export abstract class BattleAnim {
|
||||
const setSpritePriority = (priority: integer) => {
|
||||
switch (priority) {
|
||||
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;
|
||||
case 1:
|
||||
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);
|
||||
globalScene.field.moveTo(moveSprite, globalScene.field.getAll().length - 1);
|
||||
break;
|
||||
case 2:
|
||||
switch (frame.focus) {
|
||||
case AnimFocus.USER:
|
||||
if (this.bgSprite) {
|
||||
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite);
|
||||
globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite);
|
||||
} 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;
|
||||
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;
|
||||
default:
|
||||
setSpritePriority(1);
|
||||
@ -1025,10 +1021,10 @@ export abstract class BattleAnim {
|
||||
case 3:
|
||||
switch (frame.focus) {
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
setSpritePriority(1);
|
||||
@ -1056,7 +1052,7 @@ export abstract class BattleAnim {
|
||||
}
|
||||
if (anim?.frameTimedEvents.has(f)) {
|
||||
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);
|
||||
@ -1086,7 +1082,7 @@ export abstract class BattleAnim {
|
||||
}
|
||||
}
|
||||
if (r) {
|
||||
scene.tweens.addCounter({
|
||||
globalScene.tweens.addCounter({
|
||||
duration: Utils.getFrameMs(r),
|
||||
onComplete: () => cleanUpAndComplete()
|
||||
});
|
||||
@ -1123,8 +1119,6 @@ export abstract class BattleAnim {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param scene
|
||||
* @param targetInitialX
|
||||
* @param targetInitialY
|
||||
* @param frameTimeMult
|
||||
@ -1135,7 +1129,7 @@ export abstract class BattleAnim {
|
||||
* - 5 is on top of player sprite
|
||||
* @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 = {
|
||||
[AnimFrameTarget.GRAPHIC]: [],
|
||||
[AnimFrameTarget.USER]: [],
|
||||
@ -1156,7 +1150,7 @@ export abstract class BattleAnim {
|
||||
}
|
||||
};
|
||||
|
||||
if (!scene.moveAnimations && !this.playRegardlessOfIssues) {
|
||||
if (!globalScene.moveAnimations && !this.playRegardlessOfIssues) {
|
||||
return cleanUpAndComplete();
|
||||
}
|
||||
|
||||
@ -1168,13 +1162,13 @@ export abstract class BattleAnim {
|
||||
let totalFrames = anim!.frames.length;
|
||||
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,
|
||||
repeat: anim!.frames.length,
|
||||
onRepeat: () => {
|
||||
existingFieldSprites = scene.field.getAll().slice(0);
|
||||
existingFieldSprites = globalScene.field.getAll().slice(0);
|
||||
const spriteFrames = anim!.frames[frameCount];
|
||||
const frameData = this.getGraphicFrameDataWithoutTarget(anim!.frames[frameCount], targetInitialX, targetInitialY);
|
||||
let graphicFrameCount = 0;
|
||||
@ -1186,9 +1180,9 @@ export abstract class BattleAnim {
|
||||
|
||||
const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
|
||||
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);
|
||||
scene.field.add(newSprite);
|
||||
globalScene.field.add(newSprite);
|
||||
}
|
||||
|
||||
const graphicIndex = graphicFrameCount++;
|
||||
@ -1197,11 +1191,11 @@ export abstract class BattleAnim {
|
||||
const setSpritePriority = (priority: integer) => {
|
||||
if (existingFieldSprites.length > priority) {
|
||||
// Move to specified priority index
|
||||
const index = scene.field.getIndex(existingFieldSprites[priority]);
|
||||
scene.field.moveTo(moveSprite, index);
|
||||
const index = globalScene.field.getIndex(existingFieldSprites[priority]);
|
||||
globalScene.field.moveTo(moveSprite, index);
|
||||
} else {
|
||||
// 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);
|
||||
@ -1221,7 +1215,7 @@ export abstract class BattleAnim {
|
||||
}
|
||||
if (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);
|
||||
@ -1248,7 +1242,7 @@ export abstract class BattleAnim {
|
||||
}
|
||||
}
|
||||
if (totalFrames) {
|
||||
scene.tweens.addCounter({
|
||||
globalScene.tweens.addCounter({
|
||||
duration: Utils.getFrameMs(totalFrames),
|
||||
onComplete: () => cleanUpAndComplete()
|
||||
});
|
||||
@ -1282,7 +1276,7 @@ export class MoveAnim extends BattleAnim {
|
||||
public move: Moves;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
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 * as Utils from "../utils";
|
||||
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 { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
export function getBerryName(berryType: BerryType): string {
|
||||
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));
|
||||
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));
|
||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false);
|
||||
};
|
||||
@ -83,7 +85,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
pokemon.battleData.berriesEaten.push(berryType);
|
||||
}
|
||||
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.updateInfo();
|
||||
@ -102,7 +104,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||
const statStages = new Utils.NumberHolder(1);
|
||||
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);
|
||||
};
|
||||
case BerryType.LANSAT:
|
||||
@ -121,7 +123,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK);
|
||||
const stages = new Utils.NumberHolder(2);
|
||||
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);
|
||||
};
|
||||
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?
|
||||
if (ppRestoreMove !== undefined) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,18 +1,22 @@
|
||||
import * as Utils from "#app/utils";
|
||||
import i18next from "i18next";
|
||||
import { defaultStarterSpecies, DexAttrProps, GameData } from "#app/system/game-data";
|
||||
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import type { DexAttrProps, GameData } from "#app/system/game-data";
|
||||
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 Pokemon, { PokemonMove } from "#app/field/pokemon";
|
||||
import { BattleType, FixedBattleConfig } from "#app/battle";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import type { FixedBattleConfig } from "#app/battle";
|
||||
import { BattleType } from "#app/battle";
|
||||
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 { Challenges } from "#enums/challenges";
|
||||
import { Species } from "#enums/species";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { Moves } from "#enums/moves";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import { TypeColor, TypeShadow } from "#enums/color";
|
||||
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||
@ -84,6 +88,11 @@ export enum ChallengeType {
|
||||
* Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED.
|
||||
*/
|
||||
MOVE_WEIGHT,
|
||||
/**
|
||||
* Modifies what the pokemon stats for Flip Stat Mode.
|
||||
*/
|
||||
FLIP_STAT,
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -401,6 +410,16 @@ export abstract class Challenge {
|
||||
applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean {
|
||||
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;
|
||||
@ -467,7 +486,7 @@ export class SingleGenerationChallenge extends Challenge {
|
||||
if (trainerTypes.length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(scene => new Trainer(scene, trainerTypes[this.value - 1], TrainerVariant.DEFAULT));
|
||||
battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(() => new Trainer(trainerTypes[this.value - 1], TrainerVariant.DEFAULT));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -701,6 +720,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.
|
||||
*/
|
||||
@ -886,6 +932,9 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
||||
* @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.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;
|
||||
|
||||
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType, ...args: any[]): boolean {
|
||||
let ret = false;
|
||||
gameMode.challenges.forEach(c => {
|
||||
@ -930,6 +979,9 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
||||
case ChallengeType.MOVE_WEIGHT:
|
||||
ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]);
|
||||
break;
|
||||
case ChallengeType.FLIP_STAT:
|
||||
ret ||= c.applyFlipStat(args[0], args[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -955,6 +1007,8 @@ export function copyChallenge(source: Challenge | any): Challenge {
|
||||
return FreshStartChallenge.loadChallenge(source);
|
||||
case Challenges.INVERSE_BATTLE:
|
||||
return InverseBattleChallenge.loadChallenge(source);
|
||||
case Challenges.FLIP_STAT:
|
||||
return FlipStatChallenge.loadChallenge(source);
|
||||
}
|
||||
throw new Error("Unknown challenge copied");
|
||||
}
|
||||
@ -967,5 +1021,6 @@ export function initChallenges() {
|
||||
new SingleTypeChallenge(),
|
||||
new FreshStartChallenge(),
|
||||
new InverseBattleChallenge(),
|
||||
new FlipStatChallenge()
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Type } from "#enums/type";
|
||||
import type { Abilities } from "#enums/abilities";
|
||||
import type { Type } from "#enums/type";
|
||||
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
|
||||
* 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 {
|
||||
public spriteScale: number;
|
||||
@ -13,6 +14,8 @@ export class CustomPokemonData {
|
||||
public passive: Abilities | -1;
|
||||
public nature: Nature | -1;
|
||||
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>) {
|
||||
if (!isNullOrUndefined(data)) {
|
||||
@ -24,5 +27,10 @@ export class CustomPokemonData {
|
||||
this.passive = this.passive ?? -1;
|
||||
this.nature = this.nature ?? -1;
|
||||
this.types = this.types ?? [];
|
||||
this.hitsRecCount = this.hitsRecCount ?? 0;
|
||||
}
|
||||
|
||||
resetHitReceivedCount(): void {
|
||||
this.hitsRecCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import { Species } from "#enums/species";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import type { Species } from "#enums/species";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 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 { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||
import { Biome } from "#app/enums/biome";
|
||||
|
||||
export interface DailyRunConfig {
|
||||
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[] = [];
|
||||
|
||||
scene.executeWithSeedOffset(() => {
|
||||
const startingLevel = scene.gameMode.getStartingLevel();
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
const startingLevel = globalScene.gameMode.getStartingLevel();
|
||||
|
||||
if (/\d{18}$/.test(seed)) {
|
||||
for (let s = 0; s < 3; s++) {
|
||||
const offset = 6 + s * 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;
|
||||
}
|
||||
@ -48,17 +50,17 @@ export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[]
|
||||
.filter(s => speciesStarterCosts[s] === cost);
|
||||
const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies));
|
||||
const starterSpecies = getPokemonSpecies(randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER));
|
||||
starters.push(getDailyRunStarter(scene, starterSpecies, startingLevel));
|
||||
starters.push(getDailyRunStarter(starterSpecies, startingLevel));
|
||||
}
|
||||
}, 0, seed);
|
||||
|
||||
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 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 = {
|
||||
species: starterSpecies,
|
||||
dexAttr: pokemon.getDexAttr(),
|
||||
@ -70,3 +72,76 @@ function getDailyRunStarter(scene: BattleScene, starterSpeciesForm: PokemonSpeci
|
||||
pokemon.destroy();
|
||||
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)];
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { DexEntry, StarterDataEntry } from "#app/system/game-data";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { DexEntry, StarterDataEntry } from "#app/system/game-data";
|
||||
|
||||
/**
|
||||
* Stores data associated with a specific egg and the hatched pokemon
|
||||
@ -17,11 +17,8 @@ export class EggHatchData {
|
||||
public dexEntryBeforeUpdate: DexEntry;
|
||||
/** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */
|
||||
public starterDataEntryBeforeUpdate: StarterDataEntry;
|
||||
/** reference to the battle scene to get gamedata and update dex */
|
||||
private scene: BattleScene;
|
||||
|
||||
constructor(scene: BattleScene, pokemon: PlayerPokemon, eggMoveIndex: number) {
|
||||
this.scene = scene;
|
||||
constructor(pokemon: PlayerPokemon, eggMoveIndex: number) {
|
||||
this.pokemon = pokemon;
|
||||
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
|
||||
*/
|
||||
setDex() {
|
||||
const currDexEntry = this.scene.gameData.dexData[this.pokemon.species.speciesId];
|
||||
const currStarterDataEntry = this.scene.gameData.starterData[this.pokemon.species.getRootSpeciesId()];
|
||||
const currDexEntry = globalScene.gameData.dexData[this.pokemon.species.speciesId];
|
||||
const currStarterDataEntry = globalScene.gameData.starterData[this.pokemon.species.getRootSpeciesId()];
|
||||
this.dexEntryBeforeUpdate = {
|
||||
seenAttr: currDexEntry.seenAttr,
|
||||
caughtAttr: currDexEntry.caughtAttr,
|
||||
@ -86,9 +83,9 @@ export class EggHatchData {
|
||||
*/
|
||||
updatePokemon(showMessage : boolean = false) {
|
||||
return new Promise<void>(resolve => {
|
||||
this.scene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => {
|
||||
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
||||
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => {
|
||||
globalScene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => {
|
||||
globalScene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
||||
globalScene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => {
|
||||
this.setEggMoveUnlocked(value);
|
||||
resolve();
|
||||
});
|
||||
|
||||
110
src/data/egg.ts
@ -1,11 +1,13 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import type BattleScene from "#app/battle-scene";
|
||||
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 { VariantTier } from "#enums/variant-tier";
|
||||
import * as Utils from "#app/utils";
|
||||
import Overrides from "#app/overrides";
|
||||
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 { EggTier } from "#enums/egg-type";
|
||||
import { Species } from "#enums/species";
|
||||
@ -22,9 +24,8 @@ export interface IEggOptions {
|
||||
/** Timestamp when this egg got created */
|
||||
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.
|
||||
* NEEDS `scene` `eggOption` to work.
|
||||
*/
|
||||
pulled?: boolean;
|
||||
/**
|
||||
@ -32,7 +33,7 @@ export interface IEggOptions {
|
||||
* Will also define the text displayed in the egg list.
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* 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;
|
||||
/** Sets how many waves it will take till this egg hatches. */
|
||||
hatchWaves?: number;
|
||||
/**
|
||||
* Sets the exact species that will hatch from this egg.
|
||||
* Needs `scene` `eggOption` if not provided.
|
||||
*/
|
||||
/** Sets the exact species that will hatch from this egg. */
|
||||
species?: Species;
|
||||
/** Defines if the hatched pokemon will be a shiny. */
|
||||
isShiny?: boolean;
|
||||
@ -56,8 +54,7 @@ export interface IEggOptions {
|
||||
* Defines if the egg will hatch with the hidden ability of this species.
|
||||
* 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 */
|
||||
eggDescriptor?: string;
|
||||
}
|
||||
@ -148,7 +145,7 @@ export class Egg {
|
||||
// If egg was pulled, check if egg pity needs to override the egg tier
|
||||
if (eggOptions?.pulled) {
|
||||
// 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);
|
||||
@ -160,7 +157,7 @@ export class Egg {
|
||||
// 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._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;
|
||||
|
||||
@ -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
|
||||
this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex();
|
||||
if (eggOptions?.pulled) {
|
||||
this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct?
|
||||
this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct?
|
||||
this.increasePullStatistic();
|
||||
this.addEggToGameData();
|
||||
}
|
||||
};
|
||||
|
||||
if (eggOptions?.scene) {
|
||||
const seedOverride = Utils.randomString(24);
|
||||
eggOptions?.scene.executeWithSeedOffset(() => {
|
||||
generateEggProperties(eggOptions);
|
||||
}, 0, seedOverride);
|
||||
} else { // For legacy eggs without scene
|
||||
const seedOverride = Utils.randomString(24);
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
generateEggProperties(eggOptions);
|
||||
}
|
||||
}, 0, seedOverride);
|
||||
|
||||
this._eggDescriptor = eggOptions?.eggDescriptor;
|
||||
}
|
||||
@ -212,14 +205,14 @@ export class Egg {
|
||||
}
|
||||
|
||||
// Generates a PlayerPokemon from an egg
|
||||
public generatePlayerPokemon(scene: BattleScene): PlayerPokemon {
|
||||
public generatePlayerPokemon(): PlayerPokemon {
|
||||
let ret: PlayerPokemon;
|
||||
|
||||
const generatePlayerPokemonHelper = (scene: BattleScene) => {
|
||||
const generatePlayerPokemonHelper = () => {
|
||||
// Legacy egg wants to hatch. Generate missing properties
|
||||
if (!this._species) {
|
||||
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);
|
||||
@ -238,7 +231,7 @@ export class Egg {
|
||||
}
|
||||
|
||||
// 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.variant = this._variantTier;
|
||||
|
||||
@ -250,16 +243,16 @@ export class Egg {
|
||||
};
|
||||
|
||||
ret = ret!; // Tell TS compiler it's defined now
|
||||
scene.executeWithSeedOffset(() => {
|
||||
generatePlayerPokemonHelper(scene);
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
generatePlayerPokemonHelper();
|
||||
}, this._id, EGG_SEED.toString());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Doesn't need to be called if the egg got pulled by a gacha machiene
|
||||
public addEggToGameData(scene: BattleScene): void {
|
||||
scene.gameData.eggs.push(this);
|
||||
public addEggToGameData(): void {
|
||||
globalScene.gameData.eggs.push(this);
|
||||
}
|
||||
|
||||
public getEggDescriptor(): string {
|
||||
@ -291,12 +284,12 @@ export class Egg {
|
||||
return i18next.t("egg:hatchWavesMessageLongTime");
|
||||
}
|
||||
|
||||
public getEggTypeDescriptor(scene: BattleScene): string {
|
||||
public getEggTypeDescriptor(): string {
|
||||
switch (this.sourceType) {
|
||||
case EggSourceType.SAME_SPECIES_EGG:
|
||||
return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName() });
|
||||
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:
|
||||
return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny");
|
||||
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;
|
||||
}
|
||||
|
||||
private rollSpecies(scene: BattleScene): Species | null {
|
||||
if (!scene) {
|
||||
private rollSpecies(): Species | null {
|
||||
if (!globalScene) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
@ -376,7 +369,7 @@ export class Egg {
|
||||
} else if (this.tier === EggTier.LEGENDARY
|
||||
&& this._sourceType === EggSourceType.GACHA_LEGENDARY) {
|
||||
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);
|
||||
|
||||
// If this is the 10th egg without unlocking something new, attempt to force it.
|
||||
if (scene.gameData.unlockPity[this.tier] >= 9) {
|
||||
const lockedPool = speciesPool.filter(s => !scene.gameData.dexData[s].caughtAttr && !scene.gameData.eggs.some(e => e.species === s));
|
||||
if (globalScene.gameData.unlockPity[this.tier] >= 9) {
|
||||
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
|
||||
speciesPool = lockedPool;
|
||||
}
|
||||
@ -454,10 +447,10 @@ export class Egg {
|
||||
}
|
||||
species = species!; // tell TS compiled it's defined now!
|
||||
|
||||
if (!!scene.gameData.dexData[species].caughtAttr || scene.gameData.eggs.some(e => e.species === species)) {
|
||||
scene.gameData.unlockPity[this.tier] = Math.min(scene.gameData.unlockPity[this.tier] + 1, 10);
|
||||
if (globalScene.gameData.dexData[species].caughtAttr || globalScene.gameData.eggs.some(e => e.species === species)) {
|
||||
globalScene.gameData.unlockPity[this.tier] = Math.min(globalScene.gameData.unlockPity[this.tier] + 1, 10);
|
||||
} else {
|
||||
scene.gameData.unlockPity[this.tier] = 0;
|
||||
globalScene.gameData.unlockPity[this.tier] = 0;
|
||||
}
|
||||
|
||||
return species;
|
||||
@ -465,7 +458,7 @@ export class Egg {
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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
|
||||
// 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 {
|
||||
if (!this.isShiny) {
|
||||
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;
|
||||
scene.gameData.eggPity[EggTier.RARE] += 1;
|
||||
scene.gameData.eggPity[EggTier.EPIC] += 1;
|
||||
scene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset;
|
||||
globalScene.gameData.eggPity[EggTier.RARE] += 1;
|
||||
globalScene.gameData.eggPity[EggTier.EPIC] += 1;
|
||||
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.
|
||||
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;
|
||||
} 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;
|
||||
} 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;
|
||||
}
|
||||
scene.gameData.eggPity[this._tier] = 0;
|
||||
globalScene.gameData.eggPity[this._tier] = 0;
|
||||
}
|
||||
|
||||
private increasePullStatistic(scene: BattleScene): void {
|
||||
scene.gameData.gameStats.eggsPulled++;
|
||||
private increasePullStatistic(): void {
|
||||
globalScene.gameData.gameStats.eggsPulled++;
|
||||
if (this.isManaphyEgg()) {
|
||||
scene.gameData.gameStats.manaphyEggsPulled++;
|
||||
globalScene.gameData.gameStats.manaphyEggsPulled++;
|
||||
this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC);
|
||||
return;
|
||||
}
|
||||
switch (this.tier) {
|
||||
case EggTier.RARE:
|
||||
scene.gameData.gameStats.rareEggsPulled++;
|
||||
globalScene.gameData.gameStats.rareEggsPulled++;
|
||||
break;
|
||||
case EggTier.EPIC:
|
||||
scene.gameData.gameStats.epicEggsPulled++;
|
||||
globalScene.gameData.gameStats.epicEggsPulled++;
|
||||
break;
|
||||
case EggTier.LEGENDARY:
|
||||
scene.gameData.gameStats.legendaryEggsPulled++;
|
||||
globalScene.gameData.gameStats.legendaryEggsPulled++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -552,7 +546,7 @@ export function getValidLegendaryGachaSpecies() : Species[] {
|
||||
.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();
|
||||
|
||||
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 index = Math.floor(dayTimestamp / 86400000) % legendarySpecies.length; // Index within cycle
|
||||
|
||||
scene.executeWithSeedOffset(() => {
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
ret = Phaser.Math.RND.shuffle(legendarySpecies)[index];
|
||||
}, offset, EGG_SEED.toString());
|
||||
ret = ret!; // tell TS compiler it's
|
||||
|
||||
1503
src/data/move.ts
@ -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 { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-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 { TrainerType } from "#enums/trainer-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { randSeedInt } from "#app/utils";
|
||||
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 { EggTier } from "#enums/egg-type";
|
||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
@ -36,8 +38,8 @@ export const ATrainersTestEncounter: MysteryEncounter =
|
||||
},
|
||||
])
|
||||
.withAutoHideIntroVisuals(false)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Randomly pick from 1 of the 5 stat trainers to spawn
|
||||
let trainerType: TrainerType;
|
||||
@ -138,23 +140,22 @@ export const ATrainersTestEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}:option.1.label`,
|
||||
buttonTooltip: `${namespace}:option.1.tooltip`
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Battle the stat trainer for an Egg and great rewards
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
|
||||
const eggOptions: IEggOptions = {
|
||||
scene,
|
||||
pulled: false,
|
||||
sourceType: EggSourceType.EVENT,
|
||||
eggDescriptor: encounter.misc.trainerEggDescription,
|
||||
tier: EggTier.EPIC
|
||||
};
|
||||
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`));
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]);
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
@ -162,21 +163,20 @@ export const ATrainersTestEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}:option.2.label`,
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Full heal party
|
||||
scene.unshiftPhase(new PartyHealPhase(scene, true));
|
||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
||||
|
||||
const eggOptions: IEggOptions = {
|
||||
scene,
|
||||
pulled: false,
|
||||
sourceType: EggSourceType.EVENT,
|
||||
eggDescriptor: encounter.misc.trainerEggDescription,
|
||||
tier: EggTier.RARE
|
||||
};
|
||||
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`));
|
||||
setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]);
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
)
|
||||
.withOutroDialogue([
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
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 { Species } from "#enums/species";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
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 { TrainerSlot } from "#app/data/trainer-config";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import type { BerryType } from "#enums/berry-type";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
@ -170,18 +174,18 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
scene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav");
|
||||
scene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3");
|
||||
globalScene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav");
|
||||
globalScene.loadSe("Follow Me", "battle_anims", "Follow Me.mp3");
|
||||
|
||||
// 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
|
||||
const berryItemsMap = new Map<number, BerryModifier[]>();
|
||||
scene.getPlayerParty().forEach(pokemon => {
|
||||
globalScene.getPlayerParty().forEach(pokemon => {
|
||||
const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id);
|
||||
if (pokemonBerries?.length > 0) {
|
||||
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
|
||||
// Overflow berries will be "lost" on the boss, but it's un-catchable anyway
|
||||
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 });
|
||||
}
|
||||
});
|
||||
@ -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
|
||||
|
||||
// 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.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
|
||||
|
||||
@ -221,8 +225,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||
modifierConfigs: bossModifierConfigs,
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.boss_enraged`);
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -233,18 +237,18 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||
|
||||
return true;
|
||||
})
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
doGreedentSpriteSteal(scene);
|
||||
doBerrySpritePile(scene);
|
||||
.withOnVisualsStart(() => {
|
||||
doGreedentSpriteSteal();
|
||||
doBerrySpritePile();
|
||||
|
||||
// Remove the berries from the party
|
||||
// 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 => {
|
||||
scene.removeModifier(berryMod);
|
||||
globalScene.removeModifier(berryMod);
|
||||
});
|
||||
|
||||
scene.updateModifiers(true);
|
||||
globalScene.updateModifiers(true);
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -260,26 +264,26 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Pick battle
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// 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"));
|
||||
const givePartyPokemonReviverSeeds = () => {
|
||||
const party = scene.getPlayerParty();
|
||||
const party = globalScene.getPlayerParty();
|
||||
party.forEach(p => {
|
||||
const heldItems = p.getHeldItems();
|
||||
if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) {
|
||||
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({
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [ BattlerIndex.ENEMY ],
|
||||
@ -287,8 +291,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||
ignorePp: true
|
||||
});
|
||||
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -304,12 +308,12 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const berryMap = encounter.misc.berryItemsMap;
|
||||
|
||||
// Returns 2/5 of the berries stolen to each Pokemon
|
||||
const party = scene.getPlayerParty();
|
||||
const party = globalScene.getPlayerParty();
|
||||
party.forEach(pokemon => {
|
||||
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
|
||||
const berryTypesAsArray: BerryType[] = [];
|
||||
@ -322,15 +326,15 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||
Phaser.Math.RND.shuffle(berryTypesAsArray);
|
||||
const randBerryType = berryTypesAsArray.pop();
|
||||
|
||||
const berryModType = generateModifierType(scene, modifierTypes.BERRY, [ randBerryType ]) as BerryModifierType;
|
||||
applyModifierTypeToPlayerPokemon(scene, pokemon, berryModType);
|
||||
const berryModType = generateModifierType(modifierTypes.BERRY, [ randBerryType ]) as BerryModifierType;
|
||||
applyModifierTypeToPlayerPokemon(pokemon, berryModType);
|
||||
}
|
||||
}
|
||||
});
|
||||
await scene.updateModifiers(true);
|
||||
await globalScene.updateModifiers(true);
|
||||
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -346,36 +350,36 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Animate berries being eaten
|
||||
doGreedentEatBerries(scene);
|
||||
doBerrySpritePile(scene, true);
|
||||
doGreedentEatBerries();
|
||||
doBerrySpritePile(true);
|
||||
return true;
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Let it have the food
|
||||
// Greedent joins the team, level equal to 2 below highest party member (shiny locked)
|
||||
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2;
|
||||
const greedent = new EnemyPokemon(scene, getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false, true);
|
||||
const level = getHighestLevelPlayerPokemon(false, true).level - 2;
|
||||
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.passive = true;
|
||||
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||
await catchPokemon(scene, greedent, null, PokeballType.POKEBALL, false);
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||
await catchPokemon(greedent, null, PokeballType.POKEBALL, false);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
function doGreedentSpriteSteal(scene: BattleScene) {
|
||||
function doGreedentSpriteSteal() {
|
||||
const shakeDelay = 50;
|
||||
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");
|
||||
scene.tweens.chain({
|
||||
globalScene.playSound("battle_anims/Follow Me");
|
||||
globalScene.tweens.chain({
|
||||
targets: greedentSprites,
|
||||
tweens: [
|
||||
{ // Slide Greedent diagonally
|
||||
@ -445,10 +449,10 @@ function doGreedentSpriteSteal(scene: BattleScene) {
|
||||
});
|
||||
}
|
||||
|
||||
function doGreedentEatBerries(scene: BattleScene) {
|
||||
const greedentSprites = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1);
|
||||
function doGreedentEatBerries() {
|
||||
const greedentSprites = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1);
|
||||
let index = 1;
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: greedentSprites,
|
||||
duration: 150,
|
||||
ease: "Cubic.easeOut",
|
||||
@ -456,11 +460,11 @@ function doGreedentEatBerries(scene: BattleScene) {
|
||||
y: "-=8",
|
||||
loop: 5,
|
||||
onStart: () => {
|
||||
scene.playSound("battle_anims/PRSFX- Bug Bite");
|
||||
globalScene.playSound("battle_anims/PRSFX- Bug Bite");
|
||||
},
|
||||
onLoop: () => {
|
||||
if (index % 2 === 0) {
|
||||
scene.playSound("battle_anims/PRSFX- Bug Bite");
|
||||
globalScene.playSound("battle_anims/PRSFX- Bug Bite");
|
||||
}
|
||||
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.
|
||||
*/
|
||||
function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
|
||||
function doBerrySpritePile(isEat: boolean = false) {
|
||||
const berryAddDelay = 150;
|
||||
let animationOrder = [ "starf", "sitrus", "lansat", "salac", "apicot", "enigma", "liechi", "ganlon", "lum", "petaya", "leppa" ];
|
||||
if (isEat) {
|
||||
animationOrder = animationOrder.reverse();
|
||||
}
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
animationOrder.forEach((berry, i) => {
|
||||
const introVisualsIndex = encounter.spriteConfigs.findIndex(config => config.spriteKey?.includes(berry));
|
||||
let sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite;
|
||||
@ -487,7 +489,7 @@ function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
|
||||
sprite = sprites[0];
|
||||
tintSprite = sprites[1];
|
||||
}
|
||||
scene.time.delayedCall(berryAddDelay * i + 400, () => {
|
||||
globalScene.time.delayedCall(berryAddDelay * i + 400, () => {
|
||||
if (sprite) {
|
||||
sprite.setVisible(!isEat);
|
||||
}
|
||||
@ -497,20 +499,20 @@ function doBerrySpritePile(scene: BattleScene, isEat: boolean = false) {
|
||||
|
||||
// Animate Petaya berry falling off the pile
|
||||
if (berry === "petaya" && sprite && tintSprite && !isEat) {
|
||||
scene.time.delayedCall(200, () => {
|
||||
doBerryBounce(scene, [ sprite, tintSprite ], 30, 500);
|
||||
globalScene.time.delayedCall(200, () => {
|
||||
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 bounceYOffset = yd;
|
||||
|
||||
const doBounce = () => {
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: berrySprites,
|
||||
y: "+=" + bounceYOffset,
|
||||
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },
|
||||
@ -522,7 +524,7 @@ function doBerryBounce(scene: BattleScene, berrySprites: Phaser.GameObjects.Spri
|
||||
if (bouncePower) {
|
||||
bounceYOffset = bounceYOffset * bouncePower;
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: berrySprites,
|
||||
y: "-=" + bounceYOffset,
|
||||
x: { value: "+=" + (bouncePower * bouncePower * 10), ease: "Linear" },
|
||||
|
||||
@ -2,8 +2,9 @@ import { generateModifierType, leaveEncounterWithoutBattle, setEncounterExp, upd
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { AbilityRequirement, CombinationPokemonRequirement, MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
@ -69,14 +70,14 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const pokemon = getHighestStatTotalPlayerPokemon(scene, true, true);
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const pokemon = getHighestStatTotalPlayerPokemon(true, true);
|
||||
|
||||
const baseSpecies = pokemon.getSpeciesForm().getRootSpeciesId();
|
||||
const starterValue: number = speciesStarterCosts[baseSpecies] ?? 1;
|
||||
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("price", price.toString());
|
||||
@ -89,7 +90,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
|
||||
|
||||
// If player meets the combo OR requirements for option 2, populate the token
|
||||
const opt2Req = encounter.options[1].primaryPokemonRequirements[0];
|
||||
if (opt2Req.meetsRequirement(scene)) {
|
||||
if (opt2Req.meetsRequirement()) {
|
||||
const abilityToken = encounter.dialogueTokens["option2PrimaryAbility"];
|
||||
const moveToken = encounter.dialogueTokens["option2PrimaryMove"];
|
||||
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("liepardName", getPokemonSpecies(Species.LIEPARD).getName());
|
||||
|
||||
@ -118,17 +119,17 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Update money and remove pokemon from party
|
||||
updatePlayerMoney(scene, encounter.misc.price);
|
||||
scene.removePokemonFromPlayerParty(encounter.misc.pokemon);
|
||||
updatePlayerMoney(encounter.misc.price);
|
||||
globalScene.removePokemonFromPlayerParty(encounter.misc.pokemon);
|
||||
return true;
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Give the player a Shiny Charm
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.SHINY_CHARM));
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM));
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -152,15 +153,15 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Extort the rich kid for money
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// 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()
|
||||
)
|
||||
@ -175,9 +176,9 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,23 +1,32 @@
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import type {
|
||||
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import {
|
||||
EnemyPartyConfig, generateModifierType, generateModifierTypeOption,
|
||||
generateModifierType,
|
||||
generateModifierTypeOption,
|
||||
initBattleWithEnemyConfig,
|
||||
leaveEncounterWithoutBattle, setEncounterExp,
|
||||
leaveEncounterWithoutBattle,
|
||||
setEncounterExp,
|
||||
setEncounterRewards
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import {
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import type {
|
||||
BerryModifierType,
|
||||
ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||
import {
|
||||
getPartyLuckValue,
|
||||
ModifierPoolType,
|
||||
ModifierTypeOption, modifierTypes,
|
||||
modifierTypes,
|
||||
regenerateModifierPoolThresholds,
|
||||
} from "#app/modifier/modifier-type";
|
||||
import { randSeedInt, randSeedItem } from "#app/utils";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
@ -31,7 +40,8 @@ import { BerryType } from "#enums/berry-type";
|
||||
import { PERMANENT_STATS, Stat } from "#enums/stat";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounters/berriesAbound";
|
||||
@ -54,20 +64,20 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mon
|
||||
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
let bossSpecies: PokemonSpecies;
|
||||
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);
|
||||
if (globalScene.eventManager.isEventActive() && globalScene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
|
||||
const eventEncounter = randSeedItem(globalScene.eventManager.activeEvent()!.uncommonBreedEncounters!);
|
||||
const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, globalScene.gameMode);
|
||||
bossSpecies = getPokemonSpecies( levelSpecies );
|
||||
} else {
|
||||
bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
||||
bossSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
|
||||
}
|
||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||
const bossPokemon = new EnemyPokemon(bossSpecies, level, TrainerSlot.NONE, true);
|
||||
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [{
|
||||
@ -82,10 +92,10 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
// Calculate the number of extra berries that player receives
|
||||
// 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7
|
||||
const numBerries =
|
||||
scene.currentBattle.waveIndex > 160 ? 7
|
||||
: scene.currentBattle.waveIndex > 120 ? 5
|
||||
: scene.currentBattle.waveIndex > 40 ? 4 : 2;
|
||||
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
|
||||
globalScene.currentBattle.waveIndex > 160 ? 7
|
||||
: globalScene.currentBattle.waveIndex > 120 ? 5
|
||||
: globalScene.currentBattle.waveIndex > 40 ? 4 : 2;
|
||||
regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
|
||||
encounter.misc = { numBerries };
|
||||
|
||||
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
|
||||
@ -113,7 +123,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
];
|
||||
|
||||
// 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.enemySpeed = bossPokemon.getStat(Stat.SPD);
|
||||
encounter.setDialogueToken("fastestPokemon", fastestPokemon.getNameToRender());
|
||||
@ -134,34 +144,34 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Pick battle
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const numBerries = encounter.misc.numBerries;
|
||||
|
||||
const doBerryRewards = () => {
|
||||
const berryText = i18next.t(`${namespace}:berries`);
|
||||
|
||||
scene.playSound("item_fanfare");
|
||||
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
|
||||
globalScene.playSound("item_fanfare");
|
||||
queueEncounterMessage(i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
|
||||
|
||||
// Generate a random berry and give it to the first Pokemon with room for it
|
||||
for (let i = 0; i < numBerries; i++) {
|
||||
tryGiveBerry(scene);
|
||||
tryGiveBerry();
|
||||
}
|
||||
};
|
||||
|
||||
const shopOptions: ModifierTypeOption[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
// Generate shop berries
|
||||
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY);
|
||||
const mod = generateModifierTypeOption(modifierTypes.BERRY);
|
||||
if (mod) {
|
||||
shopOptions.push(mod);
|
||||
}
|
||||
}
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
|
||||
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
|
||||
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
||||
}
|
||||
)
|
||||
.withOption(
|
||||
@ -171,9 +181,9 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}:option.2.label`,
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Pick race for berries
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const fastestPokemon: PlayerPokemon = encounter.misc.fastestPokemon;
|
||||
const enemySpeed: number = encounter.misc.enemySpeed;
|
||||
const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1);
|
||||
@ -182,7 +192,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
const shopOptions: ModifierTypeOption[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
// Generate shop berries
|
||||
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY);
|
||||
const mod = generateModifierTypeOption(modifierTypes.BERRY);
|
||||
if (mod) {
|
||||
shopOptions.push(mod);
|
||||
}
|
||||
@ -193,29 +203,29 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
const doBerryRewards = () => {
|
||||
const berryText = i18next.t(`${namespace}:berries`);
|
||||
|
||||
scene.playSound("item_fanfare");
|
||||
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
|
||||
globalScene.playSound("item_fanfare");
|
||||
queueEncounterMessage(i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerries }));
|
||||
|
||||
// Generate a random berry and give it to the first Pokemon with room for it
|
||||
for (let i = 0; i < numBerries; i++) {
|
||||
tryGiveBerry(scene);
|
||||
tryGiveBerry();
|
||||
}
|
||||
};
|
||||
|
||||
// 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.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].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(pokemon.scene, `${namespace}:option.2.boss_enraged`);
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
queueEncounterMessage(`${namespace}:option.2.boss_enraged`);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
};
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
|
||||
await showEncounterText(scene, `${namespace}:option.2.selected_bad`);
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
|
||||
await showEncounterText(`${namespace}:option.2.selected_bad`);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
return;
|
||||
} 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
|
||||
@ -224,19 +234,19 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
const doFasterBerryRewards = () => {
|
||||
const berryText = i18next.t(`${namespace}:berries`);
|
||||
|
||||
scene.playSound("item_fanfare");
|
||||
queueEncounterMessage(scene, i18next.t("battle:rewardGainCount", { modifierName: berryText, count: numBerriesGrabbed }));
|
||||
globalScene.playSound("item_fanfare");
|
||||
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)
|
||||
for (let i = 0; i < numBerriesGrabbed; i++) {
|
||||
tryGiveBerry(scene, fastestPokemon);
|
||||
tryGiveBerry(fastestPokemon);
|
||||
}
|
||||
};
|
||||
|
||||
setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
|
||||
await showEncounterText(scene, `${namespace}:option.2.selected`);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterExp(fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
|
||||
await showEncounterText(`${namespace}:option.2.selected`);
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
})
|
||||
.build()
|
||||
@ -251,38 +261,38 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
.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 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
|
||||
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;
|
||||
|
||||
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
||||
applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry);
|
||||
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) {
|
||||
applyModifierTypeToPlayerPokemon(prioritizedPokemon, berry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the party until berry was successfully given
|
||||
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;
|
||||
|
||||
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
||||
applyModifierTypeToPlayerPokemon(scene, pokemon, berry);
|
||||
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount()) {
|
||||
applyModifierTypeToPlayerPokemon(pokemon, berry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import type {
|
||||
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import {
|
||||
EnemyPartyConfig, generateModifierType,
|
||||
generateModifierType,
|
||||
generateModifierTypeOption,
|
||||
initBattleWithEnemyConfig,
|
||||
leaveEncounterWithoutBattle,
|
||||
@ -17,17 +19,20 @@ import {
|
||||
} from "#app/data/trainer-config";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
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 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 { TrainerType } from "#enums/trainer-type";
|
||||
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 { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
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 { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import {
|
||||
@ -37,14 +42,17 @@ import {
|
||||
TypeRequirement
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
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 {
|
||||
AttackTypeBoosterModifier,
|
||||
BypassSpeedChanceModifier,
|
||||
ContactHeldItemTransferChanceModifier,
|
||||
GigantamaxAccessModifier,
|
||||
MegaEvolutionAccessModifier,
|
||||
PokemonHeldItemModifier
|
||||
MegaEvolutionAccessModifier
|
||||
} from "#app/modifier/modifier";
|
||||
import i18next from "i18next";
|
||||
import MoveInfoOverlay from "#app/ui/move-info-overlay";
|
||||
@ -214,12 +222,12 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro_dialogue`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Calculates what trainers are available for battle in the encounter
|
||||
|
||||
// Bug type superfan trainer config
|
||||
const config = getTrainerConfigForWave(scene.currentBattle.waveIndex);
|
||||
const config = getTrainerConfigForWave(globalScene.currentBattle.waveIndex);
|
||||
const spriteKey = config.getSpriteKey();
|
||||
encounter.enemyPartyConfigs.push({
|
||||
trainerConfig: config,
|
||||
@ -227,7 +235,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
});
|
||||
|
||||
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);
|
||||
butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false);
|
||||
} else {
|
||||
@ -270,9 +278,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
];
|
||||
|
||||
const requiredItems = [
|
||||
generateModifierType(scene, modifierTypes.QUICK_CLAW),
|
||||
generateModifierType(scene, modifierTypes.GRIP_CLAW),
|
||||
generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.BUG ]),
|
||||
generateModifierType(modifierTypes.QUICK_CLAW),
|
||||
generateModifierType(modifierTypes.GRIP_CLAW),
|
||||
generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.BUG ]),
|
||||
];
|
||||
|
||||
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
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
// Init the moves available for tutor
|
||||
@ -313,9 +321,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
// Assigns callback that teaches move before continuing to rewards
|
||||
encounter.onRewards = doBugTypeMoveTutor;
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true);
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
await transitionMysteryEncounterIntroVisuals(true, true);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
}
|
||||
)
|
||||
.withOption(MysteryEncounterOptionBuilder
|
||||
@ -326,17 +334,17 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`,
|
||||
disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// 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
|
||||
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 });
|
||||
encounter.setDialogueToken("numBugTypes", numBugTypesText);
|
||||
|
||||
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 = [
|
||||
{
|
||||
speaker: `${namespace}:speaker`,
|
||||
@ -344,7 +352,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
},
|
||||
];
|
||||
} 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 = [
|
||||
{
|
||||
speaker: `${namespace}:speaker`,
|
||||
@ -352,7 +360,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
},
|
||||
];
|
||||
} 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 = [
|
||||
{
|
||||
speaker: `${namespace}:speaker`,
|
||||
@ -362,28 +370,28 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
} else {
|
||||
// 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
|
||||
const modifierOptions: ModifierTypeOption[] = [ generateModifierTypeOption(scene, modifierTypes.MASTER_BALL)! ];
|
||||
const modifierOptions: ModifierTypeOption[] = [ generateModifierTypeOption(modifierTypes.MASTER_BALL)! ];
|
||||
const specialOptions: ModifierTypeOption[] = [];
|
||||
|
||||
if (!scene.findModifier(m => m instanceof MegaEvolutionAccessModifier)) {
|
||||
modifierOptions.push(generateModifierTypeOption(scene, modifierTypes.MEGA_BRACELET)!);
|
||||
if (!globalScene.findModifier(m => m instanceof MegaEvolutionAccessModifier)) {
|
||||
modifierOptions.push(generateModifierTypeOption(modifierTypes.MEGA_BRACELET)!);
|
||||
}
|
||||
if (!scene.findModifier(m => m instanceof GigantamaxAccessModifier)) {
|
||||
modifierOptions.push(generateModifierTypeOption(scene, modifierTypes.DYNAMAX_BAND)!);
|
||||
if (!globalScene.findModifier(m => m instanceof GigantamaxAccessModifier)) {
|
||||
modifierOptions.push(generateModifierTypeOption(modifierTypes.DYNAMAX_BAND)!);
|
||||
}
|
||||
const nonRareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.EVOLUTION_ITEM);
|
||||
const nonRareEvolutionModifier = generateModifierTypeOption(modifierTypes.EVOLUTION_ITEM);
|
||||
if (nonRareEvolutionModifier) {
|
||||
specialOptions.push(nonRareEvolutionModifier);
|
||||
}
|
||||
const rareEvolutionModifier = generateModifierTypeOption(scene, modifierTypes.RARE_EVOLUTION_ITEM);
|
||||
const rareEvolutionModifier = generateModifierTypeOption(modifierTypes.RARE_EVOLUTION_ITEM);
|
||||
if (rareEvolutionModifier) {
|
||||
specialOptions.push(rareEvolutionModifier);
|
||||
}
|
||||
const formChangeModifier = generateModifierTypeOption(scene, modifierTypes.FORM_CHANGE_ITEM);
|
||||
const formChangeModifier = generateModifierTypeOption(modifierTypes.FORM_CHANGE_ITEM);
|
||||
if (formChangeModifier) {
|
||||
specialOptions.push(formChangeModifier);
|
||||
}
|
||||
const rareFormChangeModifier = generateModifierTypeOption(scene, modifierTypes.RARE_FORM_CHANGE_ITEM);
|
||||
const rareFormChangeModifier = generateModifierTypeOption(modifierTypes.RARE_FORM_CHANGE_ITEM);
|
||||
if (rareFormChangeModifier) {
|
||||
specialOptions.push(rareFormChangeModifier);
|
||||
}
|
||||
@ -391,7 +399,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]);
|
||||
}
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false });
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: modifierOptions, fillRemaining: false });
|
||||
encounter.selectedOption!.dialogue!.selected = [
|
||||
{
|
||||
speaker: `${namespace}:speaker`,
|
||||
@ -400,9 +408,9 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
];
|
||||
}
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Player shows off their bug types
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build())
|
||||
.withOption(MysteryEncounterOptionBuilder
|
||||
@ -429,8 +437,8 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
||||
],
|
||||
secondOptionPrompt: `${namespace}:option.3.select_prompt`,
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// 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);
|
||||
});
|
||||
if (!hasValidItem) {
|
||||
return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null;
|
||||
return getEncounterText(`${namespace}:option.3.invalid_selection`) ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||
return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const modifier = encounter.misc.chosenModifier;
|
||||
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
|
||||
|
||||
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;
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ bugNet ], guaranteedModifierTypeFuncs: [ modifierTypes.REVIVER_SEED ], fillRemaining: false });
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: [ bugNet ], guaranteedModifierTypeFuncs: [ modifierTypes.REVIVER_SEED ], fillRemaining: false });
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build())
|
||||
.withOutroDialogue([
|
||||
@ -642,22 +650,22 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
return config;
|
||||
}
|
||||
|
||||
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
|
||||
function doBugTypeMoveTutor(): Promise<void> {
|
||||
return new Promise<void>(async resolve => {
|
||||
const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
|
||||
await showEncounterDialogue(scene, `${namespace}:battle_won`, `${namespace}:speaker`);
|
||||
const moveOptions = globalScene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
|
||||
await showEncounterDialogue(`${namespace}:battle_won`, `${namespace}:speaker`);
|
||||
|
||||
const overlayScale = 1;
|
||||
const moveInfoOverlay = new MoveInfoOverlay(scene, {
|
||||
const moveInfoOverlay = new MoveInfoOverlay({
|
||||
delayVisibility: false,
|
||||
scale: overlayScale,
|
||||
onSide: true,
|
||||
right: true,
|
||||
x: 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 option: OptionSelectItem = {
|
||||
@ -680,7 +688,7 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
|
||||
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;
|
||||
if (!result) {
|
||||
moveInfoOverlay.active = false;
|
||||
@ -691,7 +699,7 @@ function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
|
||||
|
||||
// Option select complete, handle if they are learning a move
|
||||
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
|
||||
|
||||
@ -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 { 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 { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { Species } from "#enums/species";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
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 { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { randSeedInt, randSeedShuffle } from "#app/utils";
|
||||
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import i18next from "i18next";
|
||||
import { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { Ability } from "#app/data/ability";
|
||||
import { BerryModifier } from "#app/modifier/modifier";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
@ -105,8 +109,8 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||
speaker: `${namespace}:speaker`
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const clownTrainerType = TrainerType.HARLEQUIN;
|
||||
const clownConfig = trainerConfigs[clownTrainerType].clone();
|
||||
@ -142,7 +146,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||
});
|
||||
|
||||
// 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());
|
||||
|
||||
@ -165,12 +169,12 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Spawn battle
|
||||
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
|
||||
encounter.startOfBattleEffects.push(
|
||||
@ -193,28 +197,28 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||
ignorePp: true
|
||||
});
|
||||
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
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
|
||||
const abilityWasSwapped = await handleSwapAbility(scene);
|
||||
const abilityWasSwapped = await handleSwapAbility();
|
||||
if (abilityWasSwapped) {
|
||||
await showEncounterText(scene, `${namespace}:option.1.ability_gained`);
|
||||
await showEncounterText(`${namespace}:option.1.ability_gained`);
|
||||
}
|
||||
|
||||
// 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
|
||||
scene.tweens.add({
|
||||
targets: scene.currentBattle.trainer,
|
||||
globalScene.tweens.add({
|
||||
targets: globalScene.currentBattle.trainer,
|
||||
x: "+=16",
|
||||
y: "-=16",
|
||||
alpha: 0,
|
||||
ease: "Sine.easeInOut",
|
||||
duration: 250
|
||||
});
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||
background.playWithoutTargets(scene, 230, 40, 2);
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
|
||||
background.playWithoutTargets(230, 40, 2);
|
||||
return true;
|
||||
})
|
||||
.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
|
||||
// Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items
|
||||
// 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 count = mostHeldItemsPokemon.getHeldItems()
|
||||
.filter(m => m.isTransferable && !(m instanceof BerryModifier))
|
||||
@ -270,10 +274,10 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||
items.filter(m => m instanceof BerryModifier)
|
||||
.forEach(m => {
|
||||
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)
|
||||
// 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;
|
||||
if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) {
|
||||
numRogue += m.stackCount;
|
||||
scene.removeModifier(m);
|
||||
globalScene.removeModifier(m);
|
||||
} else if (type.id === "LUCKY_EGG" || type.id === "SOOTHE_BELL" || tier === ModifierTier.ULTRA) {
|
||||
numUltra += m.stackCount;
|
||||
scene.removeModifier(m);
|
||||
globalScene.removeModifier(m);
|
||||
}
|
||||
});
|
||||
|
||||
generateItemsOfTier(scene, mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA);
|
||||
generateItemsOfTier(scene, mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE);
|
||||
generateItemsOfTier(mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA);
|
||||
generateItemsOfTier(mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
.withOptionPhase(async () => {
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
.withPostOptionPhase(async () => {
|
||||
// Play animations
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||
background.playWithoutTargets(scene, 230, 40, 2);
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200);
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
|
||||
background.playWithoutTargets(230, 40, 2);
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 200);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -327,10 +331,10 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Randomize the second type of all player's pokemon
|
||||
// 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);
|
||||
|
||||
// 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) => {
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
.withOptionPhase(async () => {
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
.withPostOptionPhase(async () => {
|
||||
// Play animations
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||
background.playWithoutTargets(scene, 230, 40, 2);
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true, 200);
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
|
||||
background.playWithoutTargets(230, 40, 2);
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 200);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -385,24 +389,24 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||
])
|
||||
.build();
|
||||
|
||||
async function handleSwapAbility(scene: BattleScene) {
|
||||
async function handleSwapAbility() {
|
||||
return new Promise<boolean>(async resolve => {
|
||||
await showEncounterDialogue(scene, `${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`);
|
||||
await showEncounterText(scene, `${namespace}:option.1.apply_ability_message`);
|
||||
await showEncounterDialogue(`${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`);
|
||||
await showEncounterText(`${namespace}:option.1.apply_ability_message`);
|
||||
|
||||
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||
displayYesNoOptions(scene, resolve);
|
||||
globalScene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||
displayYesNoOptions(resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function displayYesNoOptions(scene: BattleScene, resolve) {
|
||||
showEncounterText(scene, `${namespace}:option.1.ability_prompt`, null, 500, false);
|
||||
function displayYesNoOptions(resolve) {
|
||||
showEncounterText(`${namespace}:option.1.ability_prompt`, null, 500, false);
|
||||
const fullOptions = [
|
||||
{
|
||||
label: i18next.t("menu:yes"),
|
||||
handler: () => {
|
||||
onYesAbilitySwap(scene, resolve);
|
||||
onYesAbilitySwap(resolve);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
@ -420,29 +424,29 @@ function displayYesNoOptions(scene: BattleScene, resolve) {
|
||||
maxOptions: 7,
|
||||
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) => {
|
||||
// Do ability swap
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
applyAbilityOverrideToPokemon(pokemon, encounter.misc.ability);
|
||||
encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
|
||||
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
|
||||
globalScene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
|
||||
};
|
||||
|
||||
const onPokemonNotSelected = () => {
|
||||
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||
displayYesNoOptions(scene, resolve);
|
||||
globalScene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||
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
|
||||
// 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
|
||||
@ -495,11 +499,11 @@ function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItem
|
||||
const newItemType = pool[randIndex];
|
||||
let newMod: PokemonHeldItemModifierType;
|
||||
if (tier === "Berries") {
|
||||
newMod = generateModifierType(scene, modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType;
|
||||
newMod = generateModifierType(modifierTypes.BERRY, [ newItemType[0] ]) as PokemonHeldItemModifierType;
|
||||
} 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
|
||||
newItemType[1]--;
|
||||
if (newItemType[1] <= 0) {
|
||||
|
||||
@ -1,22 +1,26 @@
|
||||
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 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 { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
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 { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
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 { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
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 { Biome } from "#enums/biome";
|
||||
import { EncounterAnim } from "#enums/encounter-anims";
|
||||
@ -91,10 +95,10 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
.withAutoHideIntroVisuals(false)
|
||||
.withCatchAllowed(true)
|
||||
.withFleeAllowed(false)
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
const oricorio = scene.getEnemyPokemon()!;
|
||||
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, oricorio, scene.getPlayerPokemon()!);
|
||||
danceAnim.play(scene, false, () => {
|
||||
.withOnVisualsStart(() => {
|
||||
const oricorio = globalScene.getEnemyPokemon()!;
|
||||
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, oricorio, globalScene.getPlayerPokemon()!);
|
||||
danceAnim.play(false, () => {
|
||||
if (oricorio.shiny) {
|
||||
oricorio.sparkle();
|
||||
}
|
||||
@ -110,12 +114,12 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const species = getPokemonSpecies(Species.ORICORIO);
|
||||
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const enemyPokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, false);
|
||||
const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const enemyPokemon = new EnemyPokemon(species, level, TrainerSlot.NONE, false);
|
||||
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) {
|
||||
if (enemyPokemon.moveset.length < 4) {
|
||||
enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE));
|
||||
@ -126,7 +130,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
|
||||
// Set the form index based on the biome
|
||||
// Defaults to Baile style if somehow nothing matches
|
||||
const currentBiome = scene.arena.biomeType;
|
||||
const currentBiome = globalScene.arena.biomeType;
|
||||
if (BAILE_STYLE_BIOMES.includes(currentBiome)) {
|
||||
enemyPokemon.formIndex = 0;
|
||||
} else if (POM_POM_STYLE_BIOMES.includes(currentBiome)) {
|
||||
@ -140,14 +144,14 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
}
|
||||
|
||||
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)
|
||||
scene.getEnemyParty().forEach(enemyPokemon => {
|
||||
scene.field.remove(enemyPokemon, true);
|
||||
globalScene.getEnemyParty().forEach(enemyPokemon => {
|
||||
globalScene.field.remove(enemyPokemon, true);
|
||||
});
|
||||
scene.currentBattle.enemyParty = [ oricorio ];
|
||||
scene.field.add(oricorio);
|
||||
globalScene.currentBattle.enemyParty = [ oricorio ];
|
||||
globalScene.field.add(oricorio);
|
||||
// Spawns on offscreen field
|
||||
oricorio.x -= 300;
|
||||
encounter.loadAssets.push(oricorio.loadAssets());
|
||||
@ -160,8 +164,8 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
// Gets +1 to all stats except SPD on battle start
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.boss_enraged`);
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF ], 1));
|
||||
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
||||
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
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
encounter.startOfBattleEffects.push({
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
@ -197,9 +201,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
ignorePp: true
|
||||
});
|
||||
|
||||
await hideOricorioPokemon(scene);
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.BATON ], fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||
await hideOricorioPokemon();
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.BATON ], fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -215,25 +219,25 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Learn its Dance
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
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
|
||||
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon());
|
||||
danceAnim.play(scene);
|
||||
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, globalScene.getEnemyPokemon()!, globalScene.getPlayerPokemon());
|
||||
danceAnim.play();
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected);
|
||||
return selectPokemonForOption(onPokemonSelected);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Learn its Dance
|
||||
await hideOricorioPokemon(scene);
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
await hideOricorioPokemon();
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -252,9 +256,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Open menu for selecting pokemon with a Dancing move
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Return the options for nature selection
|
||||
return pokemon.moveset
|
||||
@ -281,20 +285,20 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
if (!pokemon.isAllowedInBattle()) {
|
||||
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) {
|
||||
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
|
||||
return getEncounterText(`${namespace}:invalid_selection`) ?? 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
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const oricorio = encounter.misc.oricorioData.toPokemon(scene);
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const oricorio = encounter.misc.oricorioData.toPokemon();
|
||||
oricorio.passive = true;
|
||||
|
||||
// Ensure the Oricorio's moveset gains the Dance move the player used
|
||||
@ -307,18 +311,18 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
}
|
||||
}
|
||||
|
||||
await hideOricorioPokemon(scene);
|
||||
await catchPokemon(scene, oricorio, null, PokeballType.POKEBALL, false);
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
await hideOricorioPokemon();
|
||||
await catchPokemon(oricorio, null, PokeballType.POKEBALL, false);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
function hideOricorioPokemon(scene: BattleScene) {
|
||||
function hideOricorioPokemon() {
|
||||
return new Promise<void>(resolve => {
|
||||
const oricorioSprite = scene.getEnemyParty()[0];
|
||||
scene.tweens.add({
|
||||
const oricorioSprite = globalScene.getEnemyParty()[0];
|
||||
globalScene.tweens.add({
|
||||
targets: oricorioSprite,
|
||||
x: "+=16",
|
||||
y: "-=16",
|
||||
@ -326,7 +330,7 @@ function hideOricorioPokemon(scene: BattleScene) {
|
||||
ease: "Sine.easeInOut",
|
||||
duration: 750,
|
||||
onComplete: () => {
|
||||
scene.field.remove(oricorioSprite, true);
|
||||
globalScene.field.remove(oricorioSprite, true);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
import { Type } from "#enums/type";
|
||||
import type { Type } from "#enums/type";
|
||||
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
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 { 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 { 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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
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 { 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
|
||||
// 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
|
||||
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());
|
||||
|
||||
// Store removed pokemon types
|
||||
@ -156,16 +159,16 @@ export const DarkDealEncounter: MysteryEncounter =
|
||||
modifiers
|
||||
};
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Give the player 5 Rogue Balls
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ROGUE_BALL));
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.ROGUE_BALL));
|
||||
|
||||
// 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
|
||||
let bossTypes: Type[] = encounter.misc.removedTypes;
|
||||
const singleTypeChallenges = scene.gameMode.challenges.filter(c => c.value && c.id === Challenges.SINGLE_TYPE);
|
||||
if (scene.gameMode.isChallenge && singleTypeChallenges.length > 0) {
|
||||
const singleTypeChallenges = globalScene.gameMode.challenges.filter(c => c.value && c.id === Challenges.SINGLE_TYPE);
|
||||
if (globalScene.gameMode.isChallenge && singleTypeChallenges.length > 0) {
|
||||
bossTypes = singleTypeChallenges.map(c => (c.value - 1) as Type);
|
||||
}
|
||||
|
||||
@ -191,7 +194,7 @@ export const DarkDealEncounter: MysteryEncounter =
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [ pokemonConfig ],
|
||||
};
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -206,9 +209,9 @@ export const DarkDealEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,18 +1,22 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
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 { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
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 { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PreserveBerryModifier } from "#app/modifier/modifier";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import type { PokemonHeldItemModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
||||
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 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 { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
@ -36,19 +40,19 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
|
||||
|
||||
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
|
||||
|
||||
const doEventReward = (scene: BattleScene) => {
|
||||
const event_buff = scene.eventManager.activeEvent()?.delibirdyBuff ?? [];
|
||||
const doEventReward = () => {
|
||||
const event_buff = globalScene.eventManager.activeEvent()?.delibirdyBuff ?? [];
|
||||
if (event_buff.length > 0) {
|
||||
const candidates = event_buff.filter((c => {
|
||||
const mtype = generateModifierType(scene, modifierTypes[c]);
|
||||
const existingCharm = scene.findModifier(m => m.type.id === mtype?.id);
|
||||
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount(scene));
|
||||
const mtype = generateModifierType(modifierTypes[c]);
|
||||
const existingCharm = globalScene.findModifier(m => m.type.id === mtype?.id);
|
||||
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount());
|
||||
}));
|
||||
if (candidates.length > 0) {
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes[randSeedItem(candidates)]));
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)]));
|
||||
} else {
|
||||
// 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`,
|
||||
}
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
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;
|
||||
})
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
scene.fadeAndSwitchBgm("mystery_encounter_delibirdy");
|
||||
.withOnVisualsStart(() => {
|
||||
globalScene.fadeAndSwitchBgm("mystery_encounter_delibirdy");
|
||||
return true;
|
||||
})
|
||||
.withOption(
|
||||
@ -138,29 +142,29 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false);
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
updatePlayerMoney(-(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney, true, false);
|
||||
return true;
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Give the player an Amulet Coin
|
||||
// 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
|
||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
||||
scene.playSound("item_fanfare");
|
||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||
doEventReward(scene);
|
||||
const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
|
||||
globalScene.playSound("item_fanfare");
|
||||
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||
doEventReward();
|
||||
} else {
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.AMULET_COIN));
|
||||
doEventReward(scene);
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN));
|
||||
doEventReward();
|
||||
}
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -178,8 +182,8 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Get Pokemon held items and filter for valid ones
|
||||
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||
@ -205,57 +209,57 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||
|
||||
const selectableFilter = (pokemon: Pokemon) => {
|
||||
// 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) {
|
||||
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
|
||||
return getEncounterText(`${namespace}:invalid_selection`) ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||
return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const modifier: BerryModifier | PokemonInstantReviveModifier = encounter.misc.chosenModifier;
|
||||
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
|
||||
|
||||
// Give the player a Candy Jar if they gave a Berry, and a Berry Pouch for Reviver Seed
|
||||
if (modifier instanceof BerryModifier) {
|
||||
// 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
|
||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
||||
scene.playSound("item_fanfare");
|
||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||
doEventReward(scene);
|
||||
const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
|
||||
globalScene.playSound("item_fanfare");
|
||||
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||
doEventReward();
|
||||
} else {
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR));
|
||||
doEventReward(scene);
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR));
|
||||
doEventReward();
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
||||
scene.playSound("item_fanfare");
|
||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||
doEventReward(scene);
|
||||
const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
await applyModifierTypeToPlayerPokemon(globalScene.getPlayerPokemon()!, shellBell);
|
||||
globalScene.playSound("item_fanfare");
|
||||
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||
doEventReward();
|
||||
} else {
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH));
|
||||
doEventReward(scene);
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH));
|
||||
doEventReward();
|
||||
}
|
||||
}
|
||||
|
||||
chosenPokemon.loseHeldItem(modifier, false);
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -273,8 +277,8 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Get Pokemon held items and filter for valid ones
|
||||
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||
@ -300,39 +304,39 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||
|
||||
const selectableFilter = (pokemon: Pokemon) => {
|
||||
// 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) {
|
||||
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
|
||||
return getEncounterText(`${namespace}:invalid_selection`) ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||
return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const modifier = encounter.misc.chosenModifier;
|
||||
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
|
||||
|
||||
// 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
|
||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerParty()[0], shellBell);
|
||||
scene.playSound("item_fanfare");
|
||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||
doEventReward(scene);
|
||||
const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
await applyModifierTypeToPlayerPokemon(globalScene.getPlayerParty()[0], shellBell);
|
||||
globalScene.playSound("item_fanfare");
|
||||
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||
doEventReward();
|
||||
} else {
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM));
|
||||
doEventReward(scene);
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM));
|
||||
doEventReward();
|
||||
}
|
||||
|
||||
chosenPokemon.loseHeldItem(modifier, false);
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
|
||||
@ -2,12 +2,13 @@ import {
|
||||
leaveEncounterWithoutBattle,
|
||||
setEncounterRewards,
|
||||
} 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 { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, {
|
||||
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";
|
||||
@ -60,7 +61,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}:option.1.label`,
|
||||
buttonTooltip: `${namespace}:option.1.tooltip`,
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Choose TMs
|
||||
const modifiers: ModifierTypeFunc[] = [];
|
||||
let i = 0;
|
||||
@ -77,8 +78,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
|
||||
i++;
|
||||
}
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
@ -86,7 +87,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}:option.2.label`,
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`,
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Choose Vitamins
|
||||
const modifiers: ModifierTypeFunc[] = [];
|
||||
let i = 0;
|
||||
@ -101,8 +102,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
|
||||
i++;
|
||||
}
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
@ -110,7 +111,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}:option.3.label`,
|
||||
buttonTooltip: `${namespace}:option.3.tooltip`,
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Choose X Items
|
||||
const modifiers: ModifierTypeFunc[] = [];
|
||||
let i = 0;
|
||||
@ -125,8 +126,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
|
||||
i++;
|
||||
}
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
@ -134,7 +135,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}:option.4.label`,
|
||||
buttonTooltip: `${namespace}:option.4.tooltip`,
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Choose Pokeballs
|
||||
const modifiers: ModifierTypeFunc[] = [];
|
||||
let i = 0;
|
||||
@ -153,8 +154,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter =
|
||||
i++;
|
||||
}
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
)
|
||||
.withOutroDialogue([
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { MoveCategory } from "#app/data/move";
|
||||
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 { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
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 BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { Stat } from "#enums/stat";
|
||||
@ -64,8 +65,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
buttonTooltip: `${namespace}:option.1.tooltip`,
|
||||
secondOptionPrompt: `${namespace}:second_option_prompt`,
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Return the options for Pokemon move valid for this option
|
||||
return pokemon.moveset.map((move: PokemonMove) => {
|
||||
@ -74,7 +75,7 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
handler: () => {
|
||||
// Pokemon and move selected
|
||||
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:physical`));
|
||||
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.PHYSICAL);
|
||||
pokemonAndMoveChosen(pokemon, move, MoveCategory.PHYSICAL);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@ -82,23 +83,23 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
});
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected);
|
||||
return selectPokemonForOption(onPokemonSelected);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
if (encounter.misc.correctMove) {
|
||||
const modifiers = [
|
||||
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ATK ])!,
|
||||
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.DEF ])!,
|
||||
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
|
||||
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!,
|
||||
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
|
||||
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ATK ])!,
|
||||
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.DEF ])!,
|
||||
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
|
||||
generateModifierTypeOption(modifierTypes.DIRE_HIT)!,
|
||||
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()
|
||||
)
|
||||
@ -110,8 +111,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`,
|
||||
secondOptionPrompt: `${namespace}:second_option_prompt`,
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Return the options for Pokemon move valid for this option
|
||||
return pokemon.moveset.map((move: PokemonMove) => {
|
||||
@ -120,7 +121,7 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
handler: () => {
|
||||
// Pokemon and move selected
|
||||
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:special`));
|
||||
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.SPECIAL);
|
||||
pokemonAndMoveChosen(pokemon, move, MoveCategory.SPECIAL);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@ -128,23 +129,23 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
});
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected);
|
||||
return selectPokemonForOption(onPokemonSelected);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
if (encounter.misc.correctMove) {
|
||||
const modifiers = [
|
||||
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPATK ])!,
|
||||
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPDEF ])!,
|
||||
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
|
||||
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!,
|
||||
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
|
||||
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPATK ])!,
|
||||
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPDEF ])!,
|
||||
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
|
||||
generateModifierTypeOption(modifierTypes.DIRE_HIT)!,
|
||||
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()
|
||||
)
|
||||
@ -156,8 +157,8 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
buttonTooltip: `${namespace}:option.3.tooltip`,
|
||||
secondOptionPrompt: `${namespace}:second_option_prompt`,
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Return the options for Pokemon move valid for this option
|
||||
return pokemon.moveset.map((move: PokemonMove) => {
|
||||
@ -166,7 +167,7 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
handler: () => {
|
||||
// Pokemon and move selected
|
||||
encounter.setDialogueToken("moveCategory", i18next.t(`${namespace}:status`));
|
||||
pokemonAndMoveChosen(scene, pokemon, move, MoveCategory.STATUS);
|
||||
pokemonAndMoveChosen(pokemon, move, MoveCategory.STATUS);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@ -174,30 +175,30 @@ export const FieldTripEncounter: MysteryEncounter =
|
||||
});
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected);
|
||||
return selectPokemonForOption(onPokemonSelected);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
if (encounter.misc.correctMove) {
|
||||
const modifiers = [
|
||||
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ACC ])!,
|
||||
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
|
||||
generateModifierTypeOption(scene, modifierTypes.GREAT_BALL)!,
|
||||
generateModifierTypeOption(scene, modifierTypes.IV_SCANNER)!,
|
||||
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
|
||||
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.ACC ])!,
|
||||
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [ Stat.SPD ])!,
|
||||
generateModifierTypeOption(modifierTypes.GREAT_BALL)!,
|
||||
generateModifierTypeOption(modifierTypes.IV_SCANNER)!,
|
||||
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();
|
||||
|
||||
function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
function pokemonAndMoveChosen(pokemon: PlayerPokemon, move: PokemonMove, correctMoveCategory: MoveCategory) {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const correctMove = move.getMove().category === correctMoveCategory;
|
||||
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("move", move.getName());
|
||||
@ -214,7 +215,7 @@ function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move:
|
||||
text: `${namespace}:incorrect_exp`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, scene.getPlayerParty().map((p) => p.id), 50);
|
||||
setEncounterExp(globalScene.getPlayerParty().map((p) => p.id), 50);
|
||||
} else {
|
||||
encounter.selectedOption!.dialogue!.selected = [
|
||||
{
|
||||
@ -228,7 +229,7 @@ function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move:
|
||||
text: `${namespace}:correct_exp`,
|
||||
},
|
||||
];
|
||||
setEncounterExp(scene, [ pokemon.id ], 100);
|
||||
setEncounterExp([ pokemon.id ], 100);
|
||||
}
|
||||
encounter.misc = {
|
||||
correctMove: correctMove,
|
||||
|
||||
@ -1,16 +1,20 @@
|
||||
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 { AttackTypeBoosterModifierType, modifierTypes, } from "#app/modifier/modifier-type";
|
||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
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 BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { Species } from "#enums/species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Gender } from "#app/data/gender";
|
||||
import { Type } from "#enums/type";
|
||||
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 { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
@ -58,8 +62,8 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mons
|
||||
const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA);
|
||||
@ -71,7 +75,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
||||
gender: Gender.MALE,
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
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,
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
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
|
||||
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());
|
||||
|
||||
return true;
|
||||
})
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
.withOnVisualsStart(() => {
|
||||
// Play animations
|
||||
const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||
background.playWithoutTargets(scene, 200, 70, 2, 3);
|
||||
const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
||||
animation.playWithoutTargets(scene, 80, 100, 2);
|
||||
scene.time.delayedCall(600, () => {
|
||||
animation.playWithoutTargets(scene, -20, 100, 2);
|
||||
const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
|
||||
background.playWithoutTargets(200, 70, 2, 3);
|
||||
const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, globalScene.getPlayerPokemon()!, globalScene.getPlayerPokemon());
|
||||
animation.playWithoutTargets(80, 100, 2);
|
||||
globalScene.time.delayedCall(600, () => {
|
||||
animation.playWithoutTargets(-20, 100, 2);
|
||||
});
|
||||
scene.time.delayedCall(1200, () => {
|
||||
animation.playWithoutTargets(scene, 140, 150, 2);
|
||||
globalScene.time.delayedCall(1200, () => {
|
||||
animation.playWithoutTargets(140, 150, 2);
|
||||
});
|
||||
|
||||
return true;
|
||||
@ -150,10 +154,10 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Pick battle
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
setEncounterRewards(scene, { fillRemaining: true }, undefined, () => giveLeadPokemonAttackTypeBoostItem(scene));
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
setEncounterRewards({ fillRemaining: true }, undefined, () => giveLeadPokemonAttackTypeBoostItem());
|
||||
|
||||
encounter.startOfBattleEffects.push(
|
||||
{
|
||||
@ -168,7 +172,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
||||
move: new PokemonMove(Moves.FIRE_SPIN),
|
||||
ignorePp: true
|
||||
});
|
||||
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
||||
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
||||
}
|
||||
)
|
||||
.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
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const nonFireTypes = scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE));
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const nonFireTypes = globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE));
|
||||
|
||||
for (const pkm of nonFireTypes) {
|
||||
const percentage = DAMAGE_PERCENTAGE / 100;
|
||||
const damage = Math.floor(pkm.getMaxHp() * percentage);
|
||||
applyDamageToPokemon(scene, pkm, damage);
|
||||
applyDamageToPokemon(pkm, damage);
|
||||
}
|
||||
|
||||
// Burn random member
|
||||
@ -201,7 +205,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
||||
// Burn applied
|
||||
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
|
||||
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
|
||||
applyAbilityOverrideToPokemon(chosenPokemon, Abilities.HEATPROOF);
|
||||
@ -209,7 +213,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
||||
}
|
||||
|
||||
// No rewards
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
}
|
||||
)
|
||||
.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
|
||||
transitionMysteryEncounterIntroVisuals(scene, false, false, 2000);
|
||||
transitionMysteryEncounterIntroVisuals(false, false, 2000);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Fire types help calm the Volcarona
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
setEncounterRewards(scene,
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
setEncounterRewards(
|
||||
{ fillRemaining: true },
|
||||
undefined,
|
||||
() => {
|
||||
giveLeadPokemonAttackTypeBoostItem(scene);
|
||||
giveLeadPokemonAttackTypeBoostItem();
|
||||
});
|
||||
|
||||
const primary = encounter.options[2].primaryPokemon!;
|
||||
|
||||
setEncounterExp(scene, [ primary.id ], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterExp([ primary.id ], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
function giveLeadPokemonAttackTypeBoostItem(scene: BattleScene) {
|
||||
function giveLeadPokemonAttackTypeBoostItem() {
|
||||
// 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) {
|
||||
// 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) {
|
||||
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("leadPokemon", leadPokemon.getNameToRender());
|
||||
queueEncounterMessage(scene, `${namespace}:found_item`);
|
||||
queueEncounterMessage(`${namespace}:found_item`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,28 @@
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import type {
|
||||
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import {
|
||||
EnemyPartyConfig,
|
||||
initBattleWithEnemyConfig,
|
||||
leaveEncounterWithoutBattle, setEncounterExp,
|
||||
leaveEncounterWithoutBattle,
|
||||
setEncounterExp,
|
||||
setEncounterRewards
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
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 { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import type {
|
||||
ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||
import {
|
||||
getPartyLuckValue,
|
||||
getPlayerModifierTypeOptions,
|
||||
ModifierPoolType,
|
||||
ModifierTypeOption,
|
||||
regenerateModifierPoolThresholds,
|
||||
} from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
@ -29,7 +34,8 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun
|
||||
import { randSeedInt, randSeedItem } from "#app/utils";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounters/fightOrFlight";
|
||||
@ -52,20 +58,20 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mon
|
||||
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
let bossSpecies: PokemonSpecies;
|
||||
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);
|
||||
if (globalScene.eventManager.isEventActive() && globalScene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
|
||||
const eventEncounter = randSeedItem(globalScene.eventManager.activeEvent()!.uncommonBreedEncounters!);
|
||||
const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, globalScene.gameMode);
|
||||
bossSpecies = getPokemonSpecies( levelSpecies );
|
||||
} else {
|
||||
bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
||||
bossSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
|
||||
}
|
||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||
const bossPokemon = new EnemyPokemon(bossSpecies, level, TrainerSlot.NONE, true);
|
||||
encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender());
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [{
|
||||
@ -75,10 +81,10 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||
isBoss: true,
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.stat_boost`);
|
||||
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
||||
// Randomly boost 1 stat 2 stages
|
||||
// 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 +93,18 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||
// Calculate item
|
||||
// Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER
|
||||
const tier =
|
||||
scene.currentBattle.waveIndex > 160
|
||||
globalScene.currentBattle.waveIndex > 160
|
||||
? ModifierTier.MASTER
|
||||
: scene.currentBattle.waveIndex > 120
|
||||
: globalScene.currentBattle.waveIndex > 120
|
||||
? ModifierTier.ROGUE
|
||||
: scene.currentBattle.waveIndex > 40
|
||||
: globalScene.currentBattle.waveIndex > 40
|
||||
? ModifierTier.ULTRA
|
||||
: ModifierTier.GREAT;
|
||||
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
|
||||
regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
|
||||
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
|
||||
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.misc = item;
|
||||
@ -144,12 +150,12 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Pick battle
|
||||
// Pokemon will randomly boost 1 stat by 2 stages
|
||||
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
|
||||
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
||||
const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
|
||||
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
||||
}
|
||||
)
|
||||
.withOption(
|
||||
@ -166,16 +172,16 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||
}
|
||||
]
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Pick steal
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const item = scene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption;
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
|
||||
|
||||
// Use primaryPokemon to execute the thievery
|
||||
const primaryPokemon = encounter.options[1].primaryPokemon!;
|
||||
setEncounterExp(scene, primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterExp(primaryPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -189,9 +195,9 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { 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 { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
@ -80,14 +83,14 @@ export const FunAndGamesEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
scene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3");
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3");
|
||||
encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(Species.WOBBUFFET).getName());
|
||||
return true;
|
||||
})
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
scene.fadeAndSwitchBgm("mystery_encounter_fun_and_games");
|
||||
.withOnVisualsStart(() => {
|
||||
globalScene.fadeAndSwitchBgm("mystery_encounter_fun_and_games");
|
||||
return true;
|
||||
})
|
||||
.withOption(MysteryEncounterOptionBuilder
|
||||
@ -102,9 +105,9 @@ export const FunAndGamesEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Select Pokemon for minigame
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
encounter.misc = {
|
||||
playerPokemon: pokemon,
|
||||
@ -113,28 +116,28 @@ export const FunAndGamesEncounter: MysteryEncounter =
|
||||
|
||||
// Only Pokemon that are not KOed/legal can be selected
|
||||
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
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
encounter.misc.turnsRemaining = 3;
|
||||
|
||||
// Update money
|
||||
const moneyCost = (encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney;
|
||||
updatePlayerMoney(scene, -moneyCost, true, false);
|
||||
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:paid_money", { amount: moneyCost }));
|
||||
updatePlayerMoney(-moneyCost, true, false);
|
||||
await showEncounterText(i18next.t("mysteryEncounterMessages:paid_money", { amount: moneyCost }));
|
||||
|
||||
// Handlers for battle events
|
||||
encounter.onTurnStart = handleNextTurn; // triggered during TurnInitPhase
|
||||
encounter.doContinueEncounter = handleLoseMinigame; // triggered during MysteryEncounterRewardsPhase, post VictoryPhase if the player KOs Wobbuffet
|
||||
|
||||
hideShowmanIntroSprite(scene);
|
||||
await summonPlayerPokemon(scene);
|
||||
await showWobbuffetHealthBar(scene);
|
||||
hideShowmanIntroSprite();
|
||||
await summonPlayerPokemon();
|
||||
await showWobbuffetHealthBar();
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -150,22 +153,22 @@ export const FunAndGamesEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true);
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
await transitionMysteryEncounterIntroVisuals(true, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
async function summonPlayerPokemon(scene: BattleScene) {
|
||||
async function summonPlayerPokemon() {
|
||||
return new Promise<void>(async resolve => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const playerPokemon = encounter.misc.playerPokemon;
|
||||
// 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);
|
||||
if (chosenIndex !== 0) {
|
||||
const leadPokemon = party[0];
|
||||
@ -175,36 +178,36 @@ async function summonPlayerPokemon(scene: BattleScene) {
|
||||
|
||||
// Do trainer summon animation
|
||||
let playerAnimationPromise: Promise<void> | undefined;
|
||||
scene.ui.showText(i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(playerPokemon) }));
|
||||
scene.pbTray.hide();
|
||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||
scene.time.delayedCall(562, () => {
|
||||
scene.trainer.setFrame("2");
|
||||
scene.time.delayedCall(64, () => {
|
||||
scene.trainer.setFrame("3");
|
||||
globalScene.ui.showText(i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(playerPokemon) }));
|
||||
globalScene.pbTray.hide();
|
||||
globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||
globalScene.time.delayedCall(562, () => {
|
||||
globalScene.trainer.setFrame("2");
|
||||
globalScene.time.delayedCall(64, () => {
|
||||
globalScene.trainer.setFrame("3");
|
||||
});
|
||||
});
|
||||
scene.tweens.add({
|
||||
targets: scene.trainer,
|
||||
globalScene.tweens.add({
|
||||
targets: globalScene.trainer,
|
||||
x: -36,
|
||||
duration: 1000,
|
||||
onComplete: () => scene.trainer.setVisible(false)
|
||||
onComplete: () => globalScene.trainer.setVisible(false)
|
||||
});
|
||||
scene.time.delayedCall(750, () => {
|
||||
playerAnimationPromise = summonPlayerPokemonAnimation(scene, playerPokemon);
|
||||
globalScene.time.delayedCall(750, () => {
|
||||
playerAnimationPromise = summonPlayerPokemonAnimation(playerPokemon);
|
||||
});
|
||||
|
||||
// Also loads Wobbuffet data (cannot be shiny)
|
||||
const enemySpecies = getPokemonSpecies(Species.WOBBUFFET);
|
||||
scene.currentBattle.enemyParty = [];
|
||||
const wobbuffet = scene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false, true);
|
||||
globalScene.currentBattle.enemyParty = [];
|
||||
const wobbuffet = globalScene.addEnemyPokemon(enemySpecies, encounter.misc.playerPokemon.level, TrainerSlot.NONE, false, true);
|
||||
wobbuffet.ivs = [ 0, 0, 0, 0, 0, 0 ];
|
||||
wobbuffet.setNature(Nature.MILD);
|
||||
wobbuffet.setAlpha(0);
|
||||
wobbuffet.setVisible(false);
|
||||
wobbuffet.calculateStats();
|
||||
scene.currentBattle.enemyParty[0] = wobbuffet;
|
||||
scene.gameData.setPokemonSeen(wobbuffet, true);
|
||||
globalScene.currentBattle.enemyParty[0] = wobbuffet;
|
||||
globalScene.gameData.setPokemonSeen(wobbuffet, true);
|
||||
await wobbuffet.loadAssets();
|
||||
const id = setInterval(checkPlayerAnimationPromise, 500);
|
||||
async function checkPlayerAnimationPromise() {
|
||||
@ -217,37 +220,37 @@ async function summonPlayerPokemon(scene: BattleScene) {
|
||||
});
|
||||
}
|
||||
|
||||
function handleLoseMinigame(scene: BattleScene) {
|
||||
function handleLoseMinigame() {
|
||||
return new Promise<void>(async resolve => {
|
||||
// Check Wobbuffet is still alive
|
||||
const wobbuffet = scene.getEnemyPokemon();
|
||||
const wobbuffet = globalScene.getEnemyPokemon();
|
||||
if (!wobbuffet || wobbuffet.isFainted(true) || wobbuffet.hp === 0) {
|
||||
// Player loses
|
||||
// End the battle
|
||||
if (wobbuffet) {
|
||||
wobbuffet.hideInfo();
|
||||
scene.field.remove(wobbuffet);
|
||||
globalScene.field.remove(wobbuffet);
|
||||
}
|
||||
transitionMysteryEncounterIntroVisuals(scene, true, true);
|
||||
scene.currentBattle.enemyParty = [];
|
||||
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
await showEncounterText(scene, `${namespace}:ko`);
|
||||
const reviveCost = scene.getWaveMoneyAmount(1.5);
|
||||
updatePlayerMoney(scene, -reviveCost, true, false);
|
||||
transitionMysteryEncounterIntroVisuals(true, true);
|
||||
globalScene.currentBattle.enemyParty = [];
|
||||
globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
|
||||
leaveEncounterWithoutBattle(true);
|
||||
await showEncounterText(`${namespace}:ko`);
|
||||
const reviveCost = globalScene.getWaveMoneyAmount(1.5);
|
||||
updatePlayerMoney(-reviveCost, true, false);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function handleNextTurn(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
function handleNextTurn() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const wobbuffet = scene.getEnemyPokemon();
|
||||
const wobbuffet = globalScene.getEnemyPokemon();
|
||||
if (!wobbuffet) {
|
||||
// Should never be triggered, just handling the edge case
|
||||
handleLoseMinigame(scene);
|
||||
handleLoseMinigame();
|
||||
return true;
|
||||
}
|
||||
if (encounter.misc.turnsRemaining <= 0) {
|
||||
@ -257,15 +260,15 @@ function handleNextTurn(scene: BattleScene) {
|
||||
let isHealPhase = false;
|
||||
if (healthRatio < 0.03) {
|
||||
// Grand prize
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false });
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.MULTI_LENS ], fillRemaining: false });
|
||||
resultMessageKey = `${namespace}:best_result`;
|
||||
} else if (healthRatio < 0.15) {
|
||||
// 2nd prize
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false });
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SCOPE_LENS ], fillRemaining: false });
|
||||
resultMessageKey = `${namespace}:great_result`;
|
||||
} else if (healthRatio < 0.33) {
|
||||
// 3rd prize
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false });
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.WIDE_LENS ], fillRemaining: false });
|
||||
resultMessageKey = `${namespace}:good_result`;
|
||||
} else {
|
||||
// No prize
|
||||
@ -275,22 +278,22 @@ function handleNextTurn(scene: BattleScene) {
|
||||
|
||||
// End the battle
|
||||
wobbuffet.hideInfo();
|
||||
scene.field.remove(wobbuffet);
|
||||
scene.currentBattle.enemyParty = [];
|
||||
scene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
|
||||
leaveEncounterWithoutBattle(scene, isHealPhase);
|
||||
globalScene.field.remove(wobbuffet);
|
||||
globalScene.currentBattle.enemyParty = [];
|
||||
globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = undefined;
|
||||
leaveEncounterWithoutBattle(isHealPhase);
|
||||
// Must end the TurnInit phase prematurely so battle phases aren't added to queue
|
||||
queueEncounterMessage(scene, `${namespace}:end_game`);
|
||||
queueEncounterMessage(scene, resultMessageKey);
|
||||
queueEncounterMessage(`${namespace}:end_game`);
|
||||
queueEncounterMessage(resultMessageKey);
|
||||
|
||||
// Skip remainder of TurnInitPhase
|
||||
return true;
|
||||
} else {
|
||||
if (encounter.misc.turnsRemaining < 3) {
|
||||
// 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--;
|
||||
}
|
||||
|
||||
@ -298,33 +301,33 @@ function handleNextTurn(scene: BattleScene) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async function showWobbuffetHealthBar(scene: BattleScene) {
|
||||
const wobbuffet = scene.getEnemyPokemon()!;
|
||||
async function showWobbuffetHealthBar() {
|
||||
const wobbuffet = globalScene.getEnemyPokemon()!;
|
||||
|
||||
scene.add.existing(wobbuffet);
|
||||
scene.field.add(wobbuffet);
|
||||
globalScene.add.existing(wobbuffet);
|
||||
globalScene.field.add(wobbuffet);
|
||||
|
||||
const playerPokemon = scene.getPlayerPokemon() as Pokemon;
|
||||
const playerPokemon = globalScene.getPlayerPokemon() as Pokemon;
|
||||
if (playerPokemon?.isOnField()) {
|
||||
scene.field.moveBelow(wobbuffet, playerPokemon);
|
||||
globalScene.field.moveBelow(wobbuffet, playerPokemon);
|
||||
}
|
||||
// Show health bar and trigger cry
|
||||
wobbuffet.showInfo();
|
||||
scene.time.delayedCall(1000, () => {
|
||||
globalScene.time.delayedCall(1000, () => {
|
||||
wobbuffet.cry();
|
||||
});
|
||||
wobbuffet.resetSummonData();
|
||||
|
||||
// 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 => {
|
||||
const pokeball = scene.addFieldSprite(36, 80, "pb", getPokeballAtlasKey(pokemon.pokeball));
|
||||
const pokeball = globalScene.addFieldSprite(36, 80, "pb", getPokeballAtlasKey(pokemon.pokeball));
|
||||
pokeball.setVisible(false);
|
||||
pokeball.setOrigin(0.5, 0.625);
|
||||
scene.field.add(pokeball);
|
||||
globalScene.field.add(pokeball);
|
||||
|
||||
pokemon.setFieldPosition(FieldPosition.CENTER, 0);
|
||||
|
||||
@ -332,32 +335,32 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
|
||||
|
||||
pokeball.setVisible(true);
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: pokeball,
|
||||
duration: 650,
|
||||
x: 100 + fpOffset[0]
|
||||
});
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: pokeball,
|
||||
duration: 150,
|
||||
ease: "Cubic.easeOut",
|
||||
y: 70 + fpOffset[1],
|
||||
onComplete: () => {
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: pokeball,
|
||||
duration: 500,
|
||||
ease: "Cubic.easeIn",
|
||||
angle: 1440,
|
||||
y: 132 + fpOffset[1],
|
||||
onComplete: () => {
|
||||
scene.playSound("se/pb_rel");
|
||||
globalScene.playSound("se/pb_rel");
|
||||
pokeball.destroy();
|
||||
scene.add.existing(pokemon);
|
||||
scene.field.add(pokemon);
|
||||
addPokeballOpenParticles(scene, pokemon.x, pokemon.y - 16, pokemon.pokeball);
|
||||
scene.updateModifiers(true);
|
||||
scene.updateFieldScale();
|
||||
globalScene.add.existing(pokemon);
|
||||
globalScene.field.add(pokemon);
|
||||
addPokeballOpenParticles(pokemon.x, pokemon.y - 16, pokemon.pokeball);
|
||||
globalScene.updateModifiers(true);
|
||||
globalScene.updateFieldScale();
|
||||
pokemon.showInfo();
|
||||
pokemon.playAnim();
|
||||
pokemon.setVisible(true);
|
||||
@ -365,8 +368,8 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
|
||||
pokemon.setScale(0.5);
|
||||
pokemon.tint(getPokeballTintColor(pokemon.pokeball));
|
||||
pokemon.untint(250, "Sine.easeIn");
|
||||
scene.updateFieldScale();
|
||||
scene.tweens.add({
|
||||
globalScene.updateFieldScale();
|
||||
globalScene.tweens.add({
|
||||
targets: pokemon,
|
||||
duration: 250,
|
||||
ease: "Sine.easeIn",
|
||||
@ -375,15 +378,15 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
|
||||
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
|
||||
pokemon.getSprite().clearTint();
|
||||
pokemon.resetSummonData();
|
||||
scene.time.delayedCall(1000, () => {
|
||||
globalScene.time.delayedCall(1000, () => {
|
||||
if (pokemon.isShiny()) {
|
||||
scene.unshiftPhase(new ShinySparklePhase(scene, pokemon.getBattlerIndex()));
|
||||
globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
|
||||
}
|
||||
|
||||
pokemon.resetTurnData();
|
||||
|
||||
scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
||||
scene.pushPhase(new PostSummonPhase(scene, pokemon.getBattlerIndex()));
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
||||
globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex()));
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
@ -395,13 +398,13 @@ function summonPlayerPokemonAnimation(scene: BattleScene, pokemon: PlayerPokemon
|
||||
});
|
||||
}
|
||||
|
||||
function hideShowmanIntroSprite(scene: BattleScene) {
|
||||
const carnivalGame = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(0)[0];
|
||||
const wobbuffet = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1)[0];
|
||||
const showMan = scene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(2)[0];
|
||||
function hideShowmanIntroSprite() {
|
||||
const carnivalGame = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(0)[0];
|
||||
const wobbuffet = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(1)[0];
|
||||
const showMan = globalScene.currentBattle.mysteryEncounter!.introVisuals?.getSpriteAtIndex(2)[0];
|
||||
|
||||
// Hide the showman
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: showMan,
|
||||
x: "+=16",
|
||||
y: "-=16",
|
||||
@ -411,7 +414,7 @@ function hideShowmanIntroSprite(scene: BattleScene) {
|
||||
});
|
||||
|
||||
// Slide the Wobbuffet and Game over slightly
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: [ wobbuffet, carnivalGame ],
|
||||
x: "+=16",
|
||||
ease: "Sine.easeInOut",
|
||||
|
||||
@ -2,20 +2,26 @@ import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterReward
|
||||
import { TrainerSlot, } from "#app/data/trainer-config";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
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 BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { 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 { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, ShinyRateBoosterModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
|
||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
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 i18next from "i18next";
|
||||
import { Gender, getGenderSymbol } from "#app/data/gender";
|
||||
@ -102,24 +108,24 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Load bgm
|
||||
let bgmKey: string;
|
||||
if (scene.musicPreference === MusicPreference.GENFIVE) {
|
||||
if (globalScene.musicPreference === MusicPreference.GENFIVE) {
|
||||
bgmKey = "mystery_encounter_gen_5_gts";
|
||||
scene.loadBgm(bgmKey, `${bgmKey}.mp3`);
|
||||
globalScene.loadBgm(bgmKey, `${bgmKey}.mp3`);
|
||||
} else {
|
||||
// Mixed option
|
||||
bgmKey = "mystery_encounter_gen_6_gts";
|
||||
scene.loadBgm(bgmKey, `${bgmKey}.mp3`);
|
||||
globalScene.loadBgm(bgmKey, `${bgmKey}.mp3`);
|
||||
}
|
||||
|
||||
// Load possible trade options
|
||||
// Maps current party member's id to 3 EnemyPokemon objects
|
||||
// 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 = {
|
||||
tradeOptionsMap,
|
||||
bgmKey
|
||||
@ -127,8 +133,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||
|
||||
return true;
|
||||
})
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
scene.fadeAndSwitchBgm(scene.currentBattle.mysteryEncounter!.misc.bgmKey);
|
||||
.withOnVisualsStart(() => {
|
||||
globalScene.fadeAndSwitchBgm(globalScene.currentBattle.mysteryEncounter!.misc.bgmKey);
|
||||
return true;
|
||||
})
|
||||
.withOption(
|
||||
@ -140,8 +146,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||
buttonTooltip: `${namespace}:option.1.tooltip`,
|
||||
secondOptionPrompt: `${namespace}:option.1.trade_options_prompt`,
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Get the trade species options for the selected pokemon
|
||||
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 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 : "");
|
||||
showEncounterText(scene, `${line1}\n${line2}`, 0, 0, false);
|
||||
showEncounterText(`${line1}\n${line2}`, 0, 0, false);
|
||||
},
|
||||
};
|
||||
return option;
|
||||
});
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected);
|
||||
return selectPokemonForOption(onPokemonSelected);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon;
|
||||
const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon;
|
||||
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());
|
||||
|
||||
// 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
|
||||
receivedPokemonData.passive = tradedPokemon.passive;
|
||||
// Pokeball to Ultra ball, randomly
|
||||
receivedPokemonData.pokeball = randInt(4) as PokeballType;
|
||||
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);
|
||||
scene.getPlayerParty().push(newPlayerPokemon);
|
||||
const newPlayerPokemon = globalScene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
|
||||
globalScene.getPlayerParty().push(newPlayerPokemon);
|
||||
await newPlayerPokemon.loadAssets();
|
||||
|
||||
for (const mod of modifiers) {
|
||||
mod.pokemonId = newPlayerPokemon.id;
|
||||
scene.addModifier(mod, true, false, false, true);
|
||||
globalScene.addModifier(mod, true, false, false, true);
|
||||
}
|
||||
|
||||
// Show the trade animation
|
||||
await showTradeBackground(scene);
|
||||
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
|
||||
await showEncounterText(scene, `${namespace}:trade_received`, null, 0, true, 4000);
|
||||
scene.playBgm(encounter.misc.bgmKey);
|
||||
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon);
|
||||
await hideTradeBackground(scene);
|
||||
await showTradeBackground();
|
||||
await doPokemonTradeSequence(tradedPokemon, newPlayerPokemon);
|
||||
await showEncounterText(`${namespace}:trade_received`, null, 0, true, 4000);
|
||||
globalScene.playBgm(encounter.misc.bgmKey);
|
||||
await addPokemonDataToDexAndValidateAchievements(newPlayerPokemon);
|
||||
await hideTradeBackground();
|
||||
tradedPokemon.destroy();
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -222,19 +228,19 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||
buttonLabel: `${namespace}:option.2.label`,
|
||||
buttonTooltip: `${namespace}:option.2.tooltip`,
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Randomly generate a Wonder Trade pokemon
|
||||
const randomTradeOption = generateTradeOption(scene.getPlayerParty().map(p => p.species));
|
||||
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
|
||||
const randomTradeOption = generateTradeOption(globalScene.getPlayerParty().map(p => p.species));
|
||||
const tradePokemon = new EnemyPokemon(randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
|
||||
// Extra shiny roll at 1/128 odds (boosted by events and charms)
|
||||
if (!tradePokemon.shiny) {
|
||||
const shinyThreshold = new NumberHolder(WONDER_TRADE_SHINY_CHANCE);
|
||||
if (scene.eventManager.isEventActive()) {
|
||||
shinyThreshold.value *= scene.eventManager.getShinyMultiplier();
|
||||
if (globalScene.eventManager.isEventActive()) {
|
||||
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
|
||||
// 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.abilityIndex < hiddenIndex) {
|
||||
const hiddenAbilityChance = new NumberHolder(64);
|
||||
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
|
||||
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||
|
||||
@ -281,10 +287,10 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||
encounter.misc.receivedPokemon = tradePokemon;
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected);
|
||||
return selectPokemonForOption(onPokemonSelected);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon;
|
||||
const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon;
|
||||
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());
|
||||
|
||||
// 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
|
||||
receivedPokemonData.passive = tradedPokemon.passive;
|
||||
receivedPokemonData.pokeball = randInt(4) as PokeballType;
|
||||
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);
|
||||
scene.getPlayerParty().push(newPlayerPokemon);
|
||||
const newPlayerPokemon = globalScene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
|
||||
globalScene.getPlayerParty().push(newPlayerPokemon);
|
||||
await newPlayerPokemon.loadAssets();
|
||||
|
||||
for (const mod of modifiers) {
|
||||
mod.pokemonId = newPlayerPokemon.id;
|
||||
scene.addModifier(mod, true, false, false, true);
|
||||
globalScene.addModifier(mod, true, false, false, true);
|
||||
}
|
||||
|
||||
// Show the trade animation
|
||||
await showTradeBackground(scene);
|
||||
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
|
||||
await showEncounterText(scene, `${namespace}:trade_received`, null, 0, true, 4000);
|
||||
scene.playBgm(encounter.misc.bgmKey);
|
||||
await addPokemonDataToDexAndValidateAchievements(scene, newPlayerPokemon);
|
||||
await hideTradeBackground(scene);
|
||||
await showTradeBackground();
|
||||
await doPokemonTradeSequence(tradedPokemon, newPlayerPokemon);
|
||||
await showEncounterText(`${namespace}:trade_received`, null, 0, true, 4000);
|
||||
globalScene.playBgm(encounter.misc.bgmKey);
|
||||
await addPokemonDataToDexAndValidateAchievements(newPlayerPokemon);
|
||||
await hideTradeBackground();
|
||||
tradedPokemon.destroy();
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -330,8 +336,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||
buttonTooltip: `${namespace}:option.3.tooltip`,
|
||||
secondOptionPrompt: `${namespace}:option.3.trade_options_prompt`,
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Get Pokemon held items and filter for valid ones
|
||||
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||
@ -359,18 +365,18 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||
return it.isTransferable;
|
||||
}).length > 0;
|
||||
if (!meetsReqs) {
|
||||
return getEncounterText(scene, `${namespace}:option.3.invalid_selection`) ?? null;
|
||||
return getEncounterText(`${namespace}:option.3.invalid_selection`) ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||
return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const modifier = encounter.misc.chosenModifier as PokemonHeldItemModifier;
|
||||
const party = scene.getPlayerParty();
|
||||
const party = globalScene.getPlayerParty();
|
||||
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
|
||||
|
||||
// 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);
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: [ item ], fillRemaining: false });
|
||||
|
||||
chosenPokemon.loseHeldItem(modifier, false);
|
||||
await scene.updateModifiers(true, true);
|
||||
await globalScene.updateModifiers(true, true);
|
||||
|
||||
// Generate a trainer name
|
||||
const traderName = generateRandomTraderName();
|
||||
encounter.setDialogueToken("tradeTrainerName", traderName.trim());
|
||||
await showEncounterText(scene, `${namespace}:item_trade_selected`);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
await showEncounterText(`${namespace}:item_trade_selected`);
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -420,26 +426,26 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]> {
|
||||
function getPokemonTradeOptions(): Map<number, EnemyPokemon[]> {
|
||||
const tradeOptionsMap: Map<number, EnemyPokemon[]> = new Map<number, EnemyPokemon[]>();
|
||||
// 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 (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
||||
const generation = pokemon.species.generation;
|
||||
const tradeOptions: EnemyPokemon[] = LEGENDARY_TRADE_POOLS[generation].map(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);
|
||||
} else {
|
||||
@ -454,7 +460,7 @@ function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]>
|
||||
|
||||
// Add trade options to map
|
||||
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!;
|
||||
}
|
||||
|
||||
function showTradeBackground(scene: BattleScene) {
|
||||
function showTradeBackground() {
|
||||
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");
|
||||
|
||||
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.setOrigin(0, 0);
|
||||
flyByStaticBg.setVisible(false);
|
||||
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.setOrigin(0, 0);
|
||||
tradeContainer.add(tradeBaseBg);
|
||||
|
||||
scene.fieldUI.add(tradeContainer);
|
||||
scene.fieldUI.bringToTop(tradeContainer);
|
||||
globalScene.fieldUI.add(tradeContainer);
|
||||
globalScene.fieldUI.bringToTop(tradeContainer);
|
||||
tradeContainer.setVisible(true);
|
||||
tradeContainer.alpha = 0;
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradeContainer,
|
||||
alpha: 1,
|
||||
duration: 500,
|
||||
@ -530,17 +536,17 @@ function showTradeBackground(scene: BattleScene) {
|
||||
});
|
||||
}
|
||||
|
||||
function hideTradeBackground(scene: BattleScene) {
|
||||
function hideTradeBackground() {
|
||||
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,
|
||||
alpha: 0,
|
||||
duration: 1000,
|
||||
ease: "Sine.easeInOut",
|
||||
onComplete: () => {
|
||||
scene.fieldUI.remove(transformationContainer, true);
|
||||
globalScene.fieldUI.remove(transformationContainer, true);
|
||||
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.
|
||||
* @param scene
|
||||
* @param tradedPokemon
|
||||
* @param receivedPokemon
|
||||
*/
|
||||
function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon, receivedPokemon: PlayerPokemon) {
|
||||
function doPokemonTradeSequence(tradedPokemon: PlayerPokemon, receivedPokemon: PlayerPokemon) {
|
||||
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;
|
||||
|
||||
let tradedPokemonSprite: Phaser.GameObjects.Sprite;
|
||||
@ -564,8 +569,8 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
|
||||
let receivedPokemonTintSprite: Phaser.GameObjects.Sprite;
|
||||
|
||||
const getPokemonSprite = () => {
|
||||
const ret = scene.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 });
|
||||
const ret = globalScene.addPokemonSprite(tradedPokemon, tradeBaseBg.displayWidth / 2, tradeBaseBg.displayHeight / 2, "pkmn__sub");
|
||||
ret.setPipeline(globalScene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
|
||||
return ret;
|
||||
};
|
||||
|
||||
@ -589,7 +594,7 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
|
||||
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("spriteKey", tradedPokemon.getSpriteKey());
|
||||
sprite.setPipelineData("shiny", tradedPokemon.shiny);
|
||||
@ -610,7 +615,7 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
|
||||
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("spriteKey", receivedPokemon.getSpriteKey());
|
||||
sprite.setPipelineData("shiny", receivedPokemon.shiny);
|
||||
@ -625,45 +630,45 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
|
||||
|
||||
// Traded pokemon 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);
|
||||
tradeContainer.add(tradedPokeball);
|
||||
|
||||
// Received pokemon 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);
|
||||
tradeContainer.add(receivedPokeball);
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradedPokemonSprite,
|
||||
alpha: 1,
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: 500,
|
||||
onComplete: async () => {
|
||||
scene.fadeOutBgm(1000, false);
|
||||
await showEncounterText(scene, `${namespace}:pokemon_trade_selected`);
|
||||
globalScene.fadeOutBgm(1000, false);
|
||||
await showEncounterText(`${namespace}:pokemon_trade_selected`);
|
||||
tradedPokemon.cry();
|
||||
scene.playBgm("evolution");
|
||||
await showEncounterText(scene, `${namespace}:pokemon_trade_goodbye`);
|
||||
globalScene.playBgm("evolution");
|
||||
await showEncounterText(`${namespace}:pokemon_trade_goodbye`);
|
||||
|
||||
tradedPokeball.setAlpha(0);
|
||||
tradedPokeball.setVisible(true);
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradedPokeball,
|
||||
alpha: 1,
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: 250,
|
||||
onComplete: () => {
|
||||
tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`);
|
||||
scene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_open`));
|
||||
scene.playSound("se/pb_rel");
|
||||
globalScene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_open`));
|
||||
globalScene.playSound("se/pb_rel");
|
||||
tradedPokemonTintSprite.setVisible(true);
|
||||
|
||||
// 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 ],
|
||||
duration: 500,
|
||||
ease: "Sine.easeIn",
|
||||
@ -672,30 +677,30 @@ function doPokemonTradeSequence(scene: BattleScene, tradedPokemon: PlayerPokemon
|
||||
tradedPokemonSprite.setVisible(false);
|
||||
tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}_opening`);
|
||||
tradedPokemonTintSprite.setVisible(false);
|
||||
scene.playSound("se/pb_catch");
|
||||
scene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}`));
|
||||
globalScene.playSound("se/pb_catch");
|
||||
globalScene.time.delayedCall(17, () => tradedPokeball.setTexture("pb", `${tradedPbAtlasKey}`));
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradedPokeball,
|
||||
y: "+=10",
|
||||
duration: 200,
|
||||
delay: 250,
|
||||
ease: "Cubic.easeIn",
|
||||
onComplete: () => {
|
||||
scene.playSound("se/pb_bounce_1");
|
||||
globalScene.playSound("se/pb_bounce_1");
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradedPokeball,
|
||||
y: "-=100",
|
||||
duration: 200,
|
||||
delay: 1000,
|
||||
ease: "Cubic.easeInOut",
|
||||
onStart: () => {
|
||||
scene.playSound("se/pb_throw");
|
||||
globalScene.playSound("se/pb_throw");
|
||||
},
|
||||
onComplete: async () => {
|
||||
await doPokemonTradeFlyBySequence(scene, tradedPokemonSprite, receivedPokemonSprite);
|
||||
await doTradeReceivedSequence(scene, receivedPokemon, receivedPokemonSprite, receivedPokemonTintSprite, receivedPokeball, receivedPbAtlasKey);
|
||||
await doPokemonTradeFlyBySequence(tradedPokemonSprite, receivedPokemonSprite);
|
||||
await doTradeReceivedSequence(receivedPokemon, receivedPokemonSprite, receivedPokemonTintSprite, receivedPokeball, receivedPbAtlasKey);
|
||||
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 => {
|
||||
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 flyByStaticBg = tradeContainer.getByName("Black Background") as Phaser.GameObjects.Rectangle;
|
||||
flyByStaticBg.setVisible(true);
|
||||
@ -733,47 +738,47 @@ function doPokemonTradeFlyBySequence(scene: BattleScene, tradedPokemonSprite: Ph
|
||||
const BASE_ANIM_DURATION = 1000;
|
||||
|
||||
// Fade out trade background
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradeBaseBg,
|
||||
alpha: 0,
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: FADE_DELAY,
|
||||
onComplete: () => {
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: [ receivedPokemonSprite, tradedPokemonSprite ],
|
||||
y: tradeBaseBg.displayWidth / 2 - 100,
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: BASE_ANIM_DURATION * 3,
|
||||
onComplete: () => {
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: receivedPokemonSprite,
|
||||
x: tradeBaseBg.displayWidth / 4,
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: BASE_ANIM_DURATION / 2,
|
||||
delay: ANIM_DELAY
|
||||
});
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradedPokemonSprite,
|
||||
x: tradeBaseBg.displayWidth * 3 / 4,
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: BASE_ANIM_DURATION / 2,
|
||||
delay: ANIM_DELAY,
|
||||
onComplete: () => {
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: receivedPokemonSprite,
|
||||
y: "+=200",
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: BASE_ANIM_DURATION * 2,
|
||||
delay: ANIM_DELAY,
|
||||
});
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradedPokemonSprite,
|
||||
y: "-=200",
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: BASE_ANIM_DURATION * 2,
|
||||
delay: ANIM_DELAY,
|
||||
onComplete: () => {
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: tradeBaseBg,
|
||||
alpha: 1,
|
||||
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 => {
|
||||
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;
|
||||
|
||||
receivedPokemonSprite.setVisible(false);
|
||||
@ -812,7 +817,7 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
|
||||
// Received pokemon sparkles
|
||||
let pokemonShinySparkle: Phaser.GameObjects.Sprite;
|
||||
if (receivedPokemon.shiny) {
|
||||
pokemonShinySparkle = scene.add.sprite(receivedPokemonSprite.x, receivedPokemonSprite.y, "shiny");
|
||||
pokemonShinySparkle = globalScene.add.sprite(receivedPokemonSprite.x, receivedPokemonSprite.y, "shiny");
|
||||
pokemonShinySparkle.setVisible(false);
|
||||
tradeContainer.add(pokemonShinySparkle);
|
||||
}
|
||||
@ -820,19 +825,19 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
|
||||
const BASE_ANIM_DURATION = 1000;
|
||||
|
||||
// Pokeball falls to the screen
|
||||
scene.playSound("se/pb_throw");
|
||||
scene.tweens.add({
|
||||
globalScene.playSound("se/pb_throw");
|
||||
globalScene.tweens.add({
|
||||
targets: receivedPokeballSprite,
|
||||
y: "+=100",
|
||||
ease: "Cubic.easeInOut",
|
||||
duration: BASE_ANIM_DURATION,
|
||||
onComplete: () => {
|
||||
scene.playSound("se/pb_bounce_1");
|
||||
scene.time.delayedCall(100, () => scene.playSound("se/pb_bounce_1"));
|
||||
globalScene.playSound("se/pb_bounce_1");
|
||||
globalScene.time.delayedCall(100, () => globalScene.playSound("se/pb_bounce_1"));
|
||||
|
||||
scene.time.delayedCall(2000, () => {
|
||||
scene.playSound("se/pb_rel");
|
||||
scene.fadeOutBgm(500, false);
|
||||
globalScene.time.delayedCall(2000, () => {
|
||||
globalScene.playSound("se/pb_rel");
|
||||
globalScene.fadeOutBgm(500, false);
|
||||
receivedPokemon.cry();
|
||||
receivedPokemonTintSprite.scale = 0.25;
|
||||
receivedPokemonTintSprite.alpha = 1;
|
||||
@ -841,14 +846,14 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
|
||||
receivedPokemonTintSprite.alpha = 1;
|
||||
receivedPokemonTintSprite.setVisible(true);
|
||||
receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_opening`);
|
||||
scene.time.delayedCall(17, () => receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_open`));
|
||||
scene.tweens.add({
|
||||
globalScene.time.delayedCall(17, () => receivedPokeballSprite.setTexture("pb", `${receivedPbAtlasKey}_open`));
|
||||
globalScene.tweens.add({
|
||||
targets: receivedPokemonSprite,
|
||||
duration: 250,
|
||||
ease: "Sine.easeOut",
|
||||
scale: 1
|
||||
});
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: receivedPokemonTintSprite,
|
||||
duration: 250,
|
||||
ease: "Sine.easeOut",
|
||||
@ -856,12 +861,12 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
|
||||
alpha: 0,
|
||||
onComplete: () => {
|
||||
if (receivedPokemon.shiny) {
|
||||
scene.time.delayedCall(500, () => {
|
||||
doShinySparkleAnim(scene, pokemonShinySparkle, receivedPokemon.variant);
|
||||
globalScene.time.delayedCall(500, () => {
|
||||
doShinySparkleAnim(pokemonShinySparkle, receivedPokemon.variant);
|
||||
});
|
||||
}
|
||||
receivedPokeballSprite.destroy();
|
||||
scene.time.delayedCall(2000, () => resolve());
|
||||
globalScene.time.delayedCall(2000, () => resolve());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -2,8 +2,9 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { Species } from "#app/enums/species";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter-phase-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` }])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE));
|
||||
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()
|
||||
)
|
||||
.withOption(
|
||||
@ -89,7 +90,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => handlePokemonGuidingYouPhase(scene))
|
||||
.withOptionPhase(async () => handlePokemonGuidingYouPhase())
|
||||
.build()
|
||||
)
|
||||
.withSimpleOption(
|
||||
@ -103,16 +104,16 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const allowedPokemon = scene.getPlayerParty().filter((p) => p.isAllowedInBattle());
|
||||
async () => {
|
||||
const allowedPokemon = globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle());
|
||||
|
||||
for (const pkm of allowedPokemon) {
|
||||
const percentage = DAMAGE_PERCENTAGE / 100;
|
||||
const damage = Math.floor(pkm.getMaxHp() * percentage);
|
||||
applyDamageToPokemon(scene, pkm, damage);
|
||||
applyDamageToPokemon(pkm, damage);
|
||||
}
|
||||
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
leaveEncounterWithoutBattle();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -126,19 +127,17 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
|
||||
/**
|
||||
* 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 { mysteryEncounter } = scene.currentBattle;
|
||||
const { mysteryEncounter } = globalScene.currentBattle;
|
||||
|
||||
if (mysteryEncounter?.selectedOption?.primaryPokemon?.id) {
|
||||
setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true);
|
||||
setEncounterExp(mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true);
|
||||
} else {
|
||||
console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?");
|
||||
}
|
||||
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
leaveEncounterWithoutBattle();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import type {
|
||||
EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import {
|
||||
EnemyPartyConfig,
|
||||
initBattleWithEnemyConfig,
|
||||
setEncounterRewards,
|
||||
} 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 { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
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 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 { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
|
||||
@ -37,12 +39,12 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Calculates what trainers are available for battle in the encounter
|
||||
|
||||
// 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();
|
||||
let female = false;
|
||||
if (normalConfig.hasGenders) {
|
||||
@ -57,16 +59,16 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
|
||||
// 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
|
||||
let retries = 0;
|
||||
let hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex);
|
||||
let hardTrainerType = globalScene.arena.randomTrainerType(globalScene.currentBattle.waveIndex);
|
||||
while (retries < 5 && hardTrainerType === normalTrainerType) {
|
||||
// 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++;
|
||||
}
|
||||
const hardTemplate = new TrainerPartyCompoundTemplate(
|
||||
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true),
|
||||
new TrainerPartyTemplate(
|
||||
Math.min(Math.ceil(scene.currentBattle.waveIndex / 20), 5),
|
||||
Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 20), 5),
|
||||
PartyMemberStrength.AVERAGE,
|
||||
false,
|
||||
true
|
||||
@ -87,8 +89,8 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
|
||||
|
||||
// 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
|
||||
const brutalTrainerType = scene.arena.randomTrainerType(
|
||||
scene.currentBattle.waveIndex,
|
||||
const brutalTrainerType = globalScene.arena.randomTrainerType(
|
||||
globalScene.currentBattle.waveIndex,
|
||||
true
|
||||
);
|
||||
const e4Template = trainerPartyTemplates.ELITE_FOUR;
|
||||
@ -145,18 +147,18 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Spawn standard trainer battle with memory mushroom reward
|
||||
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
|
||||
let initBattlePromise: Promise<void>;
|
||||
scene.executeWithSeedOffset(() => {
|
||||
initBattlePromise = initBattleWithEnemyConfig(scene, config);
|
||||
}, scene.currentBattle.waveIndex * 10);
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
initBattlePromise = initBattleWithEnemyConfig(config);
|
||||
}, globalScene.currentBattle.waveIndex * 10);
|
||||
await initBattlePromise!;
|
||||
}
|
||||
)
|
||||
@ -170,18 +172,18 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Spawn hard fight
|
||||
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
|
||||
let initBattlePromise: Promise<void>;
|
||||
scene.executeWithSeedOffset(() => {
|
||||
initBattlePromise = initBattleWithEnemyConfig(scene, config);
|
||||
}, scene.currentBattle.waveIndex * 100);
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
initBattlePromise = initBattleWithEnemyConfig(config);
|
||||
}, globalScene.currentBattle.waveIndex * 100);
|
||||
await initBattlePromise!;
|
||||
}
|
||||
)
|
||||
@ -195,21 +197,21 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Spawn brutal fight
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2];
|
||||
|
||||
// To avoid player level snowballing from picking this option
|
||||
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
|
||||
let initBattlePromise: Promise<void>;
|
||||
scene.executeWithSeedOffset(() => {
|
||||
initBattlePromise = initBattleWithEnemyConfig(scene, config);
|
||||
}, scene.currentBattle.waveIndex * 1000);
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
initBattlePromise = initBattleWithEnemyConfig(config);
|
||||
}, globalScene.currentBattle.waveIndex * 1000);
|
||||
await initBattlePromise!;
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { 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 { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
@ -66,8 +68,8 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mon
|
||||
const config: EnemyPartyConfig = {
|
||||
@ -106,9 +108,9 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Play animation
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const introVisuals = encounter.introVisuals!;
|
||||
|
||||
// Determine roll first
|
||||
@ -128,13 +130,13 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
||||
introVisuals.spriteConfigs[1].disableAnimation = false;
|
||||
introVisuals.playAnim();
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Open the chest
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const roll = encounter.misc.roll;
|
||||
if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT) {
|
||||
// Choose between 2 COMMON / 2 GREAT tier items (20%)
|
||||
setEncounterRewards(scene, {
|
||||
setEncounterRewards({
|
||||
guaranteedModifierTiers: [
|
||||
ModifierTier.COMMON,
|
||||
ModifierTier.COMMON,
|
||||
@ -143,11 +145,11 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
||||
],
|
||||
});
|
||||
// Display result message then proceed to rewards
|
||||
queueEncounterMessage(scene, `${namespace}:option.1.normal`);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
queueEncounterMessage(`${namespace}:option.1.normal`);
|
||||
leaveEncounterWithoutBattle();
|
||||
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) {
|
||||
// Choose between 3 ULTRA tier items (30%)
|
||||
setEncounterRewards(scene, {
|
||||
setEncounterRewards({
|
||||
guaranteedModifierTiers: [
|
||||
ModifierTier.ULTRA,
|
||||
ModifierTier.ULTRA,
|
||||
@ -155,39 +157,39 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
||||
],
|
||||
});
|
||||
// Display result message then proceed to rewards
|
||||
queueEncounterMessage(scene, `${namespace}:option.1.good`);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
queueEncounterMessage(`${namespace}:option.1.good`);
|
||||
leaveEncounterWithoutBattle();
|
||||
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) {
|
||||
// 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
|
||||
queueEncounterMessage(scene, `${namespace}:option.1.great`);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
queueEncounterMessage(`${namespace}:option.1.great`);
|
||||
leaveEncounterWithoutBattle();
|
||||
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT - MASTER_REWARDS_PERCENT) {
|
||||
// Choose 1 MASTER tier item (5%)
|
||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.MASTER ]});
|
||||
setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.MASTER ]});
|
||||
// Display result message then proceed to rewards
|
||||
queueEncounterMessage(scene, `${namespace}:option.1.amazing`);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
queueEncounterMessage(`${namespace}:option.1.amazing`);
|
||||
leaveEncounterWithoutBattle();
|
||||
} else {
|
||||
// Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%)
|
||||
const highestLevelPokemon = getHighestLevelPlayerPokemon(scene, true, false);
|
||||
koPlayerPokemon(scene, highestLevelPokemon);
|
||||
const highestLevelPokemon = getHighestLevelPlayerPokemon(true, false);
|
||||
koPlayerPokemon(highestLevelPokemon);
|
||||
|
||||
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
|
||||
await showEncounterText(scene, `${namespace}:option.1.bad`);
|
||||
await showEncounterText(`${namespace}:option.1.bad`);
|
||||
|
||||
// Handle game over edge case
|
||||
const allowedPokemon = scene.getPokemonAllowedInBattle();
|
||||
const allowedPokemon = globalScene.getPokemonAllowedInBattle();
|
||||
if (allowedPokemon.length === 0) {
|
||||
// If there are no longer any legal pokemon in the party, game over.
|
||||
scene.clearPhaseQueue();
|
||||
scene.unshiftPhase(new GameOverPhase(scene));
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.unshiftPhase(new GameOverPhase());
|
||||
} else {
|
||||
// Show which Pokemon was KOed, then start battle against Gimmighoul
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -203,9 +205,9 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
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 { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
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 { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
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 { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
|
||||
@ -52,20 +54,20 @@ export const PartTimerEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro_dialogue`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
.withOnInit(() => {
|
||||
// Load sfx
|
||||
scene.loadSe("PRSFX- Horn Drill1", "battle_anims", "PRSFX- Horn Drill1.wav");
|
||||
scene.loadSe("PRSFX- Horn Drill3", "battle_anims", "PRSFX- Horn Drill3.wav");
|
||||
scene.loadSe("PRSFX- Guillotine2", "battle_anims", "PRSFX- Guillotine2.wav");
|
||||
scene.loadSe("PRSFX- Heavy Slam2", "battle_anims", "PRSFX- Heavy Slam2.wav");
|
||||
globalScene.loadSe("PRSFX- Horn Drill1", "battle_anims", "PRSFX- Horn Drill1.wav");
|
||||
globalScene.loadSe("PRSFX- Horn Drill3", "battle_anims", "PRSFX- Horn Drill3.wav");
|
||||
globalScene.loadSe("PRSFX- Guillotine2", "battle_anims", "PRSFX- Guillotine2.wav");
|
||||
globalScene.loadSe("PRSFX- Heavy Slam2", "battle_anims", "PRSFX- Heavy Slam2.wav");
|
||||
|
||||
scene.loadSe("PRSFX- Agility", "battle_anims", "PRSFX- Agility.wav");
|
||||
scene.loadSe("PRSFX- Extremespeed1", "battle_anims", "PRSFX- Extremespeed1.wav");
|
||||
scene.loadSe("PRSFX- Accelerock1", "battle_anims", "PRSFX- Accelerock1.wav");
|
||||
globalScene.loadSe("PRSFX- Agility", "battle_anims", "PRSFX- Agility.wav");
|
||||
globalScene.loadSe("PRSFX- Extremespeed1", "battle_anims", "PRSFX- Extremespeed1.wav");
|
||||
globalScene.loadSe("PRSFX- Accelerock1", "battle_anims", "PRSFX- Accelerock1.wav");
|
||||
|
||||
scene.loadSe("PRSFX- Captivate", "battle_anims", "PRSFX- Captivate.wav");
|
||||
scene.loadSe("PRSFX- Attract2", "battle_anims", "PRSFX- Attract2.wav");
|
||||
scene.loadSe("PRSFX- Aurora Veil2", "battle_anims", "PRSFX- Aurora Veil2.wav");
|
||||
globalScene.loadSe("PRSFX- Captivate", "battle_anims", "PRSFX- Captivate.wav");
|
||||
globalScene.loadSe("PRSFX- Attract2", "battle_anims", "PRSFX- Attract2.wav");
|
||||
globalScene.loadSe("PRSFX- Aurora Veil2", "battle_anims", "PRSFX- Aurora Veil2.wav");
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -84,8 +86,8 @@ export const PartTimerEncounter: MysteryEncounter =
|
||||
}
|
||||
]
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
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
|
||||
transitionMysteryEncounterIntroVisuals(scene, true, false);
|
||||
transitionMysteryEncounterIntroVisuals(true, false);
|
||||
// Play sfx for "working"
|
||||
doDeliverySfx(scene);
|
||||
doDeliverySfx();
|
||||
};
|
||||
|
||||
// Only Pokemon non-KOd pokemon can be selected
|
||||
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
|
||||
// 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
|
||||
if (moneyMultiplier > 2.5) {
|
||||
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`);
|
||||
await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
|
||||
} else {
|
||||
await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`);
|
||||
await showEncounterDialogue(`${namespace}:job_complete_bad`, `${namespace}:speaker`);
|
||||
}
|
||||
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier);
|
||||
updatePlayerMoney(scene, moneyChange, true, false);
|
||||
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
|
||||
await showEncounterText(scene, `${namespace}:pokemon_tired`);
|
||||
const moneyChange = globalScene.getWaveMoneyAmount(moneyMultiplier);
|
||||
updatePlayerMoney(moneyChange, true, false);
|
||||
await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
|
||||
await showEncounterText(`${namespace}:pokemon_tired`);
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -158,8 +160,8 @@ export const PartTimerEncounter: MysteryEncounter =
|
||||
}
|
||||
]
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
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
|
||||
transitionMysteryEncounterIntroVisuals(scene, true, false);
|
||||
transitionMysteryEncounterIntroVisuals(true, false);
|
||||
// Play sfx for "working"
|
||||
doStrongWorkSfx(scene);
|
||||
doStrongWorkSfx();
|
||||
};
|
||||
|
||||
// Only Pokemon non-KOd pokemon can be selected
|
||||
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
|
||||
// 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
|
||||
if (moneyMultiplier > 2.5) {
|
||||
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`);
|
||||
await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
|
||||
} else {
|
||||
await showEncounterDialogue(scene, `${namespace}:job_complete_bad`, `${namespace}:speaker`);
|
||||
await showEncounterDialogue(`${namespace}:job_complete_bad`, `${namespace}:speaker`);
|
||||
}
|
||||
const moneyChange = scene.getWaveMoneyAmount(moneyMultiplier);
|
||||
updatePlayerMoney(scene, moneyChange, true, false);
|
||||
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
|
||||
await showEncounterText(scene, `${namespace}:pokemon_tired`);
|
||||
const moneyChange = globalScene.getWaveMoneyAmount(moneyMultiplier);
|
||||
updatePlayerMoney(moneyChange, true, false);
|
||||
await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
|
||||
await showEncounterText(`${namespace}:pokemon_tired`);
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -238,8 +240,8 @@ export const PartTimerEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const selectedPokemon = encounter.selectedOption?.primaryPokemon!;
|
||||
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
|
||||
transitionMysteryEncounterIntroVisuals(scene, true, false);
|
||||
transitionMysteryEncounterIntroVisuals(true, false);
|
||||
// Play sfx for "working"
|
||||
doSalesSfx(scene);
|
||||
doSalesSfx();
|
||||
return true;
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Assist with Sales
|
||||
// Bring visuals back in
|
||||
await transitionMysteryEncounterIntroVisuals(scene, false, false);
|
||||
await transitionMysteryEncounterIntroVisuals(false, false);
|
||||
|
||||
// Give money and do dialogue
|
||||
await showEncounterDialogue(scene, `${namespace}:job_complete_good`, `${namespace}:speaker`);
|
||||
const moneyChange = scene.getWaveMoneyAmount(2.5);
|
||||
updatePlayerMoney(scene, moneyChange, true, false);
|
||||
await showEncounterText(scene, i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
|
||||
await showEncounterText(scene, `${namespace}:pokemon_tired`);
|
||||
await showEncounterDialogue(`${namespace}:job_complete_good`, `${namespace}:speaker`);
|
||||
const moneyChange = globalScene.getWaveMoneyAmount(2.5);
|
||||
updatePlayerMoney(moneyChange, true, false);
|
||||
await showEncounterText(i18next.t("mysteryEncounterMessages:receive_money", { amount: moneyChange }));
|
||||
await showEncounterText(`${namespace}:pokemon_tired`);
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -284,51 +286,51 @@ export const PartTimerEncounter: MysteryEncounter =
|
||||
])
|
||||
.build();
|
||||
|
||||
function doStrongWorkSfx(scene: BattleScene) {
|
||||
scene.playSound("battle_anims/PRSFX- Horn Drill1");
|
||||
scene.playSound("battle_anims/PRSFX- Horn Drill1");
|
||||
function doStrongWorkSfx() {
|
||||
globalScene.playSound("battle_anims/PRSFX- Horn Drill1");
|
||||
globalScene.playSound("battle_anims/PRSFX- Horn Drill1");
|
||||
|
||||
scene.time.delayedCall(1000, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Guillotine2");
|
||||
globalScene.time.delayedCall(1000, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Guillotine2");
|
||||
});
|
||||
|
||||
scene.time.delayedCall(2000, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Heavy Slam2");
|
||||
globalScene.time.delayedCall(2000, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Heavy Slam2");
|
||||
});
|
||||
|
||||
scene.time.delayedCall(2500, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Guillotine2");
|
||||
globalScene.time.delayedCall(2500, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Guillotine2");
|
||||
});
|
||||
}
|
||||
|
||||
function doDeliverySfx(scene: BattleScene) {
|
||||
scene.playSound("battle_anims/PRSFX- Accelerock1");
|
||||
function doDeliverySfx() {
|
||||
globalScene.playSound("battle_anims/PRSFX- Accelerock1");
|
||||
|
||||
scene.time.delayedCall(1500, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Extremespeed1");
|
||||
globalScene.time.delayedCall(1500, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Extremespeed1");
|
||||
});
|
||||
|
||||
scene.time.delayedCall(2000, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Extremespeed1");
|
||||
globalScene.time.delayedCall(2000, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Extremespeed1");
|
||||
});
|
||||
|
||||
scene.time.delayedCall(2250, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Agility");
|
||||
globalScene.time.delayedCall(2250, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Agility");
|
||||
});
|
||||
}
|
||||
|
||||
function doSalesSfx(scene: BattleScene) {
|
||||
scene.playSound("battle_anims/PRSFX- Captivate");
|
||||
function doSalesSfx() {
|
||||
globalScene.playSound("battle_anims/PRSFX- Captivate");
|
||||
|
||||
scene.time.delayedCall(1500, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Attract2");
|
||||
globalScene.time.delayedCall(1500, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Attract2");
|
||||
});
|
||||
|
||||
scene.time.delayedCall(2000, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Aurora Veil2");
|
||||
globalScene.time.delayedCall(2000, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Aurora Veil2");
|
||||
});
|
||||
|
||||
scene.time.delayedCall(3000, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Attract2");
|
||||
globalScene.time.delayedCall(3000, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Attract2");
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import { initSubsequentOptionSelect, leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import MysteryEncounterOption, { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import type MysteryEncounterOption 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 { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifier/modifier";
|
||||
import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { IntegerHolder, randSeedInt } from "#app/utils";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { doPlayerFlee, doPokemonFlee, getRandomSpeciesByStarterCost, trainerThrowPokeball } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
@ -59,8 +62,8 @@ export const SafariZoneEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
scene.currentBattle.mysteryEncounter?.setDialogueToken("numEncounters", NUM_SAFARI_ENCOUNTERS.toString());
|
||||
.withOnInit(() => {
|
||||
globalScene.currentBattle.mysteryEncounter?.setDialogueToken("numEncounters", NUM_SAFARI_ENCOUNTERS.toString());
|
||||
return true;
|
||||
})
|
||||
.withOption(MysteryEncounterOptionBuilder
|
||||
@ -75,25 +78,25 @@ export const SafariZoneEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Start safari encounter
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
encounter.continuousEncounter = true;
|
||||
encounter.misc = {
|
||||
safariPokemonRemaining: NUM_SAFARI_ENCOUNTERS
|
||||
};
|
||||
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
||||
updatePlayerMoney(-(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
||||
// Load bait/mud assets
|
||||
scene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav");
|
||||
scene.loadSe("PRSFX- Sludge Bomb2", "battle_anims", "PRSFX- Sludge Bomb2.wav");
|
||||
scene.loadSe("PRSFX- Taunt2", "battle_anims", "PRSFX- Taunt2.wav");
|
||||
scene.loadAtlas("safari_zone_bait", "mystery-encounters");
|
||||
scene.loadAtlas("safari_zone_mud", "mystery-encounters");
|
||||
globalScene.loadSe("PRSFX- Bug Bite", "battle_anims", "PRSFX- Bug Bite.wav");
|
||||
globalScene.loadSe("PRSFX- Sludge Bomb2", "battle_anims", "PRSFX- Sludge Bomb2.wav");
|
||||
globalScene.loadSe("PRSFX- Taunt2", "battle_anims", "PRSFX- Taunt2.wav");
|
||||
globalScene.loadAtlas("safari_zone_bait", "mystery-encounters");
|
||||
globalScene.loadAtlas("safari_zone_mud", "mystery-encounters");
|
||||
// Clear enemy party
|
||||
scene.currentBattle.enemyParty = [];
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
await summonSafariPokemon(scene);
|
||||
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, hideDescription: true });
|
||||
globalScene.currentBattle.enemyParty = [];
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
await summonSafariPokemon();
|
||||
initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, hideDescription: true });
|
||||
return true;
|
||||
})
|
||||
.build()
|
||||
@ -108,9 +111,9 @@ export const SafariZoneEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
@ -143,26 +146,26 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
||||
}
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Throw a ball option
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const pokemon = encounter.misc.pokemon;
|
||||
const catchResult = await throwPokeball(scene, pokemon);
|
||||
const catchResult = await throwPokeball(pokemon);
|
||||
|
||||
if (catchResult) {
|
||||
// You caught pokemon
|
||||
// Check how many safari pokemon left
|
||||
if (encounter.misc.safariPokemonRemaining > 0) {
|
||||
await summonSafariPokemon(scene);
|
||||
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: 0, hideDescription: true });
|
||||
await summonSafariPokemon();
|
||||
initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: 0, hideDescription: true });
|
||||
} else {
|
||||
// End safari mode
|
||||
encounter.continuousEncounter = false;
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
}
|
||||
} else {
|
||||
// Pokemon catch failed, end turn
|
||||
await doEndTurn(scene, 0);
|
||||
await doEndTurn(0);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
@ -178,22 +181,22 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Throw bait option
|
||||
const pokemon = scene.currentBattle.mysteryEncounter!.misc.pokemon;
|
||||
await throwBait(scene, pokemon);
|
||||
const pokemon = globalScene.currentBattle.mysteryEncounter!.misc.pokemon;
|
||||
await throwBait(pokemon);
|
||||
|
||||
// 100% chance to increase catch stage +2
|
||||
tryChangeCatchStage(scene, 2);
|
||||
tryChangeCatchStage(2);
|
||||
// 80% chance to increase flee stage +1
|
||||
const fleeChangeResult = tryChangeFleeStage(scene, 1, 8);
|
||||
const fleeChangeResult = tryChangeFleeStage(1, 8);
|
||||
if (!fleeChangeResult) {
|
||||
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.busy_eating`) ?? "", null, 1000, false );
|
||||
await showEncounterText(getEncounterText(`${namespace}:safari.busy_eating`) ?? "", null, 1000, false );
|
||||
} else {
|
||||
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.eating`) ?? "", null, 1000, false);
|
||||
await showEncounterText(getEncounterText(`${namespace}:safari.eating`) ?? "", null, 1000, false);
|
||||
}
|
||||
|
||||
await doEndTurn(scene, 1);
|
||||
await doEndTurn(1);
|
||||
return true;
|
||||
})
|
||||
.build(),
|
||||
@ -208,21 +211,21 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Throw mud option
|
||||
const pokemon = scene.currentBattle.mysteryEncounter!.misc.pokemon;
|
||||
await throwMud(scene, pokemon);
|
||||
const pokemon = globalScene.currentBattle.mysteryEncounter!.misc.pokemon;
|
||||
await throwMud(pokemon);
|
||||
// 100% chance to decrease flee stage -2
|
||||
tryChangeFleeStage(scene, -2);
|
||||
tryChangeFleeStage(-2);
|
||||
// 80% chance to decrease catch stage -1
|
||||
const catchChangeResult = tryChangeCatchStage(scene, -1, 8);
|
||||
const catchChangeResult = tryChangeCatchStage(-1, 8);
|
||||
if (!catchChangeResult) {
|
||||
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.beside_itself_angry`) ?? "", null, 1000, false );
|
||||
await showEncounterText(getEncounterText(`${namespace}:safari.beside_itself_angry`) ?? "", null, 1000, false );
|
||||
} else {
|
||||
await showEncounterText(scene, getEncounterText(scene, `${namespace}:safari.angry`) ?? "", null, 1000, false );
|
||||
await showEncounterText(getEncounterText(`${namespace}:safari.angry`) ?? "", null, 1000, false );
|
||||
}
|
||||
|
||||
await doEndTurn(scene, 2);
|
||||
await doEndTurn(2);
|
||||
return true;
|
||||
})
|
||||
.build(),
|
||||
@ -232,40 +235,40 @@ const safariZoneGameOptions: MysteryEncounterOption[] = [
|
||||
buttonLabel: `${namespace}:safari.4.label`,
|
||||
buttonTooltip: `${namespace}:safari.4.tooltip`,
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Flee option
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const pokemon = encounter.misc.pokemon;
|
||||
await doPlayerFlee(scene, pokemon);
|
||||
await doPlayerFlee(pokemon);
|
||||
// Check how many safari pokemon left
|
||||
if (encounter.misc.safariPokemonRemaining > 0) {
|
||||
await summonSafariPokemon(scene);
|
||||
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: 3, hideDescription: true });
|
||||
await summonSafariPokemon();
|
||||
initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: 3, hideDescription: true });
|
||||
} else {
|
||||
// End safari mode
|
||||
encounter.continuousEncounter = false;
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.build()
|
||||
];
|
||||
|
||||
async function summonSafariPokemon(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async function summonSafariPokemon() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Message pokemon remaining
|
||||
encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining);
|
||||
scene.queueMessage(getEncounterText(scene, `${namespace}:safari.remaining_count`) ?? "", null, true);
|
||||
globalScene.queueMessage(getEncounterText(`${namespace}:safari.remaining_count`) ?? "", null, true);
|
||||
|
||||
// Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken
|
||||
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
|
||||
let enemySpecies;
|
||||
let pokemon;
|
||||
scene.executeWithSeedOffset(() => {
|
||||
globalScene.executeWithSeedOffset(() => {
|
||||
enemySpecies = getSafariSpeciesSpawn();
|
||||
const level = scene.currentBattle.getLevelForWave();
|
||||
enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(level, true, false, scene.gameMode));
|
||||
pokemon = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, false);
|
||||
const level = globalScene.currentBattle.getLevelForWave();
|
||||
enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(level, true, false, globalScene.gameMode));
|
||||
pokemon = globalScene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, false);
|
||||
|
||||
// Roll shiny twice
|
||||
if (!pokemon.shiny) {
|
||||
@ -277,7 +280,7 @@ async function summonSafariPokemon(scene: BattleScene) {
|
||||
const hiddenIndex = pokemon.species.ability2 ? 2 : 1;
|
||||
if (pokemon.abilityIndex < hiddenIndex) {
|
||||
const hiddenAbilityChance = new IntegerHolder(256);
|
||||
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
|
||||
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||
|
||||
@ -289,10 +292,10 @@ async function summonSafariPokemon(scene: BattleScene) {
|
||||
|
||||
pokemon.calculateStats();
|
||||
|
||||
scene.currentBattle.enemyParty.unshift(pokemon);
|
||||
}, scene.currentBattle.waveIndex * 1000 * encounter.misc.safariPokemonRemaining);
|
||||
globalScene.currentBattle.enemyParty.unshift(pokemon);
|
||||
}, globalScene.currentBattle.waveIndex * 1000 * encounter.misc.safariPokemonRemaining);
|
||||
|
||||
scene.gameData.setPokemonSeen(pokemon, true);
|
||||
globalScene.gameData.setPokemonSeen(pokemon, true);
|
||||
await pokemon.loadAssets();
|
||||
|
||||
// Reset safari catch and flee rates
|
||||
@ -301,7 +304,7 @@ async function summonSafariPokemon(scene: BattleScene) {
|
||||
encounter.misc.pokemon = pokemon;
|
||||
encounter.misc.safariPokemonRemaining -= 1;
|
||||
|
||||
scene.unshiftPhase(new SummonPhase(scene, 0, false));
|
||||
globalScene.unshiftPhase(new SummonPhase(0, false));
|
||||
|
||||
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
|
||||
|
||||
@ -310,49 +313,49 @@ async function summonSafariPokemon(scene: BattleScene) {
|
||||
// shows up and the IV scanner breaks. For now, we place the IV scanner code
|
||||
// separately so that at least the IV scanner works.
|
||||
|
||||
const ivScannerModifier = scene.findModifier(m => m instanceof IvScannerModifier);
|
||||
const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier);
|
||||
if (ivScannerModifier) {
|
||||
scene.pushPhase(new ScanIvsPhase(scene, pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)));
|
||||
globalScene.pushPhase(new ScanIvsPhase(pokemon.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6)));
|
||||
}
|
||||
}
|
||||
|
||||
function throwPokeball(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> {
|
||||
function throwPokeball(pokemon: EnemyPokemon): Promise<boolean> {
|
||||
const baseCatchRate = pokemon.species.catchRate;
|
||||
// Catch stage ranges from -6 to +6 (like stat boost stages)
|
||||
const safariCatchStage = scene.currentBattle.mysteryEncounter!.misc.catchStage;
|
||||
const safariCatchStage = globalScene.currentBattle.mysteryEncounter!.misc.catchStage;
|
||||
// Catch modifier ranges from 2/8 (-6 stage) to 8/2 (+6)
|
||||
const safariModifier = (2 + Math.min(Math.max(safariCatchStage, 0), 6)) / (2 - Math.max(Math.min(safariCatchStage, 0), -6));
|
||||
// Catch rate same as safari ball
|
||||
const pokeballMultiplier = 1.5;
|
||||
const catchRate = Math.round(baseCatchRate * pokeballMultiplier * safariModifier);
|
||||
const ballTwitchRate = Math.round(1048560 / Math.sqrt(Math.sqrt(16711680 / catchRate)));
|
||||
return trainerThrowPokeball(scene, pokemon, PokeballType.POKEBALL, ballTwitchRate);
|
||||
return trainerThrowPokeball(pokemon, PokeballType.POKEBALL, ballTwitchRate);
|
||||
}
|
||||
|
||||
async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> {
|
||||
async function throwBait(pokemon: EnemyPokemon): Promise<boolean> {
|
||||
const originalY: number = pokemon.y;
|
||||
|
||||
const fpOffset = pokemon.getFieldPositionOffset();
|
||||
const bait: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 25, "safari_zone_bait", "0001.png");
|
||||
const bait: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(16 + 75, 80 + 25, "safari_zone_bait", "0001.png");
|
||||
bait.setOrigin(0.5, 0.625);
|
||||
scene.field.add(bait);
|
||||
globalScene.field.add(bait);
|
||||
|
||||
return new Promise(resolve => {
|
||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
|
||||
scene.playSound("se/pb_throw");
|
||||
globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||
globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
|
||||
globalScene.playSound("se/pb_throw");
|
||||
|
||||
// Trainer throw frames
|
||||
scene.trainer.setFrame("2");
|
||||
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
|
||||
scene.trainer.setFrame("3");
|
||||
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
|
||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
||||
globalScene.trainer.setFrame("2");
|
||||
globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
|
||||
globalScene.trainer.setFrame("3");
|
||||
globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
|
||||
globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
||||
});
|
||||
});
|
||||
|
||||
// Pokeball move and catch logic
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: bait,
|
||||
x: { value: 210 + fpOffset[0], ease: "Linear" },
|
||||
y: { value: 55 + fpOffset[1], ease: "Cubic.easeOut" },
|
||||
@ -360,8 +363,8 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
|
||||
onComplete: () => {
|
||||
|
||||
let index = 1;
|
||||
scene.time.delayedCall(768, () => {
|
||||
scene.tweens.add({
|
||||
globalScene.time.delayedCall(768, () => {
|
||||
globalScene.tweens.add({
|
||||
targets: pokemon,
|
||||
duration: 150,
|
||||
ease: "Cubic.easeOut",
|
||||
@ -369,12 +372,12 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
|
||||
y: originalY - 5,
|
||||
loop: 6,
|
||||
onStart: () => {
|
||||
scene.playSound("battle_anims/PRSFX- Bug Bite");
|
||||
globalScene.playSound("battle_anims/PRSFX- Bug Bite");
|
||||
bait.setFrame("0002.png");
|
||||
},
|
||||
onLoop: () => {
|
||||
if (index % 2 === 0) {
|
||||
scene.playSound("battle_anims/PRSFX- Bug Bite");
|
||||
globalScene.playSound("battle_anims/PRSFX- Bug Bite");
|
||||
}
|
||||
if (index === 4) {
|
||||
bait.setFrame("0003.png");
|
||||
@ -382,7 +385,7 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
|
||||
index++;
|
||||
},
|
||||
onComplete: () => {
|
||||
scene.time.delayedCall(256, () => {
|
||||
globalScene.time.delayedCall(256, () => {
|
||||
bait.destroy();
|
||||
resolve(true);
|
||||
});
|
||||
@ -395,55 +398,55 @@ async function throwBait(scene: BattleScene, pokemon: EnemyPokemon): Promise<boo
|
||||
});
|
||||
}
|
||||
|
||||
async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise<boolean> {
|
||||
async function throwMud(pokemon: EnemyPokemon): Promise<boolean> {
|
||||
const originalY: number = pokemon.y;
|
||||
|
||||
const fpOffset = pokemon.getFieldPositionOffset();
|
||||
const mud: Phaser.GameObjects.Sprite = scene.addFieldSprite(16 + 75, 80 + 35, "safari_zone_mud", "0001.png");
|
||||
const mud: Phaser.GameObjects.Sprite = globalScene.addFieldSprite(16 + 75, 80 + 35, "safari_zone_mud", "0001.png");
|
||||
mud.setOrigin(0.5, 0.625);
|
||||
scene.field.add(mud);
|
||||
globalScene.field.add(mud);
|
||||
|
||||
return new Promise(resolve => {
|
||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
|
||||
scene.playSound("se/pb_throw");
|
||||
globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back_pb`);
|
||||
globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[0], () => {
|
||||
globalScene.playSound("se/pb_throw");
|
||||
|
||||
// Trainer throw frames
|
||||
scene.trainer.setFrame("2");
|
||||
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
|
||||
scene.trainer.setFrame("3");
|
||||
scene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
|
||||
scene.trainer.setTexture(`trainer_${scene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
||||
globalScene.trainer.setFrame("2");
|
||||
globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[1], () => {
|
||||
globalScene.trainer.setFrame("3");
|
||||
globalScene.time.delayedCall(TRAINER_THROW_ANIMATION_TIMES[2], () => {
|
||||
globalScene.trainer.setTexture(`trainer_${globalScene.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
|
||||
});
|
||||
});
|
||||
|
||||
// Mud throw and splat
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: mud,
|
||||
x: { value: 230 + fpOffset[0], ease: "Linear" },
|
||||
y: { value: 55 + fpOffset[1], ease: "Cubic.easeOut" },
|
||||
duration: 500,
|
||||
onComplete: () => {
|
||||
// Mud frame 2
|
||||
scene.playSound("battle_anims/PRSFX- Sludge Bomb2");
|
||||
globalScene.playSound("battle_anims/PRSFX- Sludge Bomb2");
|
||||
mud.setFrame("0002.png");
|
||||
// Mud splat
|
||||
scene.time.delayedCall(200, () => {
|
||||
globalScene.time.delayedCall(200, () => {
|
||||
mud.setFrame("0003.png");
|
||||
scene.time.delayedCall(400, () => {
|
||||
globalScene.time.delayedCall(400, () => {
|
||||
mud.setFrame("0004.png");
|
||||
});
|
||||
});
|
||||
|
||||
// Fade mud then angry animation
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: mud,
|
||||
alpha: 0,
|
||||
ease: "Cubic.easeIn",
|
||||
duration: 1000,
|
||||
onComplete: () => {
|
||||
mud.destroy();
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: pokemon,
|
||||
duration: 300,
|
||||
ease: "Cubic.easeOut",
|
||||
@ -451,10 +454,10 @@ async function throwMud(scene: BattleScene, pokemon: EnemyPokemon): Promise<bool
|
||||
y: originalY - 20,
|
||||
loop: 1,
|
||||
onStart: () => {
|
||||
scene.playSound("battle_anims/PRSFX- Taunt2");
|
||||
globalScene.playSound("battle_anims/PRSFX- Taunt2");
|
||||
},
|
||||
onLoop: () => {
|
||||
scene.playSound("battle_anims/PRSFX- Taunt2");
|
||||
globalScene.playSound("battle_anims/PRSFX- Taunt2");
|
||||
},
|
||||
onComplete: () => {
|
||||
resolve(true);
|
||||
@ -478,53 +481,53 @@ function isPokemonFlee(pokemon: EnemyPokemon, fleeStage: number): boolean {
|
||||
return roll < fleeRate;
|
||||
}
|
||||
|
||||
function tryChangeFleeStage(scene: BattleScene, change: number, chance?: number): boolean {
|
||||
function tryChangeFleeStage(change: number, chance?: number): boolean {
|
||||
if (chance && randSeedInt(10) >= chance) {
|
||||
return false;
|
||||
}
|
||||
const currentFleeStage = scene.currentBattle.mysteryEncounter!.misc.fleeStage ?? 0;
|
||||
scene.currentBattle.mysteryEncounter!.misc.fleeStage = Math.min(Math.max(currentFleeStage + change, -6), 6);
|
||||
const currentFleeStage = globalScene.currentBattle.mysteryEncounter!.misc.fleeStage ?? 0;
|
||||
globalScene.currentBattle.mysteryEncounter!.misc.fleeStage = Math.min(Math.max(currentFleeStage + change, -6), 6);
|
||||
return true;
|
||||
}
|
||||
|
||||
function tryChangeCatchStage(scene: BattleScene, change: number, chance?: number): boolean {
|
||||
function tryChangeCatchStage(change: number, chance?: number): boolean {
|
||||
if (chance && randSeedInt(10) >= chance) {
|
||||
return false;
|
||||
}
|
||||
const currentCatchStage = scene.currentBattle.mysteryEncounter!.misc.catchStage ?? 0;
|
||||
scene.currentBattle.mysteryEncounter!.misc.catchStage = Math.min(Math.max(currentCatchStage + change, -6), 6);
|
||||
const currentCatchStage = globalScene.currentBattle.mysteryEncounter!.misc.catchStage ?? 0;
|
||||
globalScene.currentBattle.mysteryEncounter!.misc.catchStage = Math.min(Math.max(currentCatchStage + change, -6), 6);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function doEndTurn(scene: BattleScene, cursorIndex: number) {
|
||||
async function doEndTurn(cursorIndex: number) {
|
||||
// First cleanup and destroy old Pokemon objects that were left in the enemyParty
|
||||
// They are left in enemyParty temporarily so that VictoryPhase properly handles EXP
|
||||
const party = scene.getEnemyParty();
|
||||
const party = globalScene.getEnemyParty();
|
||||
if (party.length > 1) {
|
||||
for (let i = 1; i < party.length; i++) {
|
||||
party[i].destroy();
|
||||
}
|
||||
scene.currentBattle.enemyParty = party.slice(0, 1);
|
||||
globalScene.currentBattle.enemyParty = party.slice(0, 1);
|
||||
}
|
||||
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const pokemon = encounter.misc.pokemon;
|
||||
const isFlee = isPokemonFlee(pokemon, encounter.misc.fleeStage);
|
||||
if (isFlee) {
|
||||
// Pokemon flees!
|
||||
await doPokemonFlee(scene, pokemon);
|
||||
await doPokemonFlee(pokemon);
|
||||
// Check how many safari pokemon left
|
||||
if (encounter.misc.safariPokemonRemaining > 0) {
|
||||
await summonSafariPokemon(scene);
|
||||
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||
await summonSafariPokemon();
|
||||
initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||
} else {
|
||||
// End safari mode
|
||||
encounter.continuousEncounter = false;
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
}
|
||||
} else {
|
||||
scene.queueMessage(getEncounterText(scene, `${namespace}:safari.watching`) ?? "", 0, null, 1000);
|
||||
initSubsequentOptionSelect(scene, { overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||
globalScene.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000);
|
||||
initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, hideDescription: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { randSeedInt } from "#app/utils";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon, isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { Nature } from "#enums/nature";
|
||||
import type { Nature } from "#enums/nature";
|
||||
import { getNatureName } from "#app/data/nature";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import i18next from "i18next";
|
||||
@ -79,15 +81,15 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Update money
|
||||
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
||||
updatePlayerMoney(-(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
||||
// Calculate modifiers and dialogue tokens
|
||||
const modifiers = [
|
||||
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)!,
|
||||
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)!,
|
||||
generateModifierType(modifierTypes.BASE_STAT_BOOSTER)!,
|
||||
generateModifierType(modifierTypes.BASE_STAT_BOOSTER)!,
|
||||
];
|
||||
encounter.setDialogueToken("boost1", modifiers[0].name);
|
||||
encounter.setDialogueToken("boost2", modifiers[1].name);
|
||||
@ -103,34 +105,34 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||
if (!pokemon.isAllowedInChallenge()) {
|
||||
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
|
||||
}
|
||||
if (!encounter.pokemonMeetsPrimaryRequirements(scene, pokemon)) {
|
||||
return getEncounterText(scene, `${namespace}:invalid_selection`) ?? null;
|
||||
if (!encounter.pokemonMeetsPrimaryRequirements(pokemon)) {
|
||||
return getEncounterText(`${namespace}:invalid_selection`) ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||
return selectPokemonForOption(onPokemonSelected, undefined, selectableFilter);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Choose Cheap Option
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||
const modifiers = encounter.misc.modifiers;
|
||||
|
||||
for (const modType of modifiers) {
|
||||
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType);
|
||||
await applyModifierTypeToPlayerPokemon(chosenPokemon, modType);
|
||||
}
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
.withPostOptionPhase(async () => {
|
||||
// Damage and status applied after dealer leaves (to make thematic sense)
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const chosenPokemon = encounter.misc.chosenPokemon as PlayerPokemon;
|
||||
|
||||
// Pokemon takes half max HP damage and nature is randomized (does not update dex)
|
||||
applyDamageToPokemon(scene, chosenPokemon, Math.floor(chosenPokemon.getMaxHp() / 2));
|
||||
applyDamageToPokemon(chosenPokemon, Math.floor(chosenPokemon.getMaxHp() / 2));
|
||||
|
||||
const currentNature = chosenPokemon.nature;
|
||||
let newNature = randSeedInt(25) as Nature;
|
||||
@ -140,8 +142,8 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||
|
||||
chosenPokemon.setCustomNature(newNature);
|
||||
encounter.setDialogueToken("newNature", getNatureName(newNature));
|
||||
queueEncounterMessage(scene, `${namespace}:cheap_side_effects`);
|
||||
setEncounterExp(scene, [ chosenPokemon.id ], 100);
|
||||
queueEncounterMessage(`${namespace}:cheap_side_effects`);
|
||||
setEncounterExp([ chosenPokemon.id ], 100);
|
||||
await chosenPokemon.updateInfo();
|
||||
})
|
||||
.build()
|
||||
@ -159,15 +161,15 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Update money
|
||||
updatePlayerMoney(scene, -(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney);
|
||||
updatePlayerMoney(-(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney);
|
||||
// Calculate modifiers and dialogue tokens
|
||||
const modifiers = [
|
||||
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)!,
|
||||
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)!,
|
||||
generateModifierType(modifierTypes.BASE_STAT_BOOSTER)!,
|
||||
generateModifierType(modifierTypes.BASE_STAT_BOOSTER)!,
|
||||
];
|
||||
encounter.setDialogueToken("boost1", modifiers[0].name);
|
||||
encounter.setDialogueToken("boost2", modifiers[1].name);
|
||||
@ -179,30 +181,30 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||
|
||||
// Only Pokemon that can gain benefits are unfainted
|
||||
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 () => {
|
||||
// Choose Expensive Option
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||
const modifiers = encounter.misc.modifiers;
|
||||
|
||||
for (const modType of modifiers) {
|
||||
await applyModifierTypeToPlayerPokemon(scene, chosenPokemon, modType);
|
||||
await applyModifierTypeToPlayerPokemon(chosenPokemon, modType);
|
||||
}
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
.withPostOptionPhase(async () => {
|
||||
// Status applied after dealer leaves (to make thematic sense)
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||
|
||||
queueEncounterMessage(scene, `${namespace}:no_bad_effects`);
|
||||
setEncounterExp(scene, [ chosenPokemon.id ], 100);
|
||||
queueEncounterMessage(`${namespace}:no_bad_effects`);
|
||||
setEncounterExp([ chosenPokemon.id ], 100);
|
||||
|
||||
await chosenPokemon.updateInfo();
|
||||
})
|
||||
@ -219,9 +221,9 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
||||
}
|
||||
]
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
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 { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
||||
import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils";
|
||||
import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
@ -51,8 +54,8 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
console.log(encounter);
|
||||
|
||||
// Calculate boss mon
|
||||
@ -65,11 +68,11 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||
moveSet: [ Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2
|
||||
},
|
||||
],
|
||||
@ -83,7 +86,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||
encounter.enemyPartyConfigs = [ config ];
|
||||
|
||||
// Load animations/sfx for Snorlax fight start moves
|
||||
loadCustomMovesForEncounter(scene, [ Moves.SNORE ]);
|
||||
loadCustomMovesForEncounter([ Moves.SNORE ]);
|
||||
|
||||
encounter.setDialogueToken("snorlaxName", getPokemonSpecies(Species.SNORLAX).getName());
|
||||
|
||||
@ -103,10 +106,10 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Pick battle
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: true });
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: true });
|
||||
encounter.startOfBattleEffects.push(
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
@ -120,7 +123,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||
move: new PokemonMove(Moves.SNORE),
|
||||
ignorePp: true
|
||||
});
|
||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
@ -133,12 +136,12 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Fall asleep waiting for Snorlax
|
||||
// Full heal party
|
||||
scene.unshiftPhase(new PartyHealPhase(scene, true));
|
||||
queueEncounterMessage(scene, `${namespace}:option.2.rest_result`);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
||||
queueEncounterMessage(`${namespace}:option.2.rest_result`);
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
)
|
||||
.withOption(
|
||||
@ -155,13 +158,13 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
|
||||
}
|
||||
]
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Steal the Snorlax's Leftovers
|
||||
const instance = scene.currentBattle.mysteryEncounter!;
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: false });
|
||||
const instance = globalScene.currentBattle.mysteryEncounter!;
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.LEFTOVERS ], fillRemaining: false });
|
||||
// Snorlax exp to Pokemon that did the stealing
|
||||
setEncounterExp(scene, instance.primaryPokemon!.id, getPokemonSpecies(Species.SNORLAX).baseExp);
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterExp(instance.primaryPokemon!.id, getPokemonSpecies(Species.SNORLAX).baseExp);
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { generateModifierTypeOption, initBattleWithEnemyConfig, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { randSeedInt } from "#app/utils";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MoneyRequirement, WaveModulusRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import Pokemon, { EnemyPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
@ -62,9 +65,9 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const price = scene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER);
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const price = globalScene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER);
|
||||
encounter.setDialogueToken("price", price.toString());
|
||||
encounter.misc = {
|
||||
price
|
||||
@ -85,14 +88,14 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
|
||||
}
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Update money
|
||||
updatePlayerMoney(scene, -scene.currentBattle.mysteryEncounter!.misc.price, true, false);
|
||||
updatePlayerMoney(-globalScene.currentBattle.mysteryEncounter!.misc.price, true, false);
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit(scene);
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
.withOptionPhase(async () => {
|
||||
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit();
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -110,11 +113,11 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
|
||||
}
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit(scene);
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
setEncounterExp(scene, scene.currentBattle.mysteryEncounter!.selectedOption!.primaryPokemon!.id, 100);
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
.withOptionPhase(async () => {
|
||||
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit();
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
setEncounterExp(globalScene.currentBattle.mysteryEncounter!.selectedOption!.primaryPokemon!.id, 100);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -128,14 +131,14 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Inspect the Machine
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Init enemy
|
||||
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||
const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const bossSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
|
||||
const bossPokemon = new EnemyPokemon(bossSpecies, level, TrainerSlot.NONE, true);
|
||||
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [{
|
||||
@ -146,36 +149,36 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
|
||||
}],
|
||||
};
|
||||
|
||||
const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!;
|
||||
const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!;
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true });
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true);
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
const magnet = generateModifierTypeOption(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!;
|
||||
const metalCoat = generateModifierTypeOption(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!;
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true });
|
||||
await transitionMysteryEncounterIntroVisuals(true, true);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async function doBiomeTransitionDialogueAndBattleInit() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate new biome (cannot be current biome)
|
||||
const filteredBiomes = BIOME_CANDIDATES.filter(b => scene.arena.biomeType !== b);
|
||||
const filteredBiomes = BIOME_CANDIDATES.filter(b => globalScene.arena.biomeType !== b);
|
||||
const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)];
|
||||
|
||||
// Show dialogue and transition biome
|
||||
await showEncounterText(scene, `${namespace}:transport`);
|
||||
await Promise.all([ animateBiomeChange(scene, newBiome), transitionMysteryEncounterIntroVisuals(scene) ]);
|
||||
scene.playBgm();
|
||||
await showEncounterText(scene, `${namespace}:attacked`);
|
||||
await showEncounterText(`${namespace}:transport`);
|
||||
await Promise.all([ animateBiomeChange(newBiome), transitionMysteryEncounterIntroVisuals() ]);
|
||||
globalScene.playBgm();
|
||||
await showEncounterText(`${namespace}:attacked`);
|
||||
|
||||
// Init enemy
|
||||
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||
const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const bossSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
|
||||
const bossPokemon = new EnemyPokemon(bossSpecies, level, TrainerSlot.NONE, true);
|
||||
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||
|
||||
// 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.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
|
||||
|
||||
@ -187,8 +190,8 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
|
||||
isBoss: true,
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(pokemon.scene, `${namespace}:boss_enraged`);
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
queueEncounterMessage(`${namespace}:boss_enraged`);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
}
|
||||
}],
|
||||
};
|
||||
@ -196,46 +199,46 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
|
||||
return config;
|
||||
}
|
||||
|
||||
async function animateBiomeChange(scene: BattleScene, nextBiome: Biome) {
|
||||
async function animateBiomeChange(nextBiome: Biome) {
|
||||
return new Promise<void>(resolve => {
|
||||
scene.tweens.add({
|
||||
targets: [ scene.arenaEnemy, scene.lastEnemyTrainer ],
|
||||
globalScene.tweens.add({
|
||||
targets: [ globalScene.arenaEnemy, globalScene.lastEnemyTrainer ],
|
||||
x: "+=300",
|
||||
duration: 2000,
|
||||
onComplete: () => {
|
||||
scene.newArena(nextBiome);
|
||||
globalScene.newArena(nextBiome);
|
||||
|
||||
const biomeKey = getBiomeKey(nextBiome);
|
||||
const bgTexture = `${biomeKey}_bg`;
|
||||
scene.arenaBgTransition.setTexture(bgTexture);
|
||||
scene.arenaBgTransition.setAlpha(0);
|
||||
scene.arenaBgTransition.setVisible(true);
|
||||
scene.arenaPlayerTransition.setBiome(nextBiome);
|
||||
scene.arenaPlayerTransition.setAlpha(0);
|
||||
scene.arenaPlayerTransition.setVisible(true);
|
||||
globalScene.arenaBgTransition.setTexture(bgTexture);
|
||||
globalScene.arenaBgTransition.setAlpha(0);
|
||||
globalScene.arenaBgTransition.setVisible(true);
|
||||
globalScene.arenaPlayerTransition.setBiome(nextBiome);
|
||||
globalScene.arenaPlayerTransition.setAlpha(0);
|
||||
globalScene.arenaPlayerTransition.setVisible(true);
|
||||
|
||||
scene.tweens.add({
|
||||
targets: [ scene.arenaPlayer, scene.arenaBgTransition, scene.arenaPlayerTransition ],
|
||||
globalScene.tweens.add({
|
||||
targets: [ globalScene.arenaPlayer, globalScene.arenaBgTransition, globalScene.arenaPlayerTransition ],
|
||||
duration: 1000,
|
||||
ease: "Sine.easeInOut",
|
||||
alpha: (target: any) => target === scene.arenaPlayer ? 0 : 1,
|
||||
alpha: (target: any) => target === globalScene.arenaPlayer ? 0 : 1,
|
||||
onComplete: () => {
|
||||
scene.arenaBg.setTexture(bgTexture);
|
||||
scene.arenaPlayer.setBiome(nextBiome);
|
||||
scene.arenaPlayer.setAlpha(1);
|
||||
scene.arenaEnemy.setBiome(nextBiome);
|
||||
scene.arenaEnemy.setAlpha(1);
|
||||
scene.arenaNextEnemy.setBiome(nextBiome);
|
||||
scene.arenaBgTransition.setVisible(false);
|
||||
scene.arenaPlayerTransition.setVisible(false);
|
||||
if (scene.lastEnemyTrainer) {
|
||||
scene.lastEnemyTrainer.destroy();
|
||||
globalScene.arenaBg.setTexture(bgTexture);
|
||||
globalScene.arenaPlayer.setBiome(nextBiome);
|
||||
globalScene.arenaPlayer.setAlpha(1);
|
||||
globalScene.arenaEnemy.setBiome(nextBiome);
|
||||
globalScene.arenaEnemy.setAlpha(1);
|
||||
globalScene.arenaNextEnemy.setBiome(nextBiome);
|
||||
globalScene.arenaBgTransition.setVisible(false);
|
||||
globalScene.arenaPlayerTransition.setVisible(false);
|
||||
if (globalScene.lastEnemyTrainer) {
|
||||
globalScene.lastEnemyTrainer.destroy();
|
||||
}
|
||||
|
||||
resolve();
|
||||
|
||||
scene.tweens.add({
|
||||
targets: scene.arenaEnemy,
|
||||
globalScene.tweens.add({
|
||||
targets: globalScene.arenaEnemy,
|
||||
x: "-=300",
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { EnemyPartyConfig, generateModifierType, handleMysteryEncounterBattleFailed, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { generateModifierType, handleMysteryEncounterBattleFailed, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { trainerConfigs } from "#app/data/trainer-config";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { randSeedShuffle } from "#app/utils";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||
import type MysteryEncounter from "../mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import { Biome } from "#enums/biome";
|
||||
@ -14,17 +16,18 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { IEggOptions } from "#app/data/egg";
|
||||
import type { IEggOptions } from "#app/data/egg";
|
||||
import { EggSourceType } from "#enums/egg-source-types";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { Type } from "#enums/type";
|
||||
import { getPokeballTintColor } from "#app/data/pokeball";
|
||||
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounters/theExpertPokemonBreeder";
|
||||
@ -93,14 +96,14 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro_dialogue`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const waveIndex = scene.currentBattle.waveIndex;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const waveIndex = globalScene.currentBattle.waveIndex;
|
||||
// Calculates what trainers are available for battle in the encounter
|
||||
|
||||
// If player is in space biome, uses special "Space" version of the trainer
|
||||
encounter.enemyPartyConfigs = [
|
||||
getPartyConfig(scene)
|
||||
getPartyConfig()
|
||||
];
|
||||
|
||||
const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE;
|
||||
@ -125,7 +128,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
];
|
||||
|
||||
// Determine the 3 pokemon the player can battle with
|
||||
let partyCopy = scene.getPlayerParty().slice(0);
|
||||
let partyCopy = globalScene.getPlayerParty().slice(0);
|
||||
partyCopy = partyCopy
|
||||
.filter(p => p.isAllowedInBattle())
|
||||
.sort((a, b) => a.friendship - b.friendship);
|
||||
@ -139,7 +142,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
|
||||
// Dialogue and egg calcs for Pokemon 1
|
||||
const [ pokemon1CommonEggs, pokemon1RareEggs ] = calculateEggRewardsForPokemon(pokemon1);
|
||||
let pokemon1Tooltip = getEncounterText(scene, `${namespace}:option.1.tooltip_base`)!;
|
||||
let pokemon1Tooltip = getEncounterText(`${namespace}:option.1.tooltip_base`)!;
|
||||
if (pokemon1RareEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||
pokemon1Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
|
||||
@ -154,7 +157,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
|
||||
// Dialogue and egg calcs for Pokemon 2
|
||||
const [ pokemon2CommonEggs, pokemon2RareEggs ] = calculateEggRewardsForPokemon(pokemon2);
|
||||
let pokemon2Tooltip = getEncounterText(scene, `${namespace}:option.2.tooltip_base`)!;
|
||||
let pokemon2Tooltip = getEncounterText(`${namespace}:option.2.tooltip_base`)!;
|
||||
if (pokemon2RareEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||
pokemon2Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
|
||||
@ -169,7 +172,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
|
||||
// Dialogue and egg calcs for Pokemon 3
|
||||
const [ pokemon3CommonEggs, pokemon3RareEggs ] = calculateEggRewardsForPokemon(pokemon3);
|
||||
let pokemon3Tooltip = getEncounterText(scene, `${namespace}:option.3.tooltip_base`)!;
|
||||
let pokemon3Tooltip = getEncounterText(`${namespace}:option.3.tooltip_base`)!;
|
||||
if (pokemon3RareEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}:numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||
pokemon3Tooltip += i18next.t(`${namespace}:eggs_tooltip`, { eggs: eggsText });
|
||||
@ -212,22 +215,22 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Spawn battle with first pokemon
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
const { pokemon1, pokemon1CommonEggs, pokemon1RareEggs } = encounter.misc;
|
||||
encounter.misc.chosenPokemon = pokemon1;
|
||||
encounter.setDialogueToken("chosenPokemon", pokemon1.getNameToRender());
|
||||
const eggOptions = getEggOptions(scene, pokemon1CommonEggs, pokemon1RareEggs);
|
||||
setEncounterRewards(scene,
|
||||
const eggOptions = getEggOptions(pokemon1CommonEggs, pokemon1RareEggs);
|
||||
setEncounterRewards(
|
||||
{ guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true },
|
||||
eggOptions,
|
||||
() => doPostEncounterCleanup(scene));
|
||||
() => doPostEncounterCleanup());
|
||||
|
||||
// Remove all Pokemon from the party except the chosen Pokemon
|
||||
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon1);
|
||||
removePokemonFromPartyAndStoreHeldItems(encounter, pokemon1);
|
||||
|
||||
// Configure outro dialogue for egg rewards
|
||||
encounter.dialogue.outro = [
|
||||
@ -248,7 +251,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
}
|
||||
|
||||
encounter.onGameOver = onGameOver;
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -264,22 +267,22 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Spawn battle with second pokemon
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
const { pokemon2, pokemon2CommonEggs, pokemon2RareEggs } = encounter.misc;
|
||||
encounter.misc.chosenPokemon = pokemon2;
|
||||
encounter.setDialogueToken("chosenPokemon", pokemon2.getNameToRender());
|
||||
const eggOptions = getEggOptions(scene, pokemon2CommonEggs, pokemon2RareEggs);
|
||||
setEncounterRewards(scene,
|
||||
const eggOptions = getEggOptions(pokemon2CommonEggs, pokemon2RareEggs);
|
||||
setEncounterRewards(
|
||||
{ guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true },
|
||||
eggOptions,
|
||||
() => doPostEncounterCleanup(scene));
|
||||
() => doPostEncounterCleanup());
|
||||
|
||||
// Remove all Pokemon from the party except the chosen Pokemon
|
||||
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon2);
|
||||
removePokemonFromPartyAndStoreHeldItems(encounter, pokemon2);
|
||||
|
||||
// Configure outro dialogue for egg rewards
|
||||
encounter.dialogue.outro = [
|
||||
@ -300,7 +303,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
}
|
||||
|
||||
encounter.onGameOver = onGameOver;
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -316,22 +319,22 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Spawn battle with third pokemon
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
const { pokemon3, pokemon3CommonEggs, pokemon3RareEggs } = encounter.misc;
|
||||
encounter.misc.chosenPokemon = pokemon3;
|
||||
encounter.setDialogueToken("chosenPokemon", pokemon3.getNameToRender());
|
||||
const eggOptions = getEggOptions(scene, pokemon3CommonEggs, pokemon3RareEggs);
|
||||
setEncounterRewards(scene,
|
||||
const eggOptions = getEggOptions(pokemon3CommonEggs, pokemon3RareEggs);
|
||||
setEncounterRewards(
|
||||
{ guaranteedModifierTypeFuncs: [ modifierTypes.SOOTHE_BELL ], fillRemaining: true },
|
||||
eggOptions,
|
||||
() => doPostEncounterCleanup(scene));
|
||||
() => doPostEncounterCleanup());
|
||||
|
||||
// Remove all Pokemon from the party except the chosen Pokemon
|
||||
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon3);
|
||||
removePokemonFromPartyAndStoreHeldItems(encounter, pokemon3);
|
||||
|
||||
// Configure outro dialogue for egg rewards
|
||||
encounter.dialogue.outro = [
|
||||
@ -352,7 +355,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
}
|
||||
|
||||
encounter.onGameOver = onGameOver;
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -364,9 +367,9 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
])
|
||||
.build();
|
||||
|
||||
function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
function getPartyConfig(): EnemyPartyConfig {
|
||||
// Bug type superfan trainer config
|
||||
const waveIndex = scene.currentBattle.waveIndex;
|
||||
const waveIndex = globalScene.currentBattle.waveIndex;
|
||||
const breederConfig = trainerConfigs[TrainerType.EXPERT_POKEMON_BREEDER].clone();
|
||||
breederConfig.name = i18next.t(trainerNameKey);
|
||||
|
||||
@ -386,14 +389,14 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
ivs: [ 31, 31, 31, 31, 31, 31 ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.TERA_SHARD, [ Type.STEEL ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.TERA_SHARD, [ Type.STEEL ]) as PokemonHeldItemModifierType,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
if (scene.arena.biomeType === Biome.SPACE) {
|
||||
if (globalScene.arena.biomeType === Biome.SPACE) {
|
||||
// All 3 members always Cleffa line, but different configs
|
||||
baseConfig.pokemonConfigs!.push({
|
||||
nickname: i18next.t(`${namespace}:cleffa_2_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }),
|
||||
@ -476,14 +479,13 @@ function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number]
|
||||
return [ numCommons, numRares ];
|
||||
}
|
||||
|
||||
function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) {
|
||||
function getEggOptions(commonEggs: number, rareEggs: number) {
|
||||
const eggDescription = i18next.t(`${namespace}:title`) + ":\n" + i18next.t(trainerNameKey);
|
||||
const eggOptions: IEggOptions[] = [];
|
||||
|
||||
if (commonEggs > 0) {
|
||||
for (let i = 0; i < commonEggs; i++) {
|
||||
eggOptions.push({
|
||||
scene,
|
||||
pulled: false,
|
||||
sourceType: EggSourceType.EVENT,
|
||||
eggDescriptor: eggDescription,
|
||||
@ -494,7 +496,6 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number)
|
||||
if (rareEggs > 0) {
|
||||
for (let i = 0; i < rareEggs; i++) {
|
||||
eggOptions.push({
|
||||
scene,
|
||||
pulled: false,
|
||||
sourceType: EggSourceType.EVENT,
|
||||
eggDescriptor: eggDescription,
|
||||
@ -506,36 +507,36 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number)
|
||||
return eggOptions;
|
||||
}
|
||||
|
||||
function removePokemonFromPartyAndStoreHeldItems(scene: BattleScene, encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) {
|
||||
const party = scene.getPlayerParty();
|
||||
function removePokemonFromPartyAndStoreHeldItems(encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) {
|
||||
const party = globalScene.getPlayerParty();
|
||||
const chosenIndex = party.indexOf(chosenPokemon);
|
||||
party[chosenIndex] = party[0];
|
||||
party[0] = chosenPokemon;
|
||||
encounter.misc.originalParty = scene.getPlayerParty().slice(1);
|
||||
encounter.misc.originalParty = globalScene.getPlayerParty().slice(1);
|
||||
encounter.misc.originalPartyHeldItems = encounter.misc.originalParty
|
||||
.map(p => p.getHeldItems());
|
||||
scene["party"] = [
|
||||
globalScene["party"] = [
|
||||
chosenPokemon
|
||||
];
|
||||
}
|
||||
|
||||
function restorePartyAndHeldItems(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
function restorePartyAndHeldItems() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Restore original party
|
||||
scene.getPlayerParty().push(...encounter.misc.originalParty);
|
||||
globalScene.getPlayerParty().push(...encounter.misc.originalParty);
|
||||
|
||||
// Restore held items
|
||||
const originalHeldItems = encounter.misc.originalPartyHeldItems;
|
||||
originalHeldItems.forEach((pokemonHeldItemsList: PokemonHeldItemModifier[]) => {
|
||||
pokemonHeldItemsList.forEach(heldItem => {
|
||||
scene.addModifier(heldItem, true, false, false, true);
|
||||
globalScene.addModifier(heldItem, true, false, false, true);
|
||||
});
|
||||
});
|
||||
scene.updateModifiers(true);
|
||||
globalScene.updateModifiers(true);
|
||||
}
|
||||
|
||||
function onGameOver(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
function onGameOver() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
@ -545,7 +546,7 @@ function onGameOver(scene: BattleScene) {
|
||||
];
|
||||
|
||||
// Restore original party, player loses all friendship with chosen mon (it remains fainted)
|
||||
restorePartyAndHeldItems(scene);
|
||||
restorePartyAndHeldItems();
|
||||
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||
chosenPokemon.friendship = 0;
|
||||
|
||||
@ -556,33 +557,33 @@ function onGameOver(scene: BattleScene) {
|
||||
encounter.misc.encounterFailed = true;
|
||||
|
||||
// Revert BGM
|
||||
scene.playBgm(scene.arena.bgm);
|
||||
globalScene.playBgm(globalScene.arena.bgm);
|
||||
|
||||
// Clear any leftover battle phases
|
||||
scene.clearPhaseQueue();
|
||||
scene.clearPhaseQueueSplice();
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.clearPhaseQueueSplice();
|
||||
|
||||
// Return enemy Pokemon
|
||||
const pokemon = scene.getEnemyPokemon();
|
||||
const pokemon = globalScene.getEnemyPokemon();
|
||||
if (pokemon) {
|
||||
scene.playSound("se/pb_rel");
|
||||
globalScene.playSound("se/pb_rel");
|
||||
pokemon.hideInfo();
|
||||
pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, "Sine.easeIn");
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: pokemon,
|
||||
duration: 250,
|
||||
ease: "Sine.easeIn",
|
||||
scale: 0.5,
|
||||
onComplete: () => {
|
||||
scene.field.remove(pokemon, true);
|
||||
globalScene.field.remove(pokemon, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show the enemy trainer
|
||||
scene.time.delayedCall(250, () => {
|
||||
const sprites = scene.currentBattle.trainer?.getSprites();
|
||||
const tintSprites = scene.currentBattle.trainer?.getTintSprites();
|
||||
globalScene.time.delayedCall(250, () => {
|
||||
const sprites = globalScene.currentBattle.trainer?.getSprites();
|
||||
const tintSprites = globalScene.currentBattle.trainer?.getTintSprites();
|
||||
if (sprites && tintSprites) {
|
||||
for (let i = 0; i < sprites.length; i++) {
|
||||
sprites[i].setVisible(true);
|
||||
@ -591,8 +592,8 @@ function onGameOver(scene: BattleScene) {
|
||||
tintSprites[i].clearTint();
|
||||
}
|
||||
}
|
||||
scene.tweens.add({
|
||||
targets: scene.currentBattle.trainer,
|
||||
globalScene.tweens.add({
|
||||
targets: globalScene.currentBattle.trainer,
|
||||
x: "-=16",
|
||||
y: "+=16",
|
||||
alpha: 1,
|
||||
@ -602,16 +603,16 @@ function onGameOver(scene: BattleScene) {
|
||||
});
|
||||
|
||||
|
||||
handleMysteryEncounterBattleFailed(scene, true);
|
||||
handleMysteryEncounterBattleFailed(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function doPostEncounterCleanup(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
function doPostEncounterCleanup() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
if (!encounter.misc.encounterFailed) {
|
||||
// Give 20 friendship to the chosen pokemon
|
||||
encounter.misc.chosenPokemon.addFriendship(FRIENDSHIP_ADDED);
|
||||
restorePartyAndHeldItems(scene);
|
||||
restorePartyAndHeldItems();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import { leaveEncounterWithoutBattle, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { catchPokemon, getRandomSpeciesByStarterCost, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { Species } from "#enums/species";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
@ -58,8 +61,8 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
let species = getSalesmanSpeciesOffer();
|
||||
let tries = 0;
|
||||
@ -74,9 +77,9 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||
if (randSeedInt(SHINY_MAGIKARP_WEIGHT) === 0 || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) {
|
||||
// If no HA mon found or you roll 1%, give shiny Magikarp with random variant
|
||||
species = getPokemonSpecies(Species.MAGIKARP);
|
||||
pokemon = new PlayerPokemon(scene, species, 5, 2, species.formIndex, undefined, true);
|
||||
pokemon = new PlayerPokemon(species, 5, 2, species.formIndex, undefined, true);
|
||||
} else {
|
||||
pokemon = new PlayerPokemon(scene, species, 5, 2, species.formIndex);
|
||||
pokemon = new PlayerPokemon(species, 5, 2, species.formIndex);
|
||||
}
|
||||
pokemon.generateAndPopulateMoveset();
|
||||
|
||||
@ -101,7 +104,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||
encounter.dialogue.encounterOptionsDialogue!.description = `${namespace}:description_shiny`;
|
||||
encounter.options[0].dialogue!.buttonTooltip = `${namespace}:option.1.tooltip_shiny`;
|
||||
}
|
||||
const price = scene.getWaveMoneyAmount(priceMultiplier);
|
||||
const price = globalScene.getWaveMoneyAmount(priceMultiplier);
|
||||
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("price", price.toString());
|
||||
encounter.misc = {
|
||||
@ -127,24 +130,24 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||
}
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const price = encounter.misc.price;
|
||||
const purchasedPokemon = encounter.misc.pokemon as PlayerPokemon;
|
||||
|
||||
// Update money
|
||||
updatePlayerMoney(scene, -price, true, false);
|
||||
updatePlayerMoney(-price, true, false);
|
||||
|
||||
// Show dialogue
|
||||
await showEncounterDialogue(scene, `${namespace}:option.1.selected_dialogue`, `${namespace}:speaker`);
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
await showEncounterDialogue(`${namespace}:option.1.selected_dialogue`, `${namespace}:speaker`);
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
|
||||
// "Catch" purchased pokemon
|
||||
const data = new PokemonData(purchasedPokemon);
|
||||
data.player = false;
|
||||
await catchPokemon(scene, data.toPokemon(scene) as EnemyPokemon, null, PokeballType.POKEBALL, true, true);
|
||||
await catchPokemon(data.toPokemon() as EnemyPokemon, null, PokeballType.POKEBALL, true, true);
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -158,9 +161,9 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { modifierTypes, PokemonHeldItemModifierType, } from "#app/modifier/modifier-type";
|
||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Species } from "#enums/species";
|
||||
import { Nature } from "#enums/nature";
|
||||
import Pokemon, { PokemonMove } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { Moves } from "#enums/moves";
|
||||
@ -67,8 +71,8 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mon
|
||||
const config: EnemyPartyConfig = {
|
||||
@ -85,26 +89,26 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||
moveSet: [ Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2
|
||||
}
|
||||
],
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(pokemon.scene, `${namespace}:option.2.stat_boost`);
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.DEF, Stat.SPDEF ], 2));
|
||||
queueEncounterMessage(`${namespace}:option.2.stat_boost`);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.DEF, Stat.SPDEF ], 2));
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -112,7 +116,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||
|
||||
encounter.enemyPartyConfigs = [ config ];
|
||||
|
||||
loadCustomMovesForEncounter(scene, [ Moves.GASTRO_ACID, Moves.STEALTH_ROCK ]);
|
||||
loadCustomMovesForEncounter([ Moves.GASTRO_ACID, Moves.STEALTH_ROCK ]);
|
||||
|
||||
encounter.setDialogueToken("shuckleName", getPokemonSpecies(Species.SHUCKLE).getName());
|
||||
|
||||
@ -132,16 +136,16 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||
}
|
||||
]
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Do blackout and hide intro visuals during blackout
|
||||
scene.time.delayedCall(750, () => {
|
||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 50);
|
||||
globalScene.time.delayedCall(750, () => {
|
||||
transitionMysteryEncounterIntroVisuals(true, true, 50);
|
||||
});
|
||||
|
||||
// -15 to all base stats of highest BST (halved for HP), +10 to all base stats of rest of party (halved for HP)
|
||||
// Sort party by bst
|
||||
const sortedParty = scene.getPlayerParty().slice(0)
|
||||
const sortedParty = globalScene.getPlayerParty().slice(0)
|
||||
.sort((pokemon1, pokemon2) => {
|
||||
const pokemon1Bst = pokemon1.calculateBaseStats().reduce((a, b) => a + b, 0);
|
||||
const pokemon2Bst = pokemon2.calculateBaseStats().reduce((a, b) => a + b, 0);
|
||||
@ -161,15 +165,15 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||
|
||||
encounter.setDialogueToken("reductionValue", HIGH_BST_REDUCTION_VALUE.toString());
|
||||
encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString());
|
||||
await showEncounterText(scene, `${namespace}:option.1.selected_2`, null, undefined, true);
|
||||
await showEncounterText(`${namespace}:option.1.selected_2`, null, undefined, true);
|
||||
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
text: `${namespace}:outro`,
|
||||
}
|
||||
];
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
@ -183,10 +187,10 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Pick battle
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SOUL_DEW ], fillRemaining: true });
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.SOUL_DEW ], fillRemaining: true });
|
||||
encounter.startOfBattleEffects.push(
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
@ -202,8 +206,8 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
||||
});
|
||||
|
||||
encounter.dialogue.outro = [];
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { EnemyPartyConfig, generateModifierType, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { generateModifierType, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { TrainerType } from "#enums/trainer-type";
|
||||
import { Species } from "#enums/species";
|
||||
@ -83,15 +86,15 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
|
||||
},
|
||||
])
|
||||
.withAutoHideIntroVisuals(false)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Loaded back to front for pop() operations
|
||||
encounter.enemyPartyConfigs.push(getVitoTrainerConfig(scene));
|
||||
encounter.enemyPartyConfigs.push(getVickyTrainerConfig(scene));
|
||||
encounter.enemyPartyConfigs.push(getViviTrainerConfig(scene));
|
||||
encounter.enemyPartyConfigs.push(getVictoriaTrainerConfig(scene));
|
||||
encounter.enemyPartyConfigs.push(getVictorTrainerConfig(scene));
|
||||
encounter.enemyPartyConfigs.push(getVitoTrainerConfig());
|
||||
encounter.enemyPartyConfigs.push(getVickyTrainerConfig());
|
||||
encounter.enemyPartyConfigs.push(getViviTrainerConfig());
|
||||
encounter.enemyPartyConfigs.push(getVictoriaTrainerConfig());
|
||||
encounter.enemyPartyConfigs.push(getVictorTrainerConfig());
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -110,13 +113,13 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Spawn 5 trainer battles back to back with Macho Brace in rewards
|
||||
scene.currentBattle.mysteryEncounter!.doContinueEncounter = async (scene: BattleScene) => {
|
||||
await endTrainerBattleAndShowDialogue(scene);
|
||||
globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = async () => {
|
||||
await endTrainerBattleAndShowDialogue();
|
||||
};
|
||||
await transitionMysteryEncounterIntroVisuals(scene, true, false);
|
||||
await spawnNextTrainerOrEndEncounter(scene);
|
||||
await transitionMysteryEncounterIntroVisuals(true, false);
|
||||
await spawnNextTrainerOrEndEncounter();
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
@ -130,47 +133,47 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Refuse the challenge, they full heal the party and give the player a Rarer Candy
|
||||
scene.unshiftPhase(new PartyHealPhase(scene, true));
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.RARER_CANDY ], fillRemaining: false });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.RARER_CANDY ], fillRemaining: false });
|
||||
leaveEncounterWithoutBattle();
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
async function spawnNextTrainerOrEndEncounter(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
async function spawnNextTrainerOrEndEncounter() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const nextConfig = encounter.enemyPartyConfigs.pop();
|
||||
if (!nextConfig) {
|
||||
await transitionMysteryEncounterIntroVisuals(scene, false, false);
|
||||
await showEncounterDialogue(scene, `${namespace}:victory`, `${namespace}:speaker`);
|
||||
await transitionMysteryEncounterIntroVisuals(false, false);
|
||||
await showEncounterDialogue(`${namespace}:victory`, `${namespace}:speaker`);
|
||||
|
||||
// Give 10x Voucher
|
||||
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier();
|
||||
await scene.addModifier(newModifier);
|
||||
scene.playSound("item_fanfare");
|
||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }));
|
||||
await globalScene.addModifier(newModifier);
|
||||
globalScene.playSound("item_fanfare");
|
||||
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }));
|
||||
|
||||
await showEncounterDialogue(scene, `${namespace}:victory_2`, `${namespace}:speaker`);
|
||||
scene.ui.clearText(); // Clears "Winstrate" title from screen as rewards get animated in
|
||||
const machoBrace = generateModifierTypeOption(scene, modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!;
|
||||
await showEncounterDialogue(`${namespace}:victory_2`, `${namespace}:speaker`);
|
||||
globalScene.ui.clearText(); // Clears "Winstrate" title from screen as rewards get animated in
|
||||
const machoBrace = generateModifierTypeOption(modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!;
|
||||
machoBrace.type.tier = ModifierTier.MASTER;
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ machoBrace ], fillRemaining: false });
|
||||
setEncounterRewards({ guaranteedModifierTypeOptions: [ machoBrace ], fillRemaining: false });
|
||||
encounter.doContinueEncounter = undefined;
|
||||
leaveEncounterWithoutBattle(scene, false, MysteryEncounterMode.NO_BATTLE);
|
||||
leaveEncounterWithoutBattle(false, MysteryEncounterMode.NO_BATTLE);
|
||||
} else {
|
||||
await initBattleWithEnemyConfig(scene, nextConfig);
|
||||
await initBattleWithEnemyConfig(nextConfig);
|
||||
}
|
||||
}
|
||||
|
||||
function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
|
||||
function endTrainerBattleAndShowDialogue(): Promise<void> {
|
||||
return new Promise(async resolve => {
|
||||
if (scene.currentBattle.mysteryEncounter!.enemyPartyConfigs.length === 0) {
|
||||
if (globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs.length === 0) {
|
||||
// Battle is over
|
||||
const trainer = scene.currentBattle.trainer;
|
||||
const trainer = globalScene.currentBattle.trainer;
|
||||
if (trainer) {
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: trainer,
|
||||
x: "+=16",
|
||||
y: "-=16",
|
||||
@ -178,38 +181,38 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
|
||||
ease: "Sine.easeInOut",
|
||||
duration: 750,
|
||||
onComplete: () => {
|
||||
scene.field.remove(trainer, true);
|
||||
globalScene.field.remove(trainer, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await spawnNextTrainerOrEndEncounter(scene);
|
||||
await spawnNextTrainerOrEndEncounter();
|
||||
resolve(); // Wait for all dialogue/post battle stuff to complete before resolving
|
||||
} else {
|
||||
scene.arena.resetArenaEffects();
|
||||
const playerField = scene.getPlayerField();
|
||||
globalScene.arena.resetArenaEffects();
|
||||
const playerField = globalScene.getPlayerField();
|
||||
playerField.forEach((pokemon) => pokemon.lapseTag(BattlerTagType.COMMANDED));
|
||||
playerField.forEach((_, p) => scene.unshiftPhase(new ReturnPhase(scene, p)));
|
||||
playerField.forEach((_, p) => globalScene.unshiftPhase(new ReturnPhase(p)));
|
||||
|
||||
for (const pokemon of scene.getPlayerParty()) {
|
||||
for (const pokemon of globalScene.getPlayerParty()) {
|
||||
// Only trigger form change when Eiscue is in Noice form
|
||||
// Hardcoded Eiscue for now in case it is fused with another pokemon
|
||||
if (pokemon.species.speciesId === Species.EISCUE && pokemon.hasAbility(Abilities.ICE_FACE) && pokemon.formIndex === 1) {
|
||||
scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger);
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger);
|
||||
}
|
||||
|
||||
pokemon.resetBattleData();
|
||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
||||
}
|
||||
|
||||
scene.unshiftPhase(new ShowTrainerPhase(scene));
|
||||
globalScene.unshiftPhase(new ShowTrainerPhase());
|
||||
// Hide the trainer and init next battle
|
||||
const trainer = scene.currentBattle.trainer;
|
||||
const trainer = globalScene.currentBattle.trainer;
|
||||
// Unassign previous trainer from battle so it isn't destroyed before animation completes
|
||||
scene.currentBattle.trainer = null;
|
||||
await spawnNextTrainerOrEndEncounter(scene);
|
||||
globalScene.currentBattle.trainer = null;
|
||||
await spawnNextTrainerOrEndEncounter();
|
||||
if (trainer) {
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: trainer,
|
||||
x: "+=16",
|
||||
y: "-=16",
|
||||
@ -217,7 +220,7 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
|
||||
ease: "Sine.easeInOut",
|
||||
duration: 750,
|
||||
onComplete: () => {
|
||||
scene.field.remove(trainer, true);
|
||||
globalScene.field.remove(trainer, true);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
@ -226,7 +229,7 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
function getVictorTrainerConfig(): EnemyPartyConfig {
|
||||
return {
|
||||
trainerType: TrainerType.VICTOR,
|
||||
pokemonConfigs: [
|
||||
@ -238,11 +241,11 @@ function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.FACADE, Moves.BRAVE_BIRD, Moves.PROTECT, Moves.QUICK_ATTACK ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
|
||||
isTransferable: false
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.FOCUS_BAND) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.FOCUS_BAND) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
isTransferable: false
|
||||
},
|
||||
@ -256,11 +259,11 @@ function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.FACADE, Moves.OBSTRUCT, Moves.NIGHT_SLASH, Moves.FIRE_PUNCH ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
|
||||
isTransferable: false
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
isTransferable: false
|
||||
}
|
||||
@ -270,7 +273,7 @@ function getVictorTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
};
|
||||
}
|
||||
|
||||
function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
function getVictoriaTrainerConfig(): EnemyPartyConfig {
|
||||
return {
|
||||
trainerType: TrainerType.VICTORIA,
|
||||
pokemonConfigs: [
|
||||
@ -282,11 +285,11 @@ function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.SYNTHESIS, Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.SLEEP_POWDER ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType,
|
||||
isTransferable: false
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
isTransferable: false
|
||||
}
|
||||
@ -300,12 +303,12 @@ function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.PSYSHOCK, Moves.MOONBLAST, Moves.SHADOW_BALL, Moves.WILL_O_WISP ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.PSYCHIC ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.PSYCHIC ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 1,
|
||||
isTransferable: false
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FAIRY ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.FAIRY ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 1,
|
||||
isTransferable: false
|
||||
}
|
||||
@ -315,7 +318,7 @@ function getVictoriaTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
};
|
||||
}
|
||||
|
||||
function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
function getViviTrainerConfig(): EnemyPartyConfig {
|
||||
return {
|
||||
trainerType: TrainerType.VIVI,
|
||||
pokemonConfigs: [
|
||||
@ -327,12 +330,12 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.WATERFALL, Moves.MEGAHORN, Moves.KNOCK_OFF, Moves.REST ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
isTransferable: false
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 4,
|
||||
isTransferable: false
|
||||
}
|
||||
@ -346,12 +349,12 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.SPORE, Moves.SWORDS_DANCE, Moves.SEED_BOMB, Moves.DRAIN_PUNCH ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [ Stat.HP ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 4,
|
||||
isTransferable: false
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.TOXIC_ORB) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.TOXIC_ORB) as PokemonHeldItemModifierType,
|
||||
isTransferable: false
|
||||
}
|
||||
]
|
||||
@ -364,7 +367,7 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.EARTH_POWER, Moves.FIRE_BLAST, Moves.YAWN, Moves.PROTECT ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||
stackCount: 3,
|
||||
isTransferable: false
|
||||
},
|
||||
@ -374,7 +377,7 @@ function getViviTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
};
|
||||
}
|
||||
|
||||
function getVickyTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
function getVickyTrainerConfig(): EnemyPartyConfig {
|
||||
return {
|
||||
trainerType: TrainerType.VICKY,
|
||||
pokemonConfigs: [
|
||||
@ -386,7 +389,7 @@ function getVickyTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.AXE_KICK, Moves.ICE_PUNCH, Moves.ZEN_HEADBUTT, Moves.BULLET_PUNCH ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType,
|
||||
isTransferable: false
|
||||
}
|
||||
]
|
||||
@ -395,7 +398,7 @@ function getVickyTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
};
|
||||
}
|
||||
|
||||
function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
function getVitoTrainerConfig(): EnemyPartyConfig {
|
||||
return {
|
||||
trainerType: TrainerType.VITO,
|
||||
pokemonConfigs: [
|
||||
@ -407,7 +410,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.THUNDERBOLT, Moves.GIGA_DRAIN, Moves.FOUL_PLAY, Moves.THUNDER_WAVE ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [ Stat.SPD ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [ Stat.SPD ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
isTransferable: false
|
||||
}
|
||||
@ -421,47 +424,47 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.ICE_BEAM, Moves.EARTHQUAKE ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.SITRUS ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.APICOT ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.GANLON ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.STARF ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.STARF ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.SALAC ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.SALAC ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LUM ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LANSAT ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LANSAT ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LIECHI ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LIECHI ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.PETAYA ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.PETAYA ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.ENIGMA ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BERRY, [ BerryType.LEPPA ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [ BerryType.LEPPA ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
}
|
||||
]
|
||||
@ -474,7 +477,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.DRILL_PECK, Moves.QUICK_ATTACK, Moves.THRASH, Moves.KNOCK_OFF ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.KINGS_ROCK) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.KINGS_ROCK) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
isTransferable: false
|
||||
}
|
||||
@ -488,7 +491,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.PSYCHIC, Moves.SHADOW_BALL, Moves.FOCUS_BLAST, Moves.THUNDERBOLT ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.WIDE_LENS) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.WIDE_LENS) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
isTransferable: false
|
||||
},
|
||||
@ -502,7 +505,7 @@ function getVitoTrainerConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
moveSet: [ Moves.EARTHQUAKE, Moves.U_TURN, Moves.FLARE_BLITZ, Moves.ROCK_SLIDE ],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||
stackCount: 2,
|
||||
isTransferable: false
|
||||
},
|
||||
|
||||
@ -1,22 +1,26 @@
|
||||
import { Ability, allAbilities } from "#app/data/ability";
|
||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { Ability } from "#app/data/ability";
|
||||
import { allAbilities } from "#app/data/ability";
|
||||
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 { getNatureName } from "#app/data/nature";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { AbilityAttr } from "#app/system/game-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 { isNullOrUndefined, randSeedShuffle } from "#app/utils";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import i18next from "i18next";
|
||||
import { getStatKey } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
@ -71,8 +75,8 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
encounter.misc = {
|
||||
playerPokemon: pokemon,
|
||||
@ -81,24 +85,24 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
|
||||
// Only Pokemon that are not KOed/legal can be trained
|
||||
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) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||
|
||||
// Spawn light training session with chosen pokemon
|
||||
// Every 50 waves, add +1 boss segment, capping at 5
|
||||
const segments = Math.min(
|
||||
2 + Math.floor(scene.currentBattle.waveIndex / 50),
|
||||
2 + Math.floor(globalScene.currentBattle.waveIndex / 50),
|
||||
5
|
||||
);
|
||||
const modifiers = new ModifiersHolder();
|
||||
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||
const config = getEnemyConfig(playerPokemon, segments, modifiers);
|
||||
globalScene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||
|
||||
const onBeforeRewardsPhase = () => {
|
||||
encounter.setDialogueToken("stat1", "-");
|
||||
@ -148,23 +152,23 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
|
||||
if (improvedCount > 0) {
|
||||
playerPokemon.calculateStats();
|
||||
scene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs);
|
||||
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||
globalScene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs);
|
||||
globalScene.gameData.setPokemonCaught(playerPokemon, false);
|
||||
}
|
||||
|
||||
// Add pokemon and mods back
|
||||
scene.getPlayerParty().push(playerPokemon);
|
||||
globalScene.getPlayerParty().push(playerPokemon);
|
||||
for (const mod of modifiers.value) {
|
||||
mod.pokemonId = playerPokemon.id;
|
||||
scene.addModifier(mod, true, false, false, true);
|
||||
globalScene.addModifier(mod, true, false, false, true);
|
||||
}
|
||||
scene.updateModifiers(true);
|
||||
queueEncounterMessage(scene, `${namespace}:option.1.finished`);
|
||||
globalScene.updateModifiers(true);
|
||||
queueEncounterMessage(`${namespace}:option.1.finished`);
|
||||
};
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||
setEncounterRewards({ fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -182,15 +186,15 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
// Open menu for selecting pokemon and Nature
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const natures = new Array(25).fill(null).map((val, i) => i as Nature);
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Return the options for nature selection
|
||||
return natures.map((nature: Nature) => {
|
||||
const option: OptionSelectItem = {
|
||||
label: getNatureName(nature, true, true, true, scene.uiTheme),
|
||||
label: getNatureName(nature, true, true, true, globalScene.uiTheme),
|
||||
handler: () => {
|
||||
// Pokemon and second option selected
|
||||
encounter.setDialogueToken("nature", getNatureName(nature));
|
||||
@ -207,40 +211,40 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
|
||||
// Only Pokemon that are not KOed/legal can be trained
|
||||
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) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||
|
||||
// Spawn medium training session with chosen pokemon
|
||||
// Every 40 waves, add +1 boss segment, capping at 6
|
||||
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 40), 6);
|
||||
const segments = Math.min(2 + Math.floor(globalScene.currentBattle.waveIndex / 40), 6);
|
||||
const modifiers = new ModifiersHolder();
|
||||
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||
const config = getEnemyConfig(playerPokemon, segments, modifiers);
|
||||
globalScene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||
|
||||
const onBeforeRewardsPhase = () => {
|
||||
queueEncounterMessage(scene, `${namespace}:option.2.finished`);
|
||||
queueEncounterMessage(`${namespace}:option.2.finished`);
|
||||
// Add the pokemon back to party with Nature change
|
||||
playerPokemon.setCustomNature(encounter.misc.chosenNature);
|
||||
scene.gameData.unlockSpeciesNature(playerPokemon.species, encounter.misc.chosenNature);
|
||||
globalScene.gameData.unlockSpeciesNature(playerPokemon.species, encounter.misc.chosenNature);
|
||||
|
||||
// Add pokemon and modifiers back
|
||||
scene.getPlayerParty().push(playerPokemon);
|
||||
globalScene.getPlayerParty().push(playerPokemon);
|
||||
for (const mod of modifiers.value) {
|
||||
mod.pokemonId = playerPokemon.id;
|
||||
scene.addModifier(mod, true, false, false, true);
|
||||
globalScene.addModifier(mod, true, false, false, true);
|
||||
}
|
||||
scene.updateModifiers(true);
|
||||
globalScene.updateModifiers(true);
|
||||
};
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||
setEncounterRewards({ fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -258,9 +262,9 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||
// Open menu for selecting pokemon and ability to learn
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Return the options for ability selection
|
||||
const speciesForm = !!pokemon.getFusionSpeciesForm()
|
||||
@ -286,7 +290,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
return true;
|
||||
},
|
||||
onHover: () => {
|
||||
showEncounterText(scene, ability.description, 0, 0, false);
|
||||
showEncounterText(ability.description, 0, 0, false);
|
||||
},
|
||||
};
|
||||
optionSelectItems.push(option);
|
||||
@ -298,28 +302,28 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
|
||||
// Only Pokemon that are not KOed/legal can be trained
|
||||
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) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOptionPhase(async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||
|
||||
// Spawn hard training session with chosen pokemon
|
||||
// Every 30 waves, add +1 boss segment, capping at 6
|
||||
// Also starts with +1 to all stats
|
||||
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 30), 6);
|
||||
const segments = Math.min(2 + Math.floor(globalScene.currentBattle.waveIndex / 30), 6);
|
||||
const modifiers = new ModifiersHolder();
|
||||
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
||||
const config = getEnemyConfig(playerPokemon, segments, modifiers);
|
||||
config.pokemonConfigs![0].tags = [
|
||||
BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON,
|
||||
];
|
||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||
globalScene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||
|
||||
const onBeforeRewardsPhase = () => {
|
||||
queueEncounterMessage(scene, `${namespace}:option.3.finished`);
|
||||
queueEncounterMessage(`${namespace}:option.3.finished`);
|
||||
// Add the pokemon back to party with ability change
|
||||
const abilityIndex = encounter.misc.abilityIndex;
|
||||
|
||||
@ -330,8 +334,8 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
const rootFusionSpecies = playerPokemon.fusionSpecies?.getRootSpeciesId();
|
||||
if (!isNullOrUndefined(rootFusionSpecies)
|
||||
&& speciesStarterCosts.hasOwnProperty(rootFusionSpecies)
|
||||
&& !!scene.gameData.dexData[rootFusionSpecies].caughtAttr) {
|
||||
scene.gameData.starterData[rootFusionSpecies].abilityAttr |= playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2
|
||||
&& !!globalScene.gameData.dexData[rootFusionSpecies].caughtAttr) {
|
||||
globalScene.gameData.starterData[rootFusionSpecies].abilityAttr |= playerPokemon.fusionAbilityIndex !== 1 || playerPokemon.fusionSpecies?.ability2
|
||||
? 1 << playerPokemon.fusionAbilityIndex
|
||||
: AbilityAttr.ABILITY_HIDDEN;
|
||||
}
|
||||
@ -340,20 +344,20 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
}
|
||||
|
||||
playerPokemon.calculateStats();
|
||||
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||
globalScene.gameData.setPokemonCaught(playerPokemon, false);
|
||||
|
||||
// Add pokemon and mods back
|
||||
scene.getPlayerParty().push(playerPokemon);
|
||||
globalScene.getPlayerParty().push(playerPokemon);
|
||||
for (const mod of modifiers.value) {
|
||||
mod.pokemonId = playerPokemon.id;
|
||||
scene.addModifier(mod, true, false, false, true);
|
||||
globalScene.addModifier(mod, true, false, false, true);
|
||||
}
|
||||
scene.updateModifiers(true);
|
||||
globalScene.updateModifiers(true);
|
||||
};
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||
setEncounterRewards({ fillRemaining: true }, undefined, onBeforeRewardsPhase);
|
||||
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
await initBattleWithEnemyConfig(config);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -367,15 +371,15 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave encounter with no rewards or exp
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
.build();
|
||||
|
||||
function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig {
|
||||
function getEnemyConfig(playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig {
|
||||
playerPokemon.resetSummonData();
|
||||
|
||||
// Passes modifiers by reference
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import type { EnemyPartyConfig, EnemyPokemonConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
@ -58,8 +61,8 @@ export const TrashToTreasureEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mon (shiny locked)
|
||||
const bossSpecies = getPokemonSpecies(Species.GARBODOR);
|
||||
@ -79,10 +82,10 @@ export const TrashToTreasureEncounter: MysteryEncounter =
|
||||
encounter.enemyPartyConfigs = [ config ];
|
||||
|
||||
// Load animations/sfx for Garbodor fight start moves
|
||||
loadCustomMovesForEncounter(scene, [ Moves.TOXIC, Moves.AMNESIA ]);
|
||||
loadCustomMovesForEncounter([ Moves.TOXIC, Moves.AMNESIA ]);
|
||||
|
||||
scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
|
||||
scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
|
||||
globalScene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
|
||||
globalScene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
|
||||
|
||||
encounter.setDialogueToken("costMultiplier", SHOP_ITEM_COST_MULTIPLIER.toString());
|
||||
|
||||
@ -100,24 +103,24 @@ export const TrashToTreasureEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Play Dig2 and then Venom Drench sfx
|
||||
doGarbageDig(scene);
|
||||
doGarbageDig();
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Gain 2 Leftovers and 2 Shell Bell
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
await tryApplyDigRewardItems(scene);
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
await tryApplyDigRewardItems();
|
||||
|
||||
const blackSludge = generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [ SHOP_ITEM_COST_MULTIPLIER ]);
|
||||
const blackSludge = generateModifierType(modifierTypes.MYSTERY_ENCOUNTER_BLACK_SLUDGE, [ SHOP_ITEM_COST_MULTIPLIER ]);
|
||||
const modifier = blackSludge?.newModifier();
|
||||
if (modifier) {
|
||||
await scene.addModifier(modifier, false, false, false, true);
|
||||
scene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 });
|
||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: modifier.type.name }), null, undefined, true);
|
||||
await globalScene.addModifier(modifier, false, false, false, true);
|
||||
globalScene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 });
|
||||
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: modifier.type.name }), null, undefined, true);
|
||||
}
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -133,15 +136,15 @@ export const TrashToTreasureEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Investigate garbage, battle Gmax Garbodor
|
||||
scene.setFieldScale(0.75);
|
||||
await showEncounterText(scene, `${namespace}:option.2.selected_2`);
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
globalScene.setFieldScale(0.75);
|
||||
await showEncounterText(`${namespace}:option.2.selected_2`);
|
||||
await transitionMysteryEncounterIntroVisuals();
|
||||
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
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 });
|
||||
encounter.startOfBattleEffects.push(
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
@ -155,81 +158,81 @@ export const TrashToTreasureEncounter: MysteryEncounter =
|
||||
move: new PokemonMove(Moves.AMNESIA),
|
||||
ignorePp: true
|
||||
});
|
||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
async function tryApplyDigRewardItems(scene: BattleScene) {
|
||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
const leftovers = generateModifierType(scene, modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType;
|
||||
async function tryApplyDigRewardItems() {
|
||||
const shellBell = generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||
const leftovers = generateModifierType(modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType;
|
||||
|
||||
const party = scene.getPlayerParty();
|
||||
const party = globalScene.getPlayerParty();
|
||||
|
||||
// Iterate over the party until an item was successfully given
|
||||
// First leftovers
|
||||
for (const pokemon of party) {
|
||||
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||
const heldItems = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||
const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier;
|
||||
|
||||
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount(scene)) {
|
||||
await applyModifierTypeToPlayerPokemon(scene, pokemon, leftovers);
|
||||
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount()) {
|
||||
await applyModifierTypeToPlayerPokemon(pokemon, leftovers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Second leftovers
|
||||
for (const pokemon of party) {
|
||||
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||
const heldItems = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||
const existingLeftovers = heldItems.find(m => m instanceof TurnHealModifier) as TurnHealModifier;
|
||||
|
||||
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount(scene)) {
|
||||
await applyModifierTypeToPlayerPokemon(scene, pokemon, leftovers);
|
||||
if (!existingLeftovers || existingLeftovers.getStackCount() < existingLeftovers.getMaxStackCount()) {
|
||||
await applyModifierTypeToPlayerPokemon(pokemon, leftovers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scene.playSound("item_fanfare");
|
||||
await showEncounterText(scene, i18next.t("battle:rewardGainCount", { modifierName: leftovers.name, count: 2 }), null, undefined, true);
|
||||
globalScene.playSound("item_fanfare");
|
||||
await showEncounterText(i18next.t("battle:rewardGainCount", { modifierName: leftovers.name, count: 2 }), null, undefined, true);
|
||||
|
||||
// First Shell bell
|
||||
for (const pokemon of party) {
|
||||
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||
const heldItems = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||
const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier;
|
||||
|
||||
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount(scene)) {
|
||||
await applyModifierTypeToPlayerPokemon(scene, pokemon, shellBell);
|
||||
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount()) {
|
||||
await applyModifierTypeToPlayerPokemon(pokemon, shellBell);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Second Shell bell
|
||||
for (const pokemon of party) {
|
||||
const heldItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||
const heldItems = globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||
&& m.pokemonId === pokemon.id, true) as PokemonHeldItemModifier[];
|
||||
const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier;
|
||||
|
||||
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount(scene)) {
|
||||
await applyModifierTypeToPlayerPokemon(scene, pokemon, shellBell);
|
||||
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount()) {
|
||||
await applyModifierTypeToPlayerPokemon(pokemon, shellBell);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scene.playSound("item_fanfare");
|
||||
await showEncounterText(scene, i18next.t("battle:rewardGainCount", { modifierName: shellBell.name, count: 2 }), null, undefined, true);
|
||||
globalScene.playSound("item_fanfare");
|
||||
await showEncounterText(i18next.t("battle:rewardGainCount", { modifierName: shellBell.name, count: 2 }), null, undefined, true);
|
||||
}
|
||||
|
||||
function doGarbageDig(scene: BattleScene) {
|
||||
scene.playSound("battle_anims/PRSFX- Dig2");
|
||||
scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Dig2");
|
||||
scene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 });
|
||||
function doGarbageDig() {
|
||||
globalScene.playSound("battle_anims/PRSFX- Dig2");
|
||||
globalScene.time.delayedCall(SOUND_EFFECT_WAIT_TIME, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Dig2");
|
||||
globalScene.playSound("battle_anims/PRSFX- Venom Drench", { volume: 2 });
|
||||
});
|
||||
scene.time.delayedCall(SOUND_EFFECT_WAIT_TIME * 2, () => {
|
||||
scene.playSound("battle_anims/PRSFX- Dig2");
|
||||
globalScene.time.delayedCall(SOUND_EFFECT_WAIT_TIME * 2, () => {
|
||||
globalScene.playSound("battle_anims/PRSFX- Dig2");
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, 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, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
import Pokemon, { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { getPartyLuckValue } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MoveRequirement, PersistentModifierRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
@ -13,7 +16,7 @@ import { TrainerSlot } from "#app/data/trainer-config";
|
||||
import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils";
|
||||
import { Moves } from "#enums/moves";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { SelfStatusMove } from "#app/data/move";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
@ -23,7 +26,8 @@ import { BerryModifier } from "#app/modifier/modifier";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounters/uncommonBreed";
|
||||
@ -46,21 +50,21 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
text: `${namespace}:intro`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mon
|
||||
// Level equal to 2 below highest party member
|
||||
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2;
|
||||
const level = getHighestLevelPlayerPokemon(false, true).level - 2;
|
||||
let species: PokemonSpecies;
|
||||
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);
|
||||
if (globalScene.eventManager.isEventActive() && globalScene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
|
||||
const eventEncounter = randSeedItem(globalScene.eventManager.activeEvent()!.uncommonBreedEncounters!);
|
||||
const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, globalScene.gameMode);
|
||||
species = getPokemonSpecies( levelSpecies );
|
||||
} else {
|
||||
species = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
||||
species = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, level, 0, getPartyLuckValue(globalScene.getPlayerParty()), true);
|
||||
}
|
||||
const pokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, true);
|
||||
const pokemon = new EnemyPokemon(species, level, TrainerSlot.NONE, true);
|
||||
|
||||
// Pokemon will always have one of its egg moves in its moveset
|
||||
const eggMoves = pokemon.getEggMoves();
|
||||
@ -81,7 +85,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
}
|
||||
|
||||
// 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.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
|
||||
|
||||
@ -93,8 +97,8 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
isBoss: false,
|
||||
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(pokemon.scene, `${namespace}:option.1.stat_boost`);
|
||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
}
|
||||
}],
|
||||
};
|
||||
@ -115,16 +119,16 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
];
|
||||
|
||||
encounter.setDialogueToken("enemyPokemon", pokemon.getNameToRender());
|
||||
scene.loadSe("PRSFX- Spotlight2", "battle_anims", "PRSFX- Spotlight2.wav");
|
||||
globalScene.loadSe("PRSFX- Spotlight2", "battle_anims", "PRSFX- Spotlight2.wav");
|
||||
return true;
|
||||
})
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
.withOnVisualsStart(() => {
|
||||
// Animate the pokemon
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const pokemonSprite = encounter.introVisuals!.getSprites();
|
||||
|
||||
// Bounce at the end, then shiny sparkle if the Pokemon is shiny
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: pokemonSprite,
|
||||
duration: 300,
|
||||
ease: "Cubic.easeOut",
|
||||
@ -134,7 +138,7 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
onComplete: () => encounter.introVisuals?.playShinySparkles()
|
||||
});
|
||||
|
||||
scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2"));
|
||||
globalScene.time.delayedCall(500, () => globalScene.playSound("battle_anims/PRSFX- Spotlight2"));
|
||||
return true;
|
||||
})
|
||||
.setLocalizationKey(`${namespace}`)
|
||||
@ -151,9 +155,9 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Pick battle
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const eggMove = encounter.misc.eggMove;
|
||||
if (!isNullOrUndefined(eggMove)) {
|
||||
@ -171,8 +175,8 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
});
|
||||
}
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||
}
|
||||
)
|
||||
.withOption(
|
||||
@ -189,33 +193,33 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
}
|
||||
]
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Give it some food
|
||||
|
||||
// Remove 4 random berries from player's party
|
||||
// Get all player berry items, remove from party, and store reference
|
||||
const berryItems: BerryModifier[] = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
|
||||
const berryItems: BerryModifier[] = globalScene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const index = randSeedInt(berryItems.length);
|
||||
const randBerry = berryItems[index];
|
||||
randBerry.stackCount--;
|
||||
if (randBerry.stackCount === 0) {
|
||||
scene.removeModifier(randBerry);
|
||||
globalScene.removeModifier(randBerry);
|
||||
berryItems.splice(index, 1);
|
||||
}
|
||||
}
|
||||
await scene.updateModifiers(true, true);
|
||||
await globalScene.updateModifiers(true, true);
|
||||
|
||||
// Pokemon joins the team, with 2 egg moves
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const pokemon = encounter.misc.pokemon;
|
||||
|
||||
// Give 1 additional egg move
|
||||
givePokemonExtraEggMove(pokemon, encounter.misc.eggMove);
|
||||
|
||||
await catchPokemon(scene, pokemon, null, PokeballType.POKEBALL, false);
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
await catchPokemon(pokemon, null, PokeballType.POKEBALL, false);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -233,10 +237,10 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
}
|
||||
]
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Attract the pokemon with a move
|
||||
// Pokemon joins the team, with 2 egg moves and IVs rolled an additional time
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
const pokemon = encounter.misc.pokemon;
|
||||
|
||||
// Give 1 additional egg move
|
||||
@ -248,12 +252,12 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||
return newValue > iv ? newValue : iv;
|
||||
});
|
||||
|
||||
await catchPokemon(scene, pokemon, null, PokeballType.POKEBALL, false);
|
||||
await catchPokemon(pokemon, null, PokeballType.POKEBALL, false);
|
||||
if (encounter.selectedOption?.primaryPokemon?.id) {
|
||||
setEncounterExp(scene, encounter.selectedOption.primaryPokemon.id, pokemon.getExpValue(), false);
|
||||
setEncounterExp(encounter.selectedOption.primaryPokemon.id, pokemon.getExpValue(), false);
|
||||
}
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
setEncounterRewards({ fillRemaining: true });
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build()
|
||||
)
|
||||
|
||||
@ -1,20 +1,27 @@
|
||||
import { Type } from "#enums/type";
|
||||
import type { Type } from "#enums/type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
||||
import type { EnemyPartyConfig, EnemyPokemonConfig } from "../utils/encounter-phase-utils";
|
||||
import { generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, } from "../utils/encounter-phase-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
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 { IntegerHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
|
||||
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
||||
import { achvs } from "#app/system/achv";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
import { doPokemonTransformationSequence, TransformationScreenPosition } from "#app/data/mystery-encounters/utils/encounter-transformation-sequence";
|
||||
import { getLevelTotalExp } from "#app/data/exp";
|
||||
@ -25,7 +32,7 @@ import { PlayerGender } from "#enums/player-gender";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import { Nature } from "#enums/nature";
|
||||
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import { trainerConfigs, TrainerPartyTemplate } from "#app/data/trainer-config";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
|
||||
@ -138,21 +145,21 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
.withTitle(`${namespace}:title`)
|
||||
.withDescription(`${namespace}:description`)
|
||||
.withQuery(`${namespace}:query`)
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
scene.loadBgm("mystery_encounter_weird_dream", "mystery_encounter_weird_dream.mp3");
|
||||
.withOnInit(() => {
|
||||
globalScene.loadBgm("mystery_encounter_weird_dream", "mystery_encounter_weird_dream.mp3");
|
||||
|
||||
// Calculate all the newly transformed Pokemon and begin asset load
|
||||
const teamTransformations = getTeamTransformations(scene);
|
||||
const teamTransformations = getTeamTransformations();
|
||||
const loadAssets = teamTransformations.map(t => (t.newPokemon as PlayerPokemon).loadAssets());
|
||||
scene.currentBattle.mysteryEncounter!.misc = {
|
||||
globalScene.currentBattle.mysteryEncounter!.misc = {
|
||||
teamTransformations,
|
||||
loadAssets
|
||||
};
|
||||
|
||||
return true;
|
||||
})
|
||||
.withOnVisualsStart((scene: BattleScene) => {
|
||||
scene.fadeAndSwitchBgm("mystery_encounter_weird_dream");
|
||||
.withOnVisualsStart(() => {
|
||||
globalScene.fadeAndSwitchBgm("mystery_encounter_weird_dream");
|
||||
return true;
|
||||
})
|
||||
.withOption(
|
||||
@ -168,25 +175,25 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
}
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
.withPreOptionPhase(async () => {
|
||||
// Play the animation as the player goes through the dialogue
|
||||
scene.time.delayedCall(1000, () => {
|
||||
doShowDreamBackground(scene);
|
||||
globalScene.time.delayedCall(1000, () => {
|
||||
doShowDreamBackground();
|
||||
});
|
||||
|
||||
for (const transformation of scene.currentBattle.mysteryEncounter!.misc.teamTransformations) {
|
||||
scene.removePokemonFromPlayerParty(transformation.previousPokemon, false);
|
||||
scene.getPlayerParty().push(transformation.newPokemon);
|
||||
for (const transformation of globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations) {
|
||||
globalScene.removePokemonFromPlayerParty(transformation.previousPokemon, false);
|
||||
globalScene.getPlayerParty().push(transformation.newPokemon);
|
||||
}
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
.withOptionPhase(async () => {
|
||||
// Starts cutscene dialogue, but does not await so that cutscene plays as player goes through dialogue
|
||||
const cutsceneDialoguePromise = showEncounterText(scene, `${namespace}:option.1.cutscene`);
|
||||
const cutsceneDialoguePromise = showEncounterText(`${namespace}:option.1.cutscene`);
|
||||
|
||||
// Change the entire player's party
|
||||
// Wait for all new Pokemon assets to be loaded before showing transformation animations
|
||||
await Promise.all(scene.currentBattle.mysteryEncounter!.misc.loadAssets);
|
||||
const transformations = scene.currentBattle.mysteryEncounter!.misc.teamTransformations;
|
||||
await Promise.all(globalScene.currentBattle.mysteryEncounter!.misc.loadAssets);
|
||||
const transformations = globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations;
|
||||
|
||||
// If there are 1-3 transformations, do them centered back to back
|
||||
// Otherwise, the first 3 transformations are executed side-by-side, then any remaining 1-3 transformations occur in those same respective positions
|
||||
@ -195,21 +202,21 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
const pokemon1 = transformation.previousPokemon;
|
||||
const pokemon2 = transformation.newPokemon;
|
||||
|
||||
await doPokemonTransformationSequence(scene, pokemon1, pokemon2, TransformationScreenPosition.CENTER);
|
||||
await doPokemonTransformationSequence(pokemon1, pokemon2, TransformationScreenPosition.CENTER);
|
||||
}
|
||||
} else {
|
||||
await doSideBySideTransformations(scene, transformations);
|
||||
await doSideBySideTransformations(transformations);
|
||||
}
|
||||
|
||||
// Make sure player has finished cutscene dialogue
|
||||
await cutsceneDialoguePromise;
|
||||
|
||||
doHideDreamBackground(scene);
|
||||
await showEncounterText(scene, `${namespace}:option.1.dream_complete`);
|
||||
doHideDreamBackground();
|
||||
await showEncounterText(`${namespace}:option.1.dream_complete`);
|
||||
|
||||
await doNewTeamPostProcess(scene, transformations);
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.MEMORY_MUSHROOM, modifierTypes.ROGUE_BALL, modifierTypes.MINT, modifierTypes.MINT, modifierTypes.MINT ], fillRemaining: false });
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
await doNewTeamPostProcess(transformations);
|
||||
setEncounterRewards({ guaranteedModifierTypeFuncs: [ modifierTypes.MEMORY_MUSHROOM, modifierTypes.ROGUE_BALL, modifierTypes.MINT, modifierTypes.MINT, modifierTypes.MINT ], fillRemaining: false });
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
@ -223,9 +230,9 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Battle your "future" team for some item rewards
|
||||
const transformations: PokemonTransformation[] = scene.currentBattle.mysteryEncounter!.misc.teamTransformations;
|
||||
const transformations: PokemonTransformation[] = globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations;
|
||||
|
||||
// Uses the pokemon that player's party would have transformed into
|
||||
const enemyPokemonConfigs: EnemyPokemonConfig[] = [];
|
||||
@ -233,7 +240,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
const newPokemon = transformation.newPokemon;
|
||||
const previousPokemon = transformation.previousPokemon;
|
||||
|
||||
await postProcessTransformedPokemon(scene, previousPokemon, newPokemon, newPokemon.species.getRootSpeciesId(), true);
|
||||
await postProcessTransformedPokemon(previousPokemon, newPokemon, newPokemon.species.getRootSpeciesId(), true);
|
||||
|
||||
const dataSource = new PokemonData(newPokemon);
|
||||
dataSource.player = false;
|
||||
@ -251,7 +258,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
if (shouldGetOldGateau(newPokemon)) {
|
||||
const stats = getOldGateauBoostedStats(newPokemon);
|
||||
newPokemonHeldItemConfigs.push({
|
||||
modifier: generateModifierType(scene, modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU, [ OLD_GATEAU_STATS_UP, stats ]) as PokemonHeldItemModifierType,
|
||||
modifier: generateModifierType(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU, [ OLD_GATEAU_STATS_UP, stats ]) as PokemonHeldItemModifierType,
|
||||
stackCount: 1,
|
||||
isTransferable: false
|
||||
});
|
||||
@ -268,7 +275,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
enemyPokemonConfigs.push(enemyConfig);
|
||||
}
|
||||
|
||||
const genderIndex = scene.gameData.gender ?? PlayerGender.UNSET;
|
||||
const genderIndex = globalScene.gameData.gender ?? PlayerGender.UNSET;
|
||||
const trainerConfig = trainerConfigs[genderIndex === PlayerGender.FEMALE ? TrainerType.FUTURE_SELF_F : TrainerType.FUTURE_SELF_M].clone();
|
||||
trainerConfig.setPartyTemplates(new TrainerPartyTemplate(transformations.length, PartyMemberStrength.STRONG));
|
||||
const enemyPartyConfig: EnemyPartyConfig = {
|
||||
@ -280,7 +287,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
const onBeforeRewards = () => {
|
||||
// Before battle rewards, unlock the passive on a pokemon in the player's team for the rest of the run (not permanently)
|
||||
// One random pokemon will get its passive unlocked
|
||||
const passiveDisabledPokemon = scene.getPlayerParty().filter(p => !p.passive);
|
||||
const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive);
|
||||
if (passiveDisabledPokemon?.length > 0) {
|
||||
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
|
||||
enablePassiveMon.passive = true;
|
||||
@ -288,10 +295,10 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
}
|
||||
};
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: false }, undefined, onBeforeRewards);
|
||||
setEncounterRewards({ guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT ], fillRemaining: false }, undefined, onBeforeRewards);
|
||||
|
||||
await showEncounterText(scene, `${namespace}:option.2.selected_2`, null, undefined, true);
|
||||
await initBattleWithEnemyConfig(scene, enemyPartyConfig);
|
||||
await showEncounterText(`${namespace}:option.2.selected_2`, null, undefined, true);
|
||||
await initBattleWithEnemyConfig(enemyPartyConfig);
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
@ -304,9 +311,9 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
async () => {
|
||||
// Leave, reduce party levels by 10%
|
||||
for (const pokemon of scene.getPlayerParty()) {
|
||||
for (const pokemon of globalScene.getPlayerParty()) {
|
||||
pokemon.level = Math.max(Math.ceil((100 - PERCENT_LEVEL_LOSS_ON_REFUSE) / 100 * pokemon.level), 1);
|
||||
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
|
||||
pokemon.levelExp = 0;
|
||||
@ -316,7 +323,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
||||
await pokemon.updateInfo();
|
||||
}
|
||||
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
return true;
|
||||
}
|
||||
)
|
||||
@ -329,8 +336,8 @@ interface PokemonTransformation {
|
||||
heldItems: PokemonHeldItemModifier[];
|
||||
}
|
||||
|
||||
function getTeamTransformations(scene: BattleScene): PokemonTransformation[] {
|
||||
const party = scene.getPlayerParty();
|
||||
function getTeamTransformations(): PokemonTransformation[] {
|
||||
const party = globalScene.getPlayerParty();
|
||||
// Removes all pokemon from the party
|
||||
const alreadyUsedSpecies: PokemonSpecies[] = party.map(p => p.species);
|
||||
const pokemonTransformations: PokemonTransformation[] = party.map(p => {
|
||||
@ -379,37 +386,37 @@ function getTeamTransformations(scene: BattleScene): PokemonTransformation[] {
|
||||
|
||||
for (const transformation of pokemonTransformations) {
|
||||
const newAbilityIndex = randSeedInt(transformation.newSpecies.getAbilityCount());
|
||||
transformation.newPokemon = scene.addPlayerPokemon(transformation.newSpecies, transformation.previousPokemon.level, newAbilityIndex, undefined);
|
||||
transformation.newPokemon = globalScene.addPlayerPokemon(transformation.newSpecies, transformation.previousPokemon.level, newAbilityIndex, undefined);
|
||||
}
|
||||
|
||||
return pokemonTransformations;
|
||||
}
|
||||
|
||||
async function doNewTeamPostProcess(scene: BattleScene, transformations: PokemonTransformation[]) {
|
||||
async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
|
||||
let atLeastOneNewStarter = false;
|
||||
for (const transformation of transformations) {
|
||||
const previousPokemon = transformation.previousPokemon;
|
||||
const newPokemon = transformation.newPokemon;
|
||||
const speciesRootForm = newPokemon.species.getRootSpeciesId();
|
||||
|
||||
if (await postProcessTransformedPokemon(scene, previousPokemon, newPokemon, speciesRootForm)) {
|
||||
if (await postProcessTransformedPokemon(previousPokemon, newPokemon, speciesRootForm)) {
|
||||
atLeastOneNewStarter = true;
|
||||
}
|
||||
|
||||
// Copy old items to new pokemon
|
||||
for (const item of transformation.heldItems) {
|
||||
item.pokemonId = newPokemon.id;
|
||||
await scene.addModifier(item, false, false, false, true);
|
||||
await globalScene.addModifier(item, false, false, false, true);
|
||||
}
|
||||
// Any pokemon that is below 570 BST gets +20 permanent BST to 3 stats
|
||||
if (shouldGetOldGateau(newPokemon)) {
|
||||
const stats = getOldGateauBoostedStats(newPokemon);
|
||||
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU()
|
||||
.generateType(scene.getPlayerParty(), [ OLD_GATEAU_STATS_UP, stats ])
|
||||
.generateType(globalScene.getPlayerParty(), [ OLD_GATEAU_STATS_UP, stats ])
|
||||
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU);
|
||||
const modifier = modType?.newModifier(newPokemon);
|
||||
if (modifier) {
|
||||
await scene.addModifier(modifier, false, false, false, true);
|
||||
await globalScene.addModifier(modifier, false, false, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,7 +425,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
||||
}
|
||||
|
||||
// One random pokemon will get its passive unlocked
|
||||
const passiveDisabledPokemon = scene.getPlayerParty().filter(p => !p.passive);
|
||||
const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive);
|
||||
if (passiveDisabledPokemon?.length > 0) {
|
||||
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
|
||||
enablePassiveMon.passive = true;
|
||||
@ -427,27 +434,26 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
||||
|
||||
// If at least one new starter was unlocked, play 1 fanfare
|
||||
if (atLeastOneNewStarter) {
|
||||
scene.playSound("level_up_fanfare");
|
||||
globalScene.playSound("level_up_fanfare");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies special changes to the newly transformed pokemon, such as passing previous moves, gaining egg moves, etc.
|
||||
* Returns whether the transformed pokemon unlocks a new starter for the player.
|
||||
* @param scene
|
||||
* @param previousPokemon
|
||||
* @param newPokemon
|
||||
* @param speciesRootForm
|
||||
* @param forBattle Default `false`. If false, will perform achievements and dex unlocks for the player.
|
||||
*/
|
||||
async function postProcessTransformedPokemon(scene: BattleScene, previousPokemon: PlayerPokemon, newPokemon: PlayerPokemon, speciesRootForm: Species, forBattle: boolean = false): Promise<boolean> {
|
||||
async function postProcessTransformedPokemon(previousPokemon: PlayerPokemon, newPokemon: PlayerPokemon, speciesRootForm: Species, forBattle: boolean = false): Promise<boolean> {
|
||||
let isNewStarter = false;
|
||||
// Roll HA a second time
|
||||
if (newPokemon.species.abilityHidden) {
|
||||
const hiddenIndex = newPokemon.species.ability2 ? 2 : 1;
|
||||
if (newPokemon.abilityIndex < hiddenIndex) {
|
||||
const hiddenAbilityChance = new IntegerHolder(256);
|
||||
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
|
||||
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||
|
||||
@ -469,26 +475,26 @@ async function postProcessTransformedPokemon(scene: BattleScene, previousPokemon
|
||||
// For pokemon at/below 570 BST or any shiny pokemon, unlock it permanently as if you had caught it
|
||||
if (!forBattle && (newPokemon.getSpeciesForm().getBaseStatTotal() <= NON_LEGENDARY_BST_THRESHOLD || newPokemon.isShiny())) {
|
||||
if (newPokemon.getSpeciesForm().abilityHidden && newPokemon.abilityIndex === newPokemon.getSpeciesForm().getAbilityCount() - 1) {
|
||||
scene.validateAchv(achvs.HIDDEN_ABILITY);
|
||||
globalScene.validateAchv(achvs.HIDDEN_ABILITY);
|
||||
}
|
||||
|
||||
if (newPokemon.species.subLegendary) {
|
||||
scene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
|
||||
globalScene.validateAchv(achvs.CATCH_SUB_LEGENDARY);
|
||||
}
|
||||
|
||||
if (newPokemon.species.legendary) {
|
||||
scene.validateAchv(achvs.CATCH_LEGENDARY);
|
||||
globalScene.validateAchv(achvs.CATCH_LEGENDARY);
|
||||
}
|
||||
|
||||
if (newPokemon.species.mythical) {
|
||||
scene.validateAchv(achvs.CATCH_MYTHICAL);
|
||||
globalScene.validateAchv(achvs.CATCH_MYTHICAL);
|
||||
}
|
||||
|
||||
scene.gameData.updateSpeciesDexIvs(newPokemon.species.getRootSpeciesId(true), newPokemon.ivs);
|
||||
const newStarterUnlocked = await scene.gameData.setPokemonCaught(newPokemon, true, false, false);
|
||||
globalScene.gameData.updateSpeciesDexIvs(newPokemon.species.getRootSpeciesId(true), newPokemon.ivs);
|
||||
const newStarterUnlocked = await globalScene.gameData.setPokemonCaught(newPokemon, true, false, false);
|
||||
if (newStarterUnlocked) {
|
||||
isNewStarter = true;
|
||||
await showEncounterText(scene, i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() }));
|
||||
await showEncounterText(i18next.t("battle:addedAsAStarter", { pokemonName: getPokemonSpecies(speciesRootForm).getName() }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -504,8 +510,8 @@ async function postProcessTransformedPokemon(scene: BattleScene, previousPokemon
|
||||
});
|
||||
|
||||
// For pokemon that the player owns (including ones just caught), gain a candy
|
||||
if (!forBattle && !!scene.gameData.dexData[speciesRootForm].caughtAttr) {
|
||||
scene.gameData.addStarterCandy(getPokemonSpecies(speciesRootForm), 1);
|
||||
if (!forBattle && !!globalScene.gameData.dexData[speciesRootForm].caughtAttr) {
|
||||
globalScene.gameData.addStarterCandy(getPokemonSpecies(speciesRootForm), 1);
|
||||
}
|
||||
|
||||
// Set the moveset of the new pokemon to be the same as previous, but with 1 egg move and 1 (attempted) STAB move of the new species
|
||||
@ -515,7 +521,7 @@ async function postProcessTransformedPokemon(scene: BattleScene, previousPokemon
|
||||
|
||||
newPokemon.moveset = previousPokemon.moveset.slice(0);
|
||||
|
||||
const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(scene, newPokemon, speciesRootForm, forBattle);
|
||||
const newEggMoveIndex = await addEggMoveToNewPokemonMoveset(newPokemon, speciesRootForm, forBattle);
|
||||
|
||||
// Try to add a favored STAB move (might fail if Pokemon already knows a bunch of moves from newPokemonGeneratedMoveset)
|
||||
addFavoredMoveToNewPokemonMoveset(newPokemon, newPokemonGeneratedMoveset, newEggMoveIndex);
|
||||
@ -597,31 +603,31 @@ function getTransformedSpecies(originalBst: number, bstSearchRange: [number, num
|
||||
return newSpecies;
|
||||
}
|
||||
|
||||
function doShowDreamBackground(scene: BattleScene) {
|
||||
const transformationContainer = scene.add.container(0, -scene.game.canvas.height / 6);
|
||||
function doShowDreamBackground() {
|
||||
const transformationContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6);
|
||||
transformationContainer.name = "Dream Background";
|
||||
|
||||
// In case it takes a bit for video to load
|
||||
const transformationStaticBg = scene.add.rectangle(0, 0, scene.game.canvas.width / 6, scene.game.canvas.height / 6, 0);
|
||||
const transformationStaticBg = globalScene.add.rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0);
|
||||
transformationStaticBg.setName("Black Background");
|
||||
transformationStaticBg.setOrigin(0, 0);
|
||||
transformationContainer.add(transformationStaticBg);
|
||||
transformationStaticBg.setVisible(true);
|
||||
|
||||
const transformationVideoBg: Phaser.GameObjects.Video = scene.add.video(0, 0, "evo_bg").stop();
|
||||
const transformationVideoBg: Phaser.GameObjects.Video = globalScene.add.video(0, 0, "evo_bg").stop();
|
||||
transformationVideoBg.setLoop(true);
|
||||
transformationVideoBg.setOrigin(0, 0);
|
||||
transformationVideoBg.setScale(0.4359673025);
|
||||
transformationContainer.add(transformationVideoBg);
|
||||
|
||||
scene.fieldUI.add(transformationContainer);
|
||||
scene.fieldUI.bringToTop(transformationContainer);
|
||||
globalScene.fieldUI.add(transformationContainer);
|
||||
globalScene.fieldUI.bringToTop(transformationContainer);
|
||||
transformationVideoBg.play();
|
||||
|
||||
transformationContainer.setVisible(true);
|
||||
transformationContainer.alpha = 0;
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: transformationContainer,
|
||||
alpha: 1,
|
||||
duration: 3000,
|
||||
@ -629,39 +635,39 @@ function doShowDreamBackground(scene: BattleScene) {
|
||||
});
|
||||
}
|
||||
|
||||
function doHideDreamBackground(scene: BattleScene) {
|
||||
const transformationContainer = scene.fieldUI.getByName("Dream Background");
|
||||
function doHideDreamBackground() {
|
||||
const transformationContainer = globalScene.fieldUI.getByName("Dream Background");
|
||||
|
||||
scene.tweens.add({
|
||||
globalScene.tweens.add({
|
||||
targets: transformationContainer,
|
||||
alpha: 0,
|
||||
duration: 3000,
|
||||
ease: "Sine.easeInOut",
|
||||
onComplete: () => {
|
||||
scene.fieldUI.remove(transformationContainer, true);
|
||||
globalScene.fieldUI.remove(transformationContainer, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function doSideBySideTransformations(scene: BattleScene, transformations: PokemonTransformation[]) {
|
||||
function doSideBySideTransformations(transformations: PokemonTransformation[]) {
|
||||
return new Promise<void>(resolve => {
|
||||
const allTransformationPromises: Promise<void>[] = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const delay = i * 4000;
|
||||
scene.time.delayedCall(delay, () => {
|
||||
globalScene.time.delayedCall(delay, () => {
|
||||
const transformation = transformations[i];
|
||||
const pokemon1 = transformation.previousPokemon;
|
||||
const pokemon2 = transformation.newPokemon;
|
||||
const screenPosition = i as TransformationScreenPosition;
|
||||
|
||||
const transformationPromise = doPokemonTransformationSequence(scene, pokemon1, pokemon2, screenPosition)
|
||||
const transformationPromise = doPokemonTransformationSequence(pokemon1, pokemon2, screenPosition)
|
||||
.then(() => {
|
||||
if (transformations.length > i + 3) {
|
||||
const nextTransformationAtPosition = transformations[i + 3];
|
||||
const nextPokemon1 = nextTransformationAtPosition.previousPokemon;
|
||||
const nextPokemon2 = nextTransformationAtPosition.newPokemon;
|
||||
|
||||
allTransformationPromises.push(doPokemonTransformationSequence(scene, nextPokemon1, nextPokemon2, screenPosition));
|
||||
allTransformationPromises.push(doPokemonTransformationSequence(nextPokemon1, nextPokemon2, screenPosition));
|
||||
}
|
||||
});
|
||||
allTransformationPromises.push(transformationPromise);
|
||||
@ -682,11 +688,10 @@ function doSideBySideTransformations(scene: BattleScene, transformations: Pokemo
|
||||
|
||||
/**
|
||||
* Returns index of the new egg move within the Pokemon's moveset (not the index of the move in `speciesEggMoves`)
|
||||
* @param scene
|
||||
* @param newPokemon
|
||||
* @param speciesRootForm
|
||||
*/
|
||||
async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: PlayerPokemon, speciesRootForm: Species, forBattle: boolean = false): Promise<number | null> {
|
||||
async function addEggMoveToNewPokemonMoveset(newPokemon: PlayerPokemon, speciesRootForm: Species, forBattle: boolean = false): Promise<number | null> {
|
||||
let eggMoveIndex: null | number = null;
|
||||
const eggMoves = newPokemon.getEggMoves()?.slice(0);
|
||||
if (eggMoves) {
|
||||
@ -712,8 +717,8 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla
|
||||
}
|
||||
|
||||
// For pokemon that the player owns (including ones just caught), unlock the egg move
|
||||
if (!forBattle && !isNullOrUndefined(randomEggMoveIndex) && !!scene.gameData.dexData[speciesRootForm].caughtAttr) {
|
||||
await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
|
||||
if (!forBattle && !isNullOrUndefined(randomEggMoveIndex) && !!globalScene.gameData.dexData[speciesRootForm].caughtAttr) {
|
||||
await globalScene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { TextStyle } from "#app/ui/text";
|
||||
import type { TextStyle } from "#app/ui/text";
|
||||
|
||||
export class TextDisplay {
|
||||
speaker?: string;
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
import { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { Type } from "#enums/type";
|
||||
import type { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
||||
import type { Moves } from "#app/enums/moves";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { Type } from "#enums/type";
|
||||
import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
|
||||
import type { CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
|
||||
import { CanLearnMoveRequirement } from "./requirements/can-learn-move-requirement";
|
||||
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
|
||||
|
||||
export type OptionPhaseCallback = (scene: BattleScene) => Promise<void | boolean>;
|
||||
export type OptionPhaseCallback = () => Promise<void | boolean>;
|
||||
|
||||
/**
|
||||
* Used by {@linkcode MysteryEncounterOptionBuilder} class to define required/optional properties on the {@linkcode MysteryEncounterOption} class when building.
|
||||
@ -74,21 +76,19 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
|
||||
|
||||
/**
|
||||
* Returns true if all {@linkcode EncounterRequirement}s for the option are met
|
||||
* @param scene
|
||||
*/
|
||||
meetsRequirements(scene: BattleScene): boolean {
|
||||
return !this.requirements.some(requirement => !requirement.meetsRequirement(scene))
|
||||
&& this.meetsSupportingRequirementAndSupportingPokemonSelected(scene)
|
||||
&& this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
|
||||
meetsRequirements(): boolean {
|
||||
return !this.requirements.some(requirement => !requirement.meetsRequirement())
|
||||
&& this.meetsSupportingRequirementAndSupportingPokemonSelected()
|
||||
&& this.meetsPrimaryRequirementAndPrimaryPokemonSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all PRIMARY {@linkcode EncounterRequirement}s for the option are met
|
||||
* @param scene
|
||||
* @param pokemon
|
||||
*/
|
||||
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean {
|
||||
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getPlayerParty()).map(p => p.id).includes(pokemon.id));
|
||||
pokemonMeetsPrimaryRequirements(pokemon: Pokemon): boolean {
|
||||
return !this.primaryPokemonRequirements.some(req => !req.queryParty(globalScene.getPlayerParty()).map(p => p.id).includes(pokemon.id));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,16 +96,15 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
|
||||
* AND there is a valid Pokemon assigned to {@linkcode primaryPokemon}.
|
||||
* If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined,
|
||||
* can cause scenarios where there are not enough Pokemon that are sufficient for all requirements.
|
||||
* @param scene
|
||||
*/
|
||||
meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene): boolean {
|
||||
meetsPrimaryRequirementAndPrimaryPokemonSelected(): boolean {
|
||||
if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) {
|
||||
return true;
|
||||
}
|
||||
let qualified: PlayerPokemon[] = scene.getPlayerParty();
|
||||
let qualified: PlayerPokemon[] = globalScene.getPlayerParty();
|
||||
for (const req of this.primaryPokemonRequirements) {
|
||||
if (req.meetsRequirement(scene)) {
|
||||
const queryParty = req.queryParty(scene.getPlayerParty());
|
||||
if (req.meetsRequirement()) {
|
||||
const queryParty = req.queryParty(globalScene.getPlayerParty());
|
||||
qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
|
||||
} else {
|
||||
this.primaryPokemon = undefined;
|
||||
@ -154,18 +153,17 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
|
||||
* AND there is a valid Pokemon assigned to {@linkcode secondaryPokemon} (if applicable).
|
||||
* If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined,
|
||||
* can cause scenarios where there are not enough Pokemon that are sufficient for all requirements.
|
||||
* @param scene
|
||||
*/
|
||||
meetsSupportingRequirementAndSupportingPokemonSelected(scene: BattleScene): boolean {
|
||||
meetsSupportingRequirementAndSupportingPokemonSelected(): boolean {
|
||||
if (!this.secondaryPokemonRequirements || this.secondaryPokemonRequirements.length === 0) {
|
||||
this.secondaryPokemon = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
let qualified: PlayerPokemon[] = scene.getPlayerParty();
|
||||
let qualified: PlayerPokemon[] = globalScene.getPlayerParty();
|
||||
for (const req of this.secondaryPokemonRequirements) {
|
||||
if (req.meetsRequirement(scene)) {
|
||||
const queryParty = req.queryParty(scene.getPlayerParty());
|
||||
if (req.meetsRequirement()) {
|
||||
const queryParty = req.queryParty(globalScene.getPlayerParty());
|
||||
qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
|
||||
} else {
|
||||
this.secondaryPokemon = [];
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { allAbilities } from "#app/data/ability";
|
||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { Nature } from "#enums/nature";
|
||||
@ -6,35 +6,33 @@ import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { Type } from "#enums/type";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
||||
import { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||
import { isNullOrUndefined } from "#app/utils";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import type { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
|
||||
export interface EncounterRequirement {
|
||||
meetsRequirement(scene: BattleScene): boolean; // Boolean to see if a requirement is met
|
||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string];
|
||||
meetsRequirement(): boolean; // Boolean to see if a requirement is met
|
||||
getDialogueToken(pokemon?: PlayerPokemon): [string, string];
|
||||
}
|
||||
|
||||
export abstract class EncounterSceneRequirement implements EncounterRequirement {
|
||||
/**
|
||||
* Returns whether the EncounterSceneRequirement's... requirements, are met by the given scene
|
||||
* @param partyPokemon
|
||||
*/
|
||||
abstract meetsRequirement(scene: BattleScene): boolean;
|
||||
abstract meetsRequirement(): boolean;
|
||||
/**
|
||||
* Returns a dialogue token key/value pair for a given Requirement.
|
||||
* Should be overridden by child Requirement classes.
|
||||
* @param scene
|
||||
* @param pokemon
|
||||
*/
|
||||
abstract getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string];
|
||||
abstract getDialogueToken(pokemon?: PlayerPokemon): [string, string];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,33 +59,31 @@ export class CombinationSceneRequirement extends EncounterSceneRequirement {
|
||||
|
||||
/**
|
||||
* Checks if all/any requirements are met (depends on {@linkcode isAnd})
|
||||
* @param scene The {@linkcode BattleScene} to check against
|
||||
* @returns true if all/any requirements are met (depends on {@linkcode isAnd})
|
||||
*/
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
override meetsRequirement(): boolean {
|
||||
return this.isAnd
|
||||
? this.requirements.every(req => req.meetsRequirement(scene))
|
||||
: this.requirements.some(req => req.meetsRequirement(scene));
|
||||
? this.requirements.every(req => req.meetsRequirement())
|
||||
: this.requirements.some(req => req.meetsRequirement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a dialogue token key/value pair for the given {@linkcode EncounterSceneRequirement | requirements}.
|
||||
* @param scene The {@linkcode BattleScene} to check against
|
||||
* @param pokemon The {@linkcode PlayerPokemon} to check against
|
||||
* @returns A dialogue token key/value pair
|
||||
* @throws An {@linkcode Error} if {@linkcode isAnd} is `true` (not supported)
|
||||
*/
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
if (this.isAnd) {
|
||||
throw new Error("Not implemented (Sorry)");
|
||||
} else {
|
||||
for (const req of this.requirements) {
|
||||
if (req.meetsRequirement(scene)) {
|
||||
return req.getDialogueToken(scene, pokemon);
|
||||
if (req.meetsRequirement()) {
|
||||
return req.getDialogueToken(pokemon);
|
||||
}
|
||||
}
|
||||
|
||||
return this.requirements[0].getDialogueToken(scene, pokemon);
|
||||
return this.requirements[0].getDialogueToken(pokemon);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,9 +94,8 @@ export abstract class EncounterPokemonRequirement implements EncounterRequiremen
|
||||
|
||||
/**
|
||||
* Returns whether the EncounterPokemonRequirement's... requirements, are met by the given scene
|
||||
* @param partyPokemon
|
||||
*/
|
||||
abstract meetsRequirement(scene: BattleScene): boolean;
|
||||
abstract meetsRequirement(): boolean;
|
||||
|
||||
/**
|
||||
* Returns all party members that are compatible with this requirement. For non pokemon related requirements, the entire party is returned.
|
||||
@ -111,10 +106,9 @@ export abstract class EncounterPokemonRequirement implements EncounterRequiremen
|
||||
/**
|
||||
* Returns a dialogue token key/value pair for a given Requirement.
|
||||
* Should be overridden by child Requirement classes.
|
||||
* @param scene
|
||||
* @param pokemon
|
||||
*/
|
||||
abstract getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string];
|
||||
abstract getDialogueToken(pokemon?: PlayerPokemon): [string, string];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,13 +137,12 @@ export class CombinationPokemonRequirement extends EncounterPokemonRequirement {
|
||||
|
||||
/**
|
||||
* Checks if all/any requirements are met (depends on {@linkcode isAnd})
|
||||
* @param scene The {@linkcode BattleScene} to check against
|
||||
* @returns true if all/any requirements are met (depends on {@linkcode isAnd})
|
||||
*/
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
override meetsRequirement(): boolean {
|
||||
return this.isAnd
|
||||
? this.requirements.every(req => req.meetsRequirement(scene))
|
||||
: this.requirements.some(req => req.meetsRequirement(scene));
|
||||
? this.requirements.every(req => req.meetsRequirement())
|
||||
: this.requirements.some(req => req.meetsRequirement());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,22 +161,21 @@ export class CombinationPokemonRequirement extends EncounterPokemonRequirement {
|
||||
|
||||
/**
|
||||
* Retrieves a dialogue token key/value pair for the given {@linkcode EncounterPokemonRequirement | requirements}.
|
||||
* @param scene The {@linkcode BattleScene} to check against
|
||||
* @param pokemon The {@linkcode PlayerPokemon} to check against
|
||||
* @returns A dialogue token key/value pair
|
||||
* @throws An {@linkcode Error} if {@linkcode isAnd} is `true` (not supported)
|
||||
*/
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
if (this.isAnd) {
|
||||
throw new Error("Not implemented (Sorry)");
|
||||
} else {
|
||||
for (const req of this.requirements) {
|
||||
if (req.meetsRequirement(scene)) {
|
||||
return req.getDialogueToken(scene, pokemon);
|
||||
if (req.meetsRequirement()) {
|
||||
return req.getDialogueToken(pokemon);
|
||||
}
|
||||
}
|
||||
|
||||
return this.requirements[0].getDialogueToken(scene, pokemon);
|
||||
return this.requirements[0].getDialogueToken(pokemon);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,12 +192,12 @@ export class PreviousEncounterRequirement extends EncounterSceneRequirement {
|
||||
this.previousEncounterRequirement = previousEncounterRequirement;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
return scene.mysteryEncounterSaveData.encounteredEvents.some(e => e.type === this.previousEncounterRequirement);
|
||||
override meetsRequirement(): boolean {
|
||||
return globalScene.mysteryEncounterSaveData.encounteredEvents.some(e => e.type === this.previousEncounterRequirement);
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "previousEncounter", scene.mysteryEncounterSaveData.encounteredEvents.find(e => e.type === this.previousEncounterRequirement)?.[0].toString() ?? "" ];
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "previousEncounter", globalScene.mysteryEncounterSaveData.encounteredEvents.find(e => e.type === this.previousEncounterRequirement)?.[0].toString() ?? "" ];
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,9 +214,9 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
|
||||
this.waveRange = waveRange;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
override meetsRequirement(): boolean {
|
||||
if (!isNullOrUndefined(this.waveRange) && this.waveRange[0] <= this.waveRange[1]) {
|
||||
const waveIndex = scene.currentBattle.waveIndex;
|
||||
const waveIndex = globalScene.currentBattle.waveIndex;
|
||||
if (waveIndex >= 0 && (this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) || (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)) {
|
||||
return false;
|
||||
}
|
||||
@ -232,8 +224,8 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "waveIndex", scene.currentBattle.waveIndex.toString() ];
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "waveIndex", globalScene.currentBattle.waveIndex.toString() ];
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,12 +249,12 @@ export class WaveModulusRequirement extends EncounterSceneRequirement {
|
||||
this.modulusValue = modulusValue;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
return this.waveModuli.includes(scene.currentBattle.waveIndex % this.modulusValue);
|
||||
override meetsRequirement(): boolean {
|
||||
return this.waveModuli.includes(globalScene.currentBattle.waveIndex % this.modulusValue);
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "waveIndex", scene.currentBattle.waveIndex.toString() ];
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "waveIndex", globalScene.currentBattle.waveIndex.toString() ];
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,8 +266,8 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
||||
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [ timeOfDay ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const timeOfDay = scene.arena?.getTimeOfDay();
|
||||
override meetsRequirement(): boolean {
|
||||
const timeOfDay = globalScene.arena?.getTimeOfDay();
|
||||
if (!isNullOrUndefined(timeOfDay) && this.requiredTimeOfDay?.length > 0 && !this.requiredTimeOfDay.includes(timeOfDay)) {
|
||||
return false;
|
||||
}
|
||||
@ -283,8 +275,8 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "timeOfDay", TimeOfDay[scene.arena.getTimeOfDay()].toLocaleLowerCase() ];
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "timeOfDay", TimeOfDay[globalScene.arena.getTimeOfDay()].toLocaleLowerCase() ];
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,8 +288,8 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
||||
this.requiredWeather = Array.isArray(weather) ? weather : [ weather ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const currentWeather = scene.arena.weather?.weatherType;
|
||||
override meetsRequirement(): boolean {
|
||||
const currentWeather = globalScene.arena.weather?.weatherType;
|
||||
if (!isNullOrUndefined(currentWeather) && this.requiredWeather?.length > 0 && !this.requiredWeather.includes(currentWeather!)) {
|
||||
return false;
|
||||
}
|
||||
@ -305,8 +297,8 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
const currentWeather = scene.arena.weather?.weatherType;
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const currentWeather = globalScene.arena.weather?.weatherType;
|
||||
let token = "";
|
||||
if (!isNullOrUndefined(currentWeather)) {
|
||||
token = WeatherType[currentWeather].replace("_", " ").toLocaleLowerCase();
|
||||
@ -331,9 +323,9 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
|
||||
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
override meetsRequirement(): boolean {
|
||||
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange[0] <= this.partySizeRange[1]) {
|
||||
const partySize = this.excludeDisallowedPokemon ? scene.getPokemonAllowedInBattle().length : scene.getPlayerParty().length;
|
||||
const partySize = this.excludeDisallowedPokemon ? globalScene.getPokemonAllowedInBattle().length : globalScene.getPlayerParty().length;
|
||||
if (partySize >= 0 && (this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) || (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)) {
|
||||
return false;
|
||||
}
|
||||
@ -342,8 +334,8 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
|
||||
return true;
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "partySize", scene.getPlayerParty().length.toString() ];
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "partySize", globalScene.getPlayerParty().length.toString() ];
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,14 +349,14 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
|
||||
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [ heldItem ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredHeldItemModifiers?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
let modifierCount = 0;
|
||||
this.requiredHeldItemModifiers.forEach(modifier => {
|
||||
const matchingMods = scene.findModifiers(m => m.constructor.name === modifier);
|
||||
const matchingMods = globalScene.findModifiers(m => m.constructor.name === modifier);
|
||||
if (matchingMods?.length > 0) {
|
||||
matchingMods.forEach(matchingMod => {
|
||||
modifierCount += matchingMod.stackCount;
|
||||
@ -375,7 +367,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
|
||||
return modifierCount >= this.minNumberOfItems;
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "requiredItem", this.requiredHeldItemModifiers[0] ];
|
||||
}
|
||||
}
|
||||
@ -390,20 +382,20 @@ export class MoneyRequirement extends EncounterSceneRequirement {
|
||||
this.scalingMultiplier = scalingMultiplier ?? 0;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const money = scene.money;
|
||||
override meetsRequirement(): boolean {
|
||||
const money = globalScene.money;
|
||||
if (isNullOrUndefined(money)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.scalingMultiplier > 0) {
|
||||
this.requiredMoney = scene.getWaveMoneyAmount(this.scalingMultiplier);
|
||||
this.requiredMoney = globalScene.getWaveMoneyAmount(this.scalingMultiplier);
|
||||
}
|
||||
return !(this.requiredMoney > 0 && this.requiredMoney > money);
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
const value = this.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString();
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const value = this.scalingMultiplier > 0 ? globalScene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString();
|
||||
return [ "money", value ];
|
||||
}
|
||||
}
|
||||
@ -420,8 +412,8 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredSpecies = Array.isArray(species) ? species : [ species ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredSpecies?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
@ -437,7 +429,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
if (pokemon?.species.speciesId && this.requiredSpecies.includes(pokemon.species.speciesId)) {
|
||||
return [ "species", Species[pokemon.species.speciesId] ];
|
||||
}
|
||||
@ -458,8 +450,8 @@ export class NatureRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredNature = Array.isArray(nature) ? nature : [ nature ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredNature?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
@ -475,7 +467,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon.nature)) {
|
||||
return [ "nature", Nature[pokemon.nature] ];
|
||||
}
|
||||
@ -497,8 +489,8 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredType = Array.isArray(type) ? type : [ type ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
let partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
let partyPokemon = globalScene.getPlayerParty();
|
||||
|
||||
if (isNullOrUndefined(partyPokemon)) {
|
||||
return false;
|
||||
@ -520,7 +512,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const includedTypes = this.requiredType.filter((ty) => pokemon?.getTypes().includes(ty));
|
||||
if (includedTypes.length > 0) {
|
||||
return [ "type", Type[includedTypes[0]] ];
|
||||
@ -544,8 +536,8 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredMoves = Array.isArray(moves) ? moves : [ moves ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
@ -566,7 +558,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const includedMoves = pokemon?.moveset.filter((move) => move?.moveId && this.requiredMoves.includes(move.moveId));
|
||||
if (includedMoves && includedMoves.length > 0 && includedMoves[0]) {
|
||||
return [ "move", includedMoves[0].getName() ];
|
||||
@ -593,8 +585,8 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [ learnableMove ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
@ -610,7 +602,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const includedCompatMoves = this.requiredMoves.filter((reqMove) => pokemon?.compatibleTms.filter((tm) => !pokemon.moveset.find(m => m?.moveId === tm)).includes(reqMove));
|
||||
if (includedCompatMoves.length > 0) {
|
||||
return [ "compatibleMove", Moves[includedCompatMoves[0]] ];
|
||||
@ -634,8 +626,8 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredAbilities = Array.isArray(abilities) ? abilities : [ abilities ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredAbilities?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
@ -655,7 +647,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(_scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const matchingAbility = this.requiredAbilities.find(a => pokemon?.hasAbility(a, false));
|
||||
if (!isNullOrUndefined(matchingAbility)) {
|
||||
return [ "ability", allAbilities[matchingAbility].name ];
|
||||
@ -676,8 +668,8 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [ statusEffect ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredStatusEffect?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
@ -713,7 +705,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const reqStatus = this.requiredStatusEffect.filter((a) => {
|
||||
if (a === StatusEffect.NONE) {
|
||||
return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === a;
|
||||
@ -745,8 +737,8 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
||||
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [ formChangeItem ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredFormChangeItem?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
@ -775,7 +767,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const requiredItems = this.requiredFormChangeItem.filter((formChangeItem) => this.filterByForm(pokemon, formChangeItem));
|
||||
if (requiredItems.length > 0) {
|
||||
return [ "formChangeItem", FormChangeItem[requiredItems[0]] ];
|
||||
@ -797,8 +789,8 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [ evolutionItems ];
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionItem?.length < 0) {
|
||||
return false;
|
||||
}
|
||||
@ -825,7 +817,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const requiredItems = this.requiredEvolutionItem.filter((evoItem) => this.filterByEvo(pokemon, evoItem));
|
||||
if (requiredItems.length > 0) {
|
||||
return [ "evolutionItem", EvolutionItem[requiredItems[0]] ];
|
||||
@ -848,8 +840,8 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
||||
this.requireTransferable = requireTransferable;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon)) {
|
||||
return false;
|
||||
}
|
||||
@ -873,7 +865,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const requiredItems = pokemon?.getHeldItems().filter((it) => {
|
||||
return this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem)
|
||||
&& (!this.requireTransferable || it.isTransferable);
|
||||
@ -899,8 +891,8 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
||||
this.requireTransferable = requireTransferable;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
override meetsRequirement(): boolean {
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
if (isNullOrUndefined(partyPokemon)) {
|
||||
return false;
|
||||
}
|
||||
@ -928,7 +920,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const requiredItems = pokemon?.getHeldItems().filter((it) => {
|
||||
return this.requiredHeldItemTypes.some(heldItemType =>
|
||||
it instanceof AttackTypeBoosterModifier
|
||||
@ -954,10 +946,10 @@ export class LevelRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredLevelRange = requiredLevelRange;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
override meetsRequirement(): boolean {
|
||||
// Party Pokemon inside required level range
|
||||
if (!isNullOrUndefined(this.requiredLevelRange) && this.requiredLevelRange[0] <= this.requiredLevelRange[1]) {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
const pokemonInRange = this.queryParty(partyPokemon);
|
||||
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
||||
return false;
|
||||
@ -975,7 +967,7 @@ export class LevelRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "level", pokemon?.level.toString() ?? "" ];
|
||||
}
|
||||
}
|
||||
@ -992,10 +984,10 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredFriendshipRange = requiredFriendshipRange;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
override meetsRequirement(): boolean {
|
||||
// Party Pokemon inside required friendship range
|
||||
if (!isNullOrUndefined(this.requiredFriendshipRange) && this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]) {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
const pokemonInRange = this.queryParty(partyPokemon);
|
||||
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
||||
return false;
|
||||
@ -1013,7 +1005,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "friendship", pokemon?.friendship.toString() ?? "" ];
|
||||
}
|
||||
}
|
||||
@ -1035,10 +1027,10 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredHealthRange = requiredHealthRange;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
override meetsRequirement(): boolean {
|
||||
// Party Pokemon's health inside required health range
|
||||
if (!isNullOrUndefined(this.requiredHealthRange) && this.requiredHealthRange[0] <= this.requiredHealthRange[1]) {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
const pokemonInRange = this.queryParty(partyPokemon);
|
||||
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
||||
return false;
|
||||
@ -1058,7 +1050,7 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
const hpRatio = pokemon?.getHpRatio();
|
||||
if (!isNullOrUndefined(hpRatio)) {
|
||||
return [ "healthRatio", Math.floor(hpRatio * 100).toString() + "%" ];
|
||||
@ -1079,10 +1071,10 @@ export class WeightRequirement extends EncounterPokemonRequirement {
|
||||
this.requiredWeightRange = requiredWeightRange;
|
||||
}
|
||||
|
||||
override meetsRequirement(scene: BattleScene): boolean {
|
||||
override meetsRequirement(): boolean {
|
||||
// Party Pokemon's weight inside required weight range
|
||||
if (!isNullOrUndefined(this.requiredWeightRange) && this.requiredWeightRange[0] <= this.requiredWeightRange[1]) {
|
||||
const partyPokemon = scene.getPlayerParty();
|
||||
const partyPokemon = globalScene.getPlayerParty();
|
||||
const pokemonInRange = this.queryParty(partyPokemon);
|
||||
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
||||
return false;
|
||||
@ -1100,7 +1092,7 @@ export class WeightRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
}
|
||||
|
||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
return [ "weight", pokemon?.getWeight().toString() ?? "" ];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import { isNullOrUndefined } from "#app/utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
|
||||
export class SeenEncounterData {
|
||||
type: MysteryEncounterType;
|
||||
|
||||
@ -1,21 +1,26 @@
|
||||
import { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import MysteryEncounterIntroVisuals, { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
||||
import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
|
||||
import * as Utils from "#app/utils";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import MysteryEncounterDialogue, { OptionTextDisplay } from "./mystery-encounter-dialogue";
|
||||
import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option";
|
||||
import type { StatusEffect } from "#enums/status-effect";
|
||||
import type { OptionTextDisplay } from "./mystery-encounter-dialogue";
|
||||
import type MysteryEncounterDialogue from "./mystery-encounter-dialogue";
|
||||
import type { OptionPhaseCallback } from "./mystery-encounter-option";
|
||||
import type MysteryEncounterOption from "./mystery-encounter-option";
|
||||
import { MysteryEncounterOptionBuilder } from "./mystery-encounter-option";
|
||||
import { EncounterPokemonRequirement, EncounterSceneRequirement, HealthRatioRequirement, PartySizeRequirement, StatusEffectRequirement, WaveRangeRequirement } from "./mystery-encounter-requirements";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { GameModes } from "#app/game-mode";
|
||||
import { EncounterAnim } from "#enums/encounter-anims";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import type { GameModes } from "#app/game-mode";
|
||||
import type { EncounterAnim } from "#enums/encounter-anims";
|
||||
import type { Challenges } from "#enums/challenges";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
export interface EncounterStartOfBattleEffect {
|
||||
sourcePokemon?: Pokemon;
|
||||
@ -55,11 +60,11 @@ export interface IMysteryEncounter {
|
||||
skipToFightInput: boolean;
|
||||
preventGameStatsUpdates: boolean;
|
||||
|
||||
onInit?: (scene: BattleScene) => boolean;
|
||||
onVisualsStart?: (scene: BattleScene) => boolean;
|
||||
doEncounterExp?: (scene: BattleScene) => boolean;
|
||||
doEncounterRewards?: (scene: BattleScene) => boolean;
|
||||
doContinueEncounter?: (scene: BattleScene) => Promise<void>;
|
||||
onInit?: () => boolean;
|
||||
onVisualsStart?: () => boolean;
|
||||
doEncounterExp?: () => boolean;
|
||||
doEncounterRewards?: () => boolean;
|
||||
doContinueEncounter?: () => Promise<void>;
|
||||
|
||||
requirements: EncounterSceneRequirement[];
|
||||
primaryPokemonRequirements: EncounterPokemonRequirement[];
|
||||
@ -159,24 +164,24 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
// #region Event callback functions
|
||||
|
||||
/** Event when Encounter is first loaded, use it for data conditioning */
|
||||
onInit?: (scene: BattleScene) => boolean;
|
||||
onInit?: () => boolean;
|
||||
/** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */
|
||||
onVisualsStart?: (scene: BattleScene) => boolean;
|
||||
onVisualsStart?: () => boolean;
|
||||
/** Event triggered prior to {@linkcode CommandPhase}, during {@linkcode TurnInitPhase} */
|
||||
onTurnStart?: (scene: BattleScene) => boolean;
|
||||
onTurnStart?: () => boolean;
|
||||
/** Event prior to any rewards logic in {@linkcode MysteryEncounterRewardsPhase} */
|
||||
onRewards?: (scene: BattleScene) => Promise<void>;
|
||||
onRewards?: () => Promise<void>;
|
||||
/** Will provide the player party EXP before rewards are displayed for that wave */
|
||||
doEncounterExp?: (scene: BattleScene) => boolean;
|
||||
doEncounterExp?: () => boolean;
|
||||
/** Will provide the player a rewards shop for that wave */
|
||||
doEncounterRewards?: (scene: BattleScene) => boolean;
|
||||
doEncounterRewards?: () => boolean;
|
||||
/** Will execute callback during VictoryPhase of a continuousEncounter */
|
||||
doContinueEncounter?: (scene: BattleScene) => Promise<void>;
|
||||
doContinueEncounter?: () => Promise<void>;
|
||||
/**
|
||||
* Can perform special logic when a ME battle is lost, before GameOver/battle retry prompt.
|
||||
* Should return `true` if it is treated as "real" Game Over, `false` if not.
|
||||
*/
|
||||
onGameOver?: (scene: BattleScene) => boolean;
|
||||
onGameOver?: () => boolean;
|
||||
|
||||
/**
|
||||
* Requirements
|
||||
@ -296,13 +301,12 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
/**
|
||||
* Checks if the current scene state meets the requirements for the {@linkcode MysteryEncounter} to spawn
|
||||
* This is used to filter the pool of encounters down to only the ones with all requirements met
|
||||
* @param scene
|
||||
* @returns
|
||||
*/
|
||||
meetsRequirements(scene: BattleScene): boolean {
|
||||
const sceneReq = !this.requirements.some(requirement => !requirement.meetsRequirement(scene));
|
||||
const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(scene); // secondary is checked first to handle cases of primary overlapping with secondary
|
||||
const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
|
||||
meetsRequirements(): boolean {
|
||||
const sceneReq = !this.requirements.some(requirement => !requirement.meetsRequirement());
|
||||
const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(); // secondary is checked first to handle cases of primary overlapping with secondary
|
||||
const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected();
|
||||
|
||||
return sceneReq && secReqs && priReqs;
|
||||
}
|
||||
@ -310,11 +314,10 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
/**
|
||||
* Checks if a specific player pokemon meets all given primary EncounterPokemonRequirements
|
||||
* Used automatically as part of {@linkcode meetsRequirements}, but can also be used to manually check certain Pokemon where needed
|
||||
* @param scene
|
||||
* @param pokemon
|
||||
*/
|
||||
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean {
|
||||
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getPlayerParty()).map(p => p.id).includes(pokemon.id));
|
||||
pokemonMeetsPrimaryRequirements(pokemon: Pokemon): boolean {
|
||||
return !this.primaryPokemonRequirements.some(req => !req.queryParty(globalScene.getPlayerParty()).map(p => p.id).includes(pokemon.id));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,22 +325,21 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
* AND there is a valid Pokemon assigned to {@linkcode primaryPokemon}.
|
||||
* If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined,
|
||||
* can cause scenarios where there are not enough Pokemon that are sufficient for all requirements.
|
||||
* @param scene
|
||||
*/
|
||||
private meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene): boolean {
|
||||
private meetsPrimaryRequirementAndPrimaryPokemonSelected(): boolean {
|
||||
if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) {
|
||||
const activeMon = scene.getPlayerParty().filter(p => p.isActive(true));
|
||||
const activeMon = globalScene.getPlayerParty().filter(p => p.isActive(true));
|
||||
if (activeMon.length > 0) {
|
||||
this.primaryPokemon = activeMon[0];
|
||||
} else {
|
||||
this.primaryPokemon = scene.getPlayerParty().filter(p => p.isAllowedInBattle())[0];
|
||||
this.primaryPokemon = globalScene.getPlayerParty().filter(p => p.isAllowedInBattle())[0];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
let qualified: PlayerPokemon[] = scene.getPlayerParty();
|
||||
let qualified: PlayerPokemon[] = globalScene.getPlayerParty();
|
||||
for (const req of this.primaryPokemonRequirements) {
|
||||
if (req.meetsRequirement(scene)) {
|
||||
qualified = qualified.filter(pkmn => req.queryParty(scene.getPlayerParty()).includes(pkmn));
|
||||
if (req.meetsRequirement()) {
|
||||
qualified = qualified.filter(pkmn => req.queryParty(globalScene.getPlayerParty()).includes(pkmn));
|
||||
} else {
|
||||
this.primaryPokemon = undefined;
|
||||
return false;
|
||||
@ -386,18 +388,17 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
* AND there is a valid Pokemon assigned to {@linkcode secondaryPokemon} (if applicable).
|
||||
* If both {@linkcode primaryPokemonRequirements} and {@linkcode secondaryPokemonRequirements} are defined,
|
||||
* can cause scenarios where there are not enough Pokemon that are sufficient for all requirements.
|
||||
* @param scene
|
||||
*/
|
||||
private meetsSecondaryRequirementAndSecondaryPokemonSelected(scene: BattleScene): boolean {
|
||||
private meetsSecondaryRequirementAndSecondaryPokemonSelected(): boolean {
|
||||
if (!this.secondaryPokemonRequirements || this.secondaryPokemonRequirements.length === 0) {
|
||||
this.secondaryPokemon = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
let qualified: PlayerPokemon[] = scene.getPlayerParty();
|
||||
let qualified: PlayerPokemon[] = globalScene.getPlayerParty();
|
||||
for (const req of this.secondaryPokemonRequirements) {
|
||||
if (req.meetsRequirement(scene)) {
|
||||
qualified = qualified.filter(pkmn => req.queryParty(scene.getPlayerParty()).includes(pkmn));
|
||||
if (req.meetsRequirement()) {
|
||||
qualified = qualified.filter(pkmn => req.queryParty(globalScene.getPlayerParty()).includes(pkmn));
|
||||
} else {
|
||||
this.secondaryPokemon = [];
|
||||
return false;
|
||||
@ -409,10 +410,9 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
|
||||
/**
|
||||
* Initializes encounter intro sprites based on the sprite configs defined in spriteConfigs
|
||||
* @param scene
|
||||
*/
|
||||
initIntroVisuals(scene: BattleScene): void {
|
||||
this.introVisuals = new MysteryEncounterIntroVisuals(scene, this);
|
||||
initIntroVisuals(): void {
|
||||
this.introVisuals = new MysteryEncounterIntroVisuals(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -420,11 +420,11 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
* Will use the first support pokemon in list
|
||||
* For multiple support pokemon in the dialogue token, it will have to be overridden.
|
||||
*/
|
||||
populateDialogueTokensFromRequirements(scene: BattleScene): void {
|
||||
this.meetsRequirements(scene);
|
||||
populateDialogueTokensFromRequirements(): void {
|
||||
this.meetsRequirements();
|
||||
if (this.requirements?.length > 0) {
|
||||
for (const req of this.requirements) {
|
||||
const dialogueToken = req.getDialogueToken(scene);
|
||||
const dialogueToken = req.getDialogueToken();
|
||||
if (dialogueToken?.length === 2) {
|
||||
this.setDialogueToken(...dialogueToken);
|
||||
}
|
||||
@ -434,7 +434,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
this.setDialogueToken("primaryName", this.primaryPokemon.getNameToRender());
|
||||
for (const req of this.primaryPokemonRequirements) {
|
||||
if (!req.invertQuery) {
|
||||
const value = req.getDialogueToken(scene, this.primaryPokemon);
|
||||
const value = req.getDialogueToken(this.primaryPokemon);
|
||||
if (value?.length === 2) {
|
||||
this.setDialogueToken("primary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||
}
|
||||
@ -445,7 +445,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
this.setDialogueToken("secondaryName", this.secondaryPokemon[0].getNameToRender());
|
||||
for (const req of this.secondaryPokemonRequirements) {
|
||||
if (!req.invertQuery) {
|
||||
const value = req.getDialogueToken(scene, this.secondaryPokemon[0]);
|
||||
const value = req.getDialogueToken(this.secondaryPokemon[0]);
|
||||
if (value?.length === 2) {
|
||||
this.setDialogueToken("primary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||
}
|
||||
@ -457,11 +457,11 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
// Dialogue tokens for options
|
||||
for (let i = 0; i < this.options.length; i++) {
|
||||
const opt = this.options[i];
|
||||
opt.meetsRequirements(scene);
|
||||
opt.meetsRequirements();
|
||||
const j = i + 1;
|
||||
if (opt.requirements.length > 0) {
|
||||
for (const req of opt.requirements) {
|
||||
const dialogueToken = req.getDialogueToken(scene);
|
||||
const dialogueToken = req.getDialogueToken();
|
||||
if (dialogueToken?.length === 2) {
|
||||
this.setDialogueToken("option" + j + capitalizeFirstLetter(dialogueToken[0]), dialogueToken[1]);
|
||||
}
|
||||
@ -471,7 +471,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
this.setDialogueToken("option" + j + "PrimaryName", opt.primaryPokemon.getNameToRender());
|
||||
for (const req of opt.primaryPokemonRequirements) {
|
||||
if (!req.invertQuery) {
|
||||
const value = req.getDialogueToken(scene, opt.primaryPokemon);
|
||||
const value = req.getDialogueToken(opt.primaryPokemon);
|
||||
if (value?.length === 2) {
|
||||
this.setDialogueToken("option" + j + "Primary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||
}
|
||||
@ -482,7 +482,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
this.setDialogueToken("option" + j + "SecondaryName", opt.secondaryPokemon[0].getNameToRender());
|
||||
for (const req of opt.secondaryPokemonRequirements) {
|
||||
if (!req.invertQuery) {
|
||||
const value = req.getDialogueToken(scene, opt.secondaryPokemon[0]);
|
||||
const value = req.getDialogueToken(opt.secondaryPokemon[0]);
|
||||
if (value?.length === 2) {
|
||||
this.setDialogueToken("option" + j + "Secondary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||
}
|
||||
@ -518,10 +518,9 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||
/**
|
||||
* Maintains seed offset for RNG consistency
|
||||
* Increments if the same {@linkcode MysteryEncounter} has multiple option select cycles
|
||||
* @param scene
|
||||
*/
|
||||
updateSeedOffset(scene: BattleScene) {
|
||||
const currentOffset = this.seedOffset ?? scene.currentBattle.waveIndex * 1000;
|
||||
updateSeedOffset() {
|
||||
const currentOffset = this.seedOffset ?? globalScene.currentBattle.waveIndex * 1000;
|
||||
this.seedOffset = currentOffset + 512;
|
||||
}
|
||||
}
|
||||
@ -858,7 +857,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @param doEncounterRewards Synchronous callback function to perform during rewards phase of the encounter
|
||||
* @returns
|
||||
*/
|
||||
withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
|
||||
withRewards(doEncounterRewards: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
|
||||
return Object.assign(this, { doEncounterRewards: doEncounterRewards });
|
||||
}
|
||||
|
||||
@ -872,7 +871,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @param doEncounterExp Synchronous callback function to perform during rewards phase of the encounter
|
||||
* @returns
|
||||
*/
|
||||
withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {
|
||||
withExp(doEncounterExp: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {
|
||||
return Object.assign(this, { doEncounterExp: doEncounterExp });
|
||||
}
|
||||
|
||||
@ -883,7 +882,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @param onInit Synchronous callback function to perform as soon as the encounter is selected for the next phase
|
||||
* @returns
|
||||
*/
|
||||
withOnInit(onInit: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onInit">> {
|
||||
withOnInit(onInit: () => boolean): this & Required<Pick<IMysteryEncounter, "onInit">> {
|
||||
return Object.assign(this, { onInit });
|
||||
}
|
||||
|
||||
@ -893,7 +892,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
* @param onVisualsStart Synchronous callback function to perform as soon as the enemy field finishes sliding in
|
||||
* @returns
|
||||
*/
|
||||
withOnVisualsStart(onVisualsStart: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> {
|
||||
withOnVisualsStart(onVisualsStart: () => boolean): this & Required<Pick<IMysteryEncounter, "onVisualsStart">> {
|
||||
return Object.assign(this, { onVisualsStart: onVisualsStart });
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounte
|
||||
import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter";
|
||||
import { SlumberingSnorlaxEncounter } from "./encounters/slumbering-snorlax-encounter";
|
||||
import { TrainingSessionEncounter } from "./encounters/training-session-encounter";
|
||||
import MysteryEncounter from "./mystery-encounter";
|
||||
import type MysteryEncounter from "./mystery-encounter";
|
||||
import { SafariZoneEncounter } from "#app/data/mystery-encounters/encounters/safari-zone-encounter";
|
||||
import { FieryFalloutEncounter } from "#app/data/mystery-encounters/encounters/fiery-fallout-encounter";
|
||||
import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter";
|
||||
|
||||