Merge remote-tracking branch 'upstream/beta' into phase-interceptor
@ -48,7 +48,7 @@
|
|||||||
"lefthook": "^1.12.2",
|
"lefthook": "^1.12.2",
|
||||||
"msw": "^2.10.4",
|
"msw": "^2.10.4",
|
||||||
"phaser3spectorjs": "^0.0.8",
|
"phaser3spectorjs": "^0.0.8",
|
||||||
"typedoc": "0.28.7",
|
"typedoc": "^0.28.13",
|
||||||
"typedoc-github-theme": "^0.3.1",
|
"typedoc-github-theme": "^0.3.1",
|
||||||
"typedoc-plugin-coverage": "^4.0.1",
|
"typedoc-plugin-coverage": "^4.0.1",
|
||||||
"typedoc-plugin-mdn-links": "^5.0.9",
|
"typedoc-plugin-mdn-links": "^5.0.9",
|
||||||
@ -74,5 +74,5 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.0.0"
|
"node": ">=22.0.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.16.1"
|
"packageManager": "pnpm@10.17.0"
|
||||||
}
|
}
|
||||||
|
@ -88,17 +88,17 @@ importers:
|
|||||||
specifier: ^0.0.8
|
specifier: ^0.0.8
|
||||||
version: 0.0.8
|
version: 0.0.8
|
||||||
typedoc:
|
typedoc:
|
||||||
specifier: 0.28.7
|
specifier: ^0.28.13
|
||||||
version: 0.28.7(typescript@5.9.2)
|
version: 0.28.13(typescript@5.9.2)
|
||||||
typedoc-github-theme:
|
typedoc-github-theme:
|
||||||
specifier: ^0.3.1
|
specifier: ^0.3.1
|
||||||
version: 0.3.1(typedoc@0.28.7(typescript@5.9.2))
|
version: 0.3.1(typedoc@0.28.13(typescript@5.9.2))
|
||||||
typedoc-plugin-coverage:
|
typedoc-plugin-coverage:
|
||||||
specifier: ^4.0.1
|
specifier: ^4.0.1
|
||||||
version: 4.0.1(typedoc@0.28.7(typescript@5.9.2))
|
version: 4.0.1(typedoc@0.28.13(typescript@5.9.2))
|
||||||
typedoc-plugin-mdn-links:
|
typedoc-plugin-mdn-links:
|
||||||
specifier: ^5.0.9
|
specifier: ^5.0.9
|
||||||
version: 5.0.9(typedoc@0.28.7(typescript@5.9.2))
|
version: 5.0.9(typedoc@0.28.13(typescript@5.9.2))
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.9.2
|
specifier: ^5.9.2
|
||||||
version: 5.9.2
|
version: 5.9.2
|
||||||
@ -717,17 +717,17 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@shikijs/engine-oniguruma@3.12.2':
|
'@shikijs/engine-oniguruma@3.13.0':
|
||||||
resolution: {integrity: sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==}
|
resolution: {integrity: sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==}
|
||||||
|
|
||||||
'@shikijs/langs@3.12.2':
|
'@shikijs/langs@3.13.0':
|
||||||
resolution: {integrity: sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==}
|
resolution: {integrity: sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==}
|
||||||
|
|
||||||
'@shikijs/themes@3.12.2':
|
'@shikijs/themes@3.13.0':
|
||||||
resolution: {integrity: sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==}
|
resolution: {integrity: sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==}
|
||||||
|
|
||||||
'@shikijs/types@3.12.2':
|
'@shikijs/types@3.13.0':
|
||||||
resolution: {integrity: sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==}
|
resolution: {integrity: sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==}
|
||||||
|
|
||||||
'@shikijs/vscode-textmate@10.0.2':
|
'@shikijs/vscode-textmate@10.0.2':
|
||||||
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
|
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
|
||||||
@ -1828,12 +1828,12 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
typedoc: 0.27.x || 0.28.x
|
typedoc: 0.27.x || 0.28.x
|
||||||
|
|
||||||
typedoc@0.28.7:
|
typedoc@0.28.13:
|
||||||
resolution: {integrity: sha512-lpz0Oxl6aidFkmS90VQDQjk/Qf2iw0IUvFqirdONBdj7jPSN9mGXhy66BcGNDxx5ZMyKKiBVAREvPEzT6Uxipw==}
|
resolution: {integrity: sha512-dNWY8msnYB2a+7Audha+aTF1Pu3euiE7ySp53w8kEsXoYw7dMouV5A1UsTUY345aB152RHnmRMDiovuBi7BD+w==}
|
||||||
engines: {node: '>= 18', pnpm: '>= 10'}
|
engines: {node: '>= 18', pnpm: '>= 10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x
|
typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x
|
||||||
|
|
||||||
typescript@5.9.2:
|
typescript@5.9.2:
|
||||||
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
|
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
|
||||||
@ -2312,10 +2312,10 @@ snapshots:
|
|||||||
|
|
||||||
'@gerrit0/mini-shiki@3.12.2':
|
'@gerrit0/mini-shiki@3.12.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/engine-oniguruma': 3.12.2
|
'@shikijs/engine-oniguruma': 3.13.0
|
||||||
'@shikijs/langs': 3.12.2
|
'@shikijs/langs': 3.13.0
|
||||||
'@shikijs/themes': 3.12.2
|
'@shikijs/themes': 3.13.0
|
||||||
'@shikijs/types': 3.12.2
|
'@shikijs/types': 3.13.0
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
|
||||||
'@inquirer/checkbox@4.2.0(@types/node@22.16.5)':
|
'@inquirer/checkbox@4.2.0(@types/node@22.16.5)':
|
||||||
@ -2547,20 +2547,20 @@ snapshots:
|
|||||||
'@rollup/rollup-win32-x64-msvc@4.50.1':
|
'@rollup/rollup-win32-x64-msvc@4.50.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@shikijs/engine-oniguruma@3.12.2':
|
'@shikijs/engine-oniguruma@3.13.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/types': 3.12.2
|
'@shikijs/types': 3.13.0
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
|
||||||
'@shikijs/langs@3.12.2':
|
'@shikijs/langs@3.13.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/types': 3.12.2
|
'@shikijs/types': 3.13.0
|
||||||
|
|
||||||
'@shikijs/themes@3.12.2':
|
'@shikijs/themes@3.13.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/types': 3.12.2
|
'@shikijs/types': 3.13.0
|
||||||
|
|
||||||
'@shikijs/types@3.12.2':
|
'@shikijs/types@3.13.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
@ -3682,19 +3682,19 @@ snapshots:
|
|||||||
|
|
||||||
type-fest@4.41.0: {}
|
type-fest@4.41.0: {}
|
||||||
|
|
||||||
typedoc-github-theme@0.3.1(typedoc@0.28.7(typescript@5.9.2)):
|
typedoc-github-theme@0.3.1(typedoc@0.28.13(typescript@5.9.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
typedoc: 0.28.7(typescript@5.9.2)
|
typedoc: 0.28.13(typescript@5.9.2)
|
||||||
|
|
||||||
typedoc-plugin-coverage@4.0.1(typedoc@0.28.7(typescript@5.9.2)):
|
typedoc-plugin-coverage@4.0.1(typedoc@0.28.13(typescript@5.9.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
typedoc: 0.28.7(typescript@5.9.2)
|
typedoc: 0.28.13(typescript@5.9.2)
|
||||||
|
|
||||||
typedoc-plugin-mdn-links@5.0.9(typedoc@0.28.7(typescript@5.9.2)):
|
typedoc-plugin-mdn-links@5.0.9(typedoc@0.28.13(typescript@5.9.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
typedoc: 0.28.7(typescript@5.9.2)
|
typedoc: 0.28.13(typescript@5.9.2)
|
||||||
|
|
||||||
typedoc@0.28.7(typescript@5.9.2):
|
typedoc@0.28.13(typescript@5.9.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@gerrit0/mini-shiki': 3.12.2
|
'@gerrit0/mini-shiki': 3.12.2
|
||||||
lunr: 2.3.9
|
lunr: 2.3.9
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 131 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 202 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 124 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 186 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 180 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 181 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 242 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 198 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 196 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 183 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 169 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 131 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 202 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 249 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 190 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 244 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 195 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 193 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 180 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 197 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 131 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 202 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 249 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 190 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 244 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 195 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 193 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 180 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 197 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 202 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 124 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 187 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 180 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 181 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 242 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 198 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 196 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 183 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 169 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 124 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 187 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 180 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 181 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 242 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 198 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 196 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 183 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 169 B |
@ -89,7 +89,8 @@ export type AbilityBattlerTagType =
|
|||||||
| BattlerTagType.QUARK_DRIVE
|
| BattlerTagType.QUARK_DRIVE
|
||||||
| BattlerTagType.UNBURDEN
|
| BattlerTagType.UNBURDEN
|
||||||
| BattlerTagType.SLOW_START
|
| BattlerTagType.SLOW_START
|
||||||
| BattlerTagType.TRUANT;
|
| BattlerTagType.TRUANT
|
||||||
|
| BattlerTagType.SUPREME_OVERLORD;
|
||||||
|
|
||||||
/** Subset of {@linkcode BattlerTagType}s that provide type boosts */
|
/** Subset of {@linkcode BattlerTagType}s that provide type boosts */
|
||||||
export type TypeBoostTagType = BattlerTagType.FIRE_BOOST | BattlerTagType.CHARGED;
|
export type TypeBoostTagType = BattlerTagType.FIRE_BOOST | BattlerTagType.CHARGED;
|
||||||
|
44
src/@types/damage-params.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import type { MoveCategory } from "#enums/move-category";
|
||||||
|
import type { Pokemon } from "#field/pokemon";
|
||||||
|
import type { Move } from "#types/move-types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of types for methods like {@linkcode Pokemon#getBaseDamage} and {@linkcode Pokemon#getAttackDamage}.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Base type for damage parameter methods, used for DRY */
|
||||||
|
export interface damageParams {
|
||||||
|
/** The attacking {@linkcode Pokemon} */
|
||||||
|
source: Pokemon;
|
||||||
|
/** The move used in the attack */
|
||||||
|
move: Move;
|
||||||
|
/** The move's {@linkcode MoveCategory} after variable-category effects are applied */
|
||||||
|
moveCategory: MoveCategory;
|
||||||
|
/** If `true`, ignores this Pokemon's defensive ability effects */
|
||||||
|
ignoreAbility?: boolean;
|
||||||
|
/** If `true`, ignores the attacking Pokemon's ability effects */
|
||||||
|
ignoreSourceAbility?: boolean;
|
||||||
|
/** If `true`, ignores the ally Pokemon's ability effects */
|
||||||
|
ignoreAllyAbility?: boolean;
|
||||||
|
/** If `true`, ignores the ability effects of the attacking pokemon's ally */
|
||||||
|
ignoreSourceAllyAbility?: boolean;
|
||||||
|
/** If `true`, calculates damage for a critical hit */
|
||||||
|
isCritical?: boolean;
|
||||||
|
/** If `true`, suppresses changes to game state during the calculation */
|
||||||
|
simulated?: boolean;
|
||||||
|
/** If defined, used in place of calculated effectiveness values */
|
||||||
|
effectiveness?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for the parameters of {@linkcode Pokemon#getBaseDamage | getBaseDamage}
|
||||||
|
* @interface
|
||||||
|
*/
|
||||||
|
export type getBaseDamageParams = Omit<damageParams, "effectiveness">;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for the parameters of {@linkcode Pokemon#getAttackDamage | getAttackDamage}
|
||||||
|
* @interface
|
||||||
|
*/
|
||||||
|
export type getAttackDamageParams = Omit<damageParams, "moveCategory">;
|
@ -1,26 +1,27 @@
|
|||||||
|
import type { Pokemon } from "#app/field/pokemon";
|
||||||
|
import type { Phase } from "#app/phase";
|
||||||
import type { PhaseConstructorMap } from "#app/phase-manager";
|
import type { PhaseConstructorMap } from "#app/phase-manager";
|
||||||
import type { ObjectValues } from "#types/type-helpers";
|
import type { ObjectValues } from "#types/type-helpers";
|
||||||
|
|
||||||
// Intentionally export the types of everything in phase-manager, as this file is meant to be
|
// Intentionally [re-]export the types of everything in phase-manager, as this file is meant to be
|
||||||
// the centralized place for type definitions for the phase system.
|
// the centralized place for type definitions for the phase system.
|
||||||
export type * from "#app/phase-manager";
|
export type * from "#app/phase-manager";
|
||||||
|
|
||||||
// This file includes helpful types for the phase system.
|
/** Map of phase names to constructors for said phase */
|
||||||
// It intentionally imports the phase constructor map from the phase manager (and re-exports it)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map of phase names to constructors for said phase
|
|
||||||
*/
|
|
||||||
export type PhaseMap = {
|
export type PhaseMap = {
|
||||||
[K in keyof PhaseConstructorMap]: InstanceType<PhaseConstructorMap[K]>;
|
[K in keyof PhaseConstructorMap]: InstanceType<PhaseConstructorMap[K]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/** Union type of all phase constructors. */
|
||||||
* Union type of all phase constructors.
|
|
||||||
*/
|
|
||||||
export type PhaseClass = ObjectValues<PhaseConstructorMap>;
|
export type PhaseClass = ObjectValues<PhaseConstructorMap>;
|
||||||
|
|
||||||
/**
|
/** Union type of all phase names as strings. */
|
||||||
* Union type of all phase names as strings.
|
|
||||||
*/
|
|
||||||
export type PhaseString = keyof PhaseMap;
|
export type PhaseString = keyof PhaseMap;
|
||||||
|
|
||||||
|
/** Type for predicate functions operating on a specific type of {@linkcode Phase}. */
|
||||||
|
export type PhaseConditionFunc<T extends PhaseString> = (phase: PhaseMap[T]) => boolean;
|
||||||
|
|
||||||
|
/** Interface type representing the assumption that all phases with pokemon associated are dynamic */
|
||||||
|
export interface DynamicPhase extends Phase {
|
||||||
|
getPokemon(): Pokemon;
|
||||||
|
}
|
||||||
|
@ -4,8 +4,10 @@ import type { BattleType } from "#enums/battle-type";
|
|||||||
import type { GameModes } from "#enums/game-modes";
|
import type { GameModes } from "#enums/game-modes";
|
||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import type { Nature } from "#enums/nature";
|
||||||
import type { PlayerGender } from "#enums/player-gender";
|
import type { PlayerGender } from "#enums/player-gender";
|
||||||
import type { PokemonType } from "#enums/pokemon-type";
|
import type { PokemonType } from "#enums/pokemon-type";
|
||||||
|
import type { SpeciesId } from "#enums/species-id";
|
||||||
import type { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data";
|
import type { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data";
|
||||||
import type { Variant } from "#sprites/variant";
|
import type { Variant } from "#sprites/variant";
|
||||||
import type { ArenaData } from "#system/arena-data";
|
import type { ArenaData } from "#system/arena-data";
|
||||||
@ -108,6 +110,22 @@ export interface DexAttrProps {
|
|||||||
formIndex: number;
|
formIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Starter {
|
||||||
|
speciesId: SpeciesId;
|
||||||
|
shiny: boolean;
|
||||||
|
variant: Variant;
|
||||||
|
formIndex: number;
|
||||||
|
female?: boolean;
|
||||||
|
abilityIndex: number;
|
||||||
|
passive: boolean;
|
||||||
|
nature: Nature;
|
||||||
|
moveset?: StarterMoveset;
|
||||||
|
pokerus: boolean;
|
||||||
|
nickname?: string;
|
||||||
|
teraType?: PokemonType;
|
||||||
|
ivs: number[];
|
||||||
|
}
|
||||||
|
|
||||||
export type RunHistoryData = Record<number, RunEntry>;
|
export type RunHistoryData = Record<number, RunEntry>;
|
||||||
|
|
||||||
export interface RunEntry {
|
export interface RunEntry {
|
||||||
|
@ -104,7 +104,6 @@ import {
|
|||||||
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
|
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data";
|
import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data";
|
||||||
import { allMysteryEncounters, mysteryEncountersByBiome } from "#mystery-encounters/mystery-encounters";
|
import { allMysteryEncounters, mysteryEncountersByBiome } from "#mystery-encounters/mystery-encounters";
|
||||||
import type { MovePhase } from "#phases/move-phase";
|
|
||||||
import { expSpriteKeys } from "#sprites/sprite-keys";
|
import { expSpriteKeys } from "#sprites/sprite-keys";
|
||||||
import { hasExpSprite } from "#sprites/sprite-utils";
|
import { hasExpSprite } from "#sprites/sprite-utils";
|
||||||
import type { Variant } from "#sprites/variant";
|
import type { Variant } from "#sprites/variant";
|
||||||
@ -799,12 +798,14 @@ export class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of EnemyPokemon of length 1 or 2 depending on if in a double battle or not.
|
* Returns an array of EnemyPokemon of length 1 or 2 depending on if in a double battle or not.
|
||||||
* Does not actually check if the pokemon are on the field or not.
|
* @param active - (Default `false`) Whether to consider only {@linkcode Pokemon.isActive | active} on-field pokemon
|
||||||
* @returns array of {@linkcode EnemyPokemon}
|
* @returns array of {@linkcode EnemyPokemon}
|
||||||
*/
|
*/
|
||||||
public getEnemyField(): EnemyPokemon[] {
|
public getEnemyField(active = false): EnemyPokemon[] {
|
||||||
const party = this.getEnemyParty();
|
const party = this.getEnemyParty();
|
||||||
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
|
return party
|
||||||
|
.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1))
|
||||||
|
.filter(p => !active || p.isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -829,25 +830,7 @@ export class BattleScene extends SceneBase {
|
|||||||
* @param allyPokemon - The {@linkcode Pokemon} allied with the removed Pokemon; will have moves redirected to it
|
* @param allyPokemon - The {@linkcode Pokemon} allied with the removed Pokemon; will have moves redirected to it
|
||||||
*/
|
*/
|
||||||
redirectPokemonMoves(removedPokemon: Pokemon, allyPokemon: Pokemon): void {
|
redirectPokemonMoves(removedPokemon: Pokemon, allyPokemon: Pokemon): void {
|
||||||
// failsafe: if not a double battle just return
|
this.phaseManager.redirectMoves(removedPokemon, allyPokemon);
|
||||||
if (this.currentBattle.double === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (allyPokemon?.isActive(true)) {
|
|
||||||
let targetingMovePhase: MovePhase;
|
|
||||||
do {
|
|
||||||
targetingMovePhase = this.phaseManager.findPhase(
|
|
||||||
mp =>
|
|
||||||
mp.is("MovePhase")
|
|
||||||
&& mp.targets.length === 1
|
|
||||||
&& mp.targets[0] === removedPokemon.getBattlerIndex()
|
|
||||||
&& mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
|
|
||||||
) as MovePhase;
|
|
||||||
if (targetingMovePhase && targetingMovePhase.targets[0] !== allyPokemon.getBattlerIndex()) {
|
|
||||||
targetingMovePhase.targets[0] = allyPokemon.getBattlerIndex();
|
|
||||||
}
|
|
||||||
} while (targetingMovePhase);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1447,7 +1430,7 @@ export class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lastBattle?.double && !newDouble) {
|
if (lastBattle?.double && !newDouble) {
|
||||||
this.phaseManager.tryRemovePhase((p: Phase) => p.is("SwitchPhase"));
|
this.phaseManager.tryRemovePhase("SwitchPhase");
|
||||||
for (const p of this.getPlayerField()) {
|
for (const p of this.getPlayerField()) {
|
||||||
p.lapseTag(BattlerTagType.COMMANDED);
|
p.lapseTag(BattlerTagType.COMMANDED);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import { CommonAnim } from "#enums/move-anims-common";
|
|||||||
import { MoveCategory } from "#enums/move-category";
|
import { MoveCategory } from "#enums/move-category";
|
||||||
import { MoveFlags } from "#enums/move-flags";
|
import { MoveFlags } from "#enums/move-flags";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
||||||
import { MoveResult } from "#enums/move-result";
|
import { MoveResult } from "#enums/move-result";
|
||||||
import { MoveTarget } from "#enums/move-target";
|
import { MoveTarget } from "#enums/move-target";
|
||||||
import { MoveUseMode } from "#enums/move-use-mode";
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
@ -2555,7 +2556,7 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr {
|
|||||||
|
|
||||||
override apply({ pokemon, simulated, cancelled }: AbAttrParamsWithCancel): void {
|
override apply({ pokemon, simulated, cancelled }: AbAttrParamsWithCancel): void {
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
globalScene.phaseManager.pushNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
"StatStageChangePhase",
|
"StatStageChangePhase",
|
||||||
pokemon.getBattlerIndex(),
|
pokemon.getBattlerIndex(),
|
||||||
false,
|
false,
|
||||||
@ -3240,6 +3241,7 @@ export class CommanderAbAttr extends AbAttr {
|
|||||||
return (
|
return (
|
||||||
globalScene.currentBattle?.double
|
globalScene.currentBattle?.double
|
||||||
&& ally != null
|
&& ally != null
|
||||||
|
&& ally.isActive(true)
|
||||||
&& ally.species.speciesId === SpeciesId.DONDOZO
|
&& ally.species.speciesId === SpeciesId.DONDOZO
|
||||||
&& !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED))
|
&& !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED))
|
||||||
);
|
);
|
||||||
@ -3254,7 +3256,7 @@ export class CommanderAbAttr extends AbAttr {
|
|||||||
// Apply boosts from this effect to the ally Dondozo
|
// Apply boosts from this effect to the ally Dondozo
|
||||||
pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, MoveId.NONE, pokemon.id);
|
pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, MoveId.NONE, pokemon.id);
|
||||||
// Cancel the source Pokemon's next move (if a move is queued)
|
// Cancel the source Pokemon's next move (if a move is queued)
|
||||||
globalScene.phaseManager.tryRemovePhase(phase => phase.is("MovePhase") && phase.pokemon === pokemon);
|
globalScene.phaseManager.tryRemovePhase("MovePhase", phase => phase.pokemon === pokemon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5004,7 +5006,14 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
|
|||||||
// If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance
|
// If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance
|
||||||
if (move.getMove().is("AttackMove") || move.getMove().is("StatusMove")) {
|
if (move.getMove().is("AttackMove") || move.getMove().is("StatusMove")) {
|
||||||
const target = this.getTarget(pokemon, source, targets);
|
const target = this.getTarget(pokemon, source, targets);
|
||||||
globalScene.phaseManager.unshiftNew("MovePhase", pokemon, target, move, MoveUseMode.INDIRECT);
|
globalScene.phaseManager.unshiftNew(
|
||||||
|
"MovePhase",
|
||||||
|
pokemon,
|
||||||
|
target,
|
||||||
|
move,
|
||||||
|
MoveUseMode.INDIRECT,
|
||||||
|
MovePhaseTimingModifier.FIRST,
|
||||||
|
);
|
||||||
} else if (move.getMove().is("SelfStatusMove")) {
|
} else if (move.getMove().is("SelfStatusMove")) {
|
||||||
// If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself
|
// If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
@ -5013,6 +5022,7 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
|
|||||||
[pokemon.getBattlerIndex()],
|
[pokemon.getBattlerIndex()],
|
||||||
move,
|
move,
|
||||||
MoveUseMode.INDIRECT,
|
MoveUseMode.INDIRECT,
|
||||||
|
MovePhaseTimingModifier.FIRST,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6028,11 +6038,6 @@ export class IllusionPostBattleAbAttr extends PostBattleAbAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BypassSpeedChanceAbAttrParams extends AbAttrBaseParams {
|
|
||||||
/** Holds whether the speed check is bypassed after ability application */
|
|
||||||
bypass: BooleanHolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a Pokémon with this Ability selects a damaging move, it has a 30% chance of going first in its priority bracket. If the Ability activates, this is announced at the start of the turn (after move selection).
|
* If a Pokémon with this Ability selects a damaging move, it has a 30% chance of going first in its priority bracket. If the Ability activates, this is announced at the start of the turn (after move selection).
|
||||||
* @sealed
|
* @sealed
|
||||||
@ -6048,26 +6053,28 @@ export class BypassSpeedChanceAbAttr extends AbAttr {
|
|||||||
this.chance = chance;
|
this.chance = chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
override canApply({ bypass, simulated, pokemon }: BypassSpeedChanceAbAttrParams): boolean {
|
override canApply({ simulated, pokemon }: AbAttrBaseParams): boolean {
|
||||||
// TODO: Consider whether we can move the simulated check to the `apply` method
|
// TODO: Consider whether we can move the simulated check to the `apply` method
|
||||||
// May be difficult as we likely do not want to modify the randBattleSeed
|
// May be difficult as we likely do not want to modify the randBattleSeed
|
||||||
const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
|
const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
|
||||||
const isCommandFight = turnCommand?.command === Command.FIGHT;
|
|
||||||
const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null;
|
const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null;
|
||||||
const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL;
|
const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL;
|
||||||
return (
|
return (
|
||||||
!simulated && !bypass.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove
|
!simulated
|
||||||
|
&& pokemon.randBattleSeedInt(100) < this.chance
|
||||||
|
&& isDamageMove
|
||||||
|
&& pokemon.canAddTag(BattlerTagType.BYPASS_SPEED)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bypass move order in their priority bracket when pokemon choose damaging move
|
* bypass move order in their priority bracket when pokemon choose damaging move
|
||||||
*/
|
*/
|
||||||
override apply({ bypass }: BypassSpeedChanceAbAttrParams): void {
|
override apply({ pokemon }: AbAttrBaseParams): void {
|
||||||
bypass.value = true;
|
pokemon.addTag(BattlerTagType.BYPASS_SPEED);
|
||||||
}
|
}
|
||||||
|
|
||||||
override getTriggerMessage({ pokemon }: BypassSpeedChanceAbAttrParams, _abilityName: string): string {
|
override getTriggerMessage({ pokemon }: AbAttrBaseParams, _abilityName: string): string {
|
||||||
return i18next.t("abilityTriggers:quickDraw", { pokemonName: getPokemonNameWithAffix(pokemon) });
|
return i18next.t("abilityTriggers:quickDraw", { pokemonName: getPokemonNameWithAffix(pokemon) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6075,8 +6082,6 @@ export class BypassSpeedChanceAbAttr extends AbAttr {
|
|||||||
export interface PreventBypassSpeedChanceAbAttrParams extends AbAttrBaseParams {
|
export interface PreventBypassSpeedChanceAbAttrParams extends AbAttrBaseParams {
|
||||||
/** Holds whether the speed check is bypassed after ability application */
|
/** Holds whether the speed check is bypassed after ability application */
|
||||||
bypass: BooleanHolder;
|
bypass: BooleanHolder;
|
||||||
/** Holds whether the Pokemon can check held items for Quick Claw's effects */
|
|
||||||
canCheckHeldItems: BooleanHolder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -6103,9 +6108,8 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr {
|
|||||||
return isCommandFight && this.condition(pokemon, move!);
|
return isCommandFight && this.condition(pokemon, move!);
|
||||||
}
|
}
|
||||||
|
|
||||||
override apply({ bypass, canCheckHeldItems }: PreventBypassSpeedChanceAbAttrParams): void {
|
override apply({ bypass }: PreventBypassSpeedChanceAbAttrParams): void {
|
||||||
bypass.value = false;
|
bypass.value = false;
|
||||||
canCheckHeldItems.value = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6203,8 +6207,7 @@ class ForceSwitchOutHelper {
|
|||||||
|
|
||||||
if (switchOutTarget.hp > 0) {
|
if (switchOutTarget.hp > 0) {
|
||||||
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||||
globalScene.phaseManager.prependNewToPhase(
|
globalScene.phaseManager.queueDeferred(
|
||||||
"MoveEndPhase",
|
|
||||||
"SwitchPhase",
|
"SwitchPhase",
|
||||||
this.switchType,
|
this.switchType,
|
||||||
switchOutTarget.getFieldIndex(),
|
switchOutTarget.getFieldIndex(),
|
||||||
@ -6226,8 +6229,7 @@ class ForceSwitchOutHelper {
|
|||||||
const summonIndex = globalScene.currentBattle.trainer
|
const summonIndex = globalScene.currentBattle.trainer
|
||||||
? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot)
|
? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot)
|
||||||
: 0;
|
: 0;
|
||||||
globalScene.phaseManager.prependNewToPhase(
|
globalScene.phaseManager.queueDeferred(
|
||||||
"MoveEndPhase",
|
|
||||||
"SwitchSummonPhase",
|
"SwitchSummonPhase",
|
||||||
this.switchType,
|
this.switchType,
|
||||||
switchOutTarget.getFieldIndex(),
|
switchOutTarget.getFieldIndex(),
|
||||||
@ -6949,7 +6951,7 @@ export function initAbilities() {
|
|||||||
.attr(TypeImmunityStatStageChangeAbAttr, PokemonType.ELECTRIC, Stat.SPD, 1)
|
.attr(TypeImmunityStatStageChangeAbAttr, PokemonType.ELECTRIC, Stat.SPD, 1)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(AbilityId.RIVALRY, 4)
|
new Ability(AbilityId.RIVALRY, 4)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, _move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender === target?.gender, 1.25, true)
|
.attr(MovePowerBoostAbAttr, (user, target, _move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender === target?.gender, 1.25)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, _move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender !== target?.gender, 0.75),
|
.attr(MovePowerBoostAbAttr, (user, target, _move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender !== target?.gender, 0.75),
|
||||||
new Ability(AbilityId.STEADFAST, 4)
|
new Ability(AbilityId.STEADFAST, 4)
|
||||||
.attr(FlinchStatStageChangeAbAttr, [ Stat.SPD ], 1),
|
.attr(FlinchStatStageChangeAbAttr, [ Stat.SPD ], 1),
|
||||||
@ -7161,7 +7163,7 @@ export function initAbilities() {
|
|||||||
new Ability(AbilityId.ANALYTIC, 5)
|
new Ability(AbilityId.ANALYTIC, 5)
|
||||||
.attr(MovePowerBoostAbAttr, (user) =>
|
.attr(MovePowerBoostAbAttr, (user) =>
|
||||||
// Boost power if all other Pokemon have already moved (no other moves are slated to execute)
|
// Boost power if all other Pokemon have already moved (no other moves are slated to execute)
|
||||||
!globalScene.phaseManager.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.id !== user?.id),
|
!globalScene.phaseManager.hasPhaseOfType("MovePhase", phase => phase.pokemon.id !== user?.id),
|
||||||
1.3),
|
1.3),
|
||||||
new Ability(AbilityId.ILLUSION, 5)
|
new Ability(AbilityId.ILLUSION, 5)
|
||||||
// The Pokemon generate an illusion if it's available
|
// The Pokemon generate an illusion if it's available
|
||||||
@ -7742,8 +7744,8 @@ export function initAbilities() {
|
|||||||
new Ability(AbilityId.SHARPNESS, 9)
|
new Ability(AbilityId.SHARPNESS, 9)
|
||||||
.attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5),
|
.attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5),
|
||||||
new Ability(AbilityId.SUPREME_OVERLORD, 9)
|
new Ability(AbilityId.SUPREME_OVERLORD, 9)
|
||||||
.attr(VariableMovePowerBoostAbAttr, (user, _target, _move) => 1 + 0.1 * Math.min(user.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints, 5))
|
.conditionalAttr((p) => (p.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints) > 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.SUPREME_OVERLORD, 0, true)
|
||||||
.partial(), // Should only boost once, on summon
|
.edgeCase(), // Tag is not tied to ability, so suppression/removal etc will not function until a structure to allow this is implemented
|
||||||
new Ability(AbilityId.COSTAR, 9, -2)
|
new Ability(AbilityId.COSTAR, 9, -2)
|
||||||
.attr(PostSummonCopyAllyStatsAbAttr),
|
.attr(PostSummonCopyAllyStatsAbAttr),
|
||||||
new Ability(AbilityId.TOXIC_DEBRIS, 9)
|
new Ability(AbilityId.TOXIC_DEBRIS, 9)
|
||||||
|
@ -74,7 +74,6 @@ function applyAbAttrsInternal<T extends CallableAbAttrString>(
|
|||||||
for (const passive of [false, true]) {
|
for (const passive of [false, true]) {
|
||||||
params.passive = passive;
|
params.passive = passive;
|
||||||
applySingleAbAttrs(attrType, params, gainedMidTurn, messages);
|
applySingleAbAttrs(attrType, params, gainedMidTurn, messages);
|
||||||
globalScene.phaseManager.clearPhaseQueueSplice();
|
|
||||||
}
|
}
|
||||||
// We need to restore passive to its original state in the case that it was undefined on entry
|
// We need to restore passive to its original state in the case that it was undefined on entry
|
||||||
// this is necessary in case this method is called with an object that is reused.
|
// this is necessary in case this method is called with an object that is reused.
|
||||||
|
@ -56,6 +56,7 @@ import { allMoves } from "#data/data-lists";
|
|||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { HitResult } from "#enums/hit-result";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { CommonAnim } from "#enums/move-anims-common";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
@ -84,6 +85,10 @@ interface BaseArenaTag {
|
|||||||
* The tag's remaining duration. Setting to any number `<=0` will make the tag's duration effectively infinite.
|
* The tag's remaining duration. Setting to any number `<=0` will make the tag's duration effectively infinite.
|
||||||
*/
|
*/
|
||||||
turnCount: number;
|
turnCount: number;
|
||||||
|
/**
|
||||||
|
* The tag's max duration.
|
||||||
|
*/
|
||||||
|
maxDuration: number;
|
||||||
/**
|
/**
|
||||||
* The {@linkcode MoveId} that created this tag, or `undefined` if not set by a move.
|
* The {@linkcode MoveId} that created this tag, or `undefined` if not set by a move.
|
||||||
*/
|
*/
|
||||||
@ -110,12 +115,14 @@ export abstract class ArenaTag implements BaseArenaTag {
|
|||||||
/** The type of the arena tag */
|
/** The type of the arena tag */
|
||||||
public abstract readonly tagType: ArenaTagType;
|
public abstract readonly tagType: ArenaTagType;
|
||||||
public turnCount: number;
|
public turnCount: number;
|
||||||
|
public maxDuration: number;
|
||||||
public sourceMove?: MoveId;
|
public sourceMove?: MoveId;
|
||||||
public sourceId: number | undefined;
|
public sourceId: number | undefined;
|
||||||
public side: ArenaTagSide;
|
public side: ArenaTagSide;
|
||||||
|
|
||||||
constructor(turnCount: number, sourceMove?: MoveId, sourceId?: number, side: ArenaTagSide = ArenaTagSide.BOTH) {
|
constructor(turnCount: number, sourceMove?: MoveId, sourceId?: number, side: ArenaTagSide = ArenaTagSide.BOTH) {
|
||||||
this.turnCount = turnCount;
|
this.turnCount = turnCount;
|
||||||
|
this.maxDuration = turnCount;
|
||||||
this.sourceMove = sourceMove;
|
this.sourceMove = sourceMove;
|
||||||
this.sourceId = sourceId;
|
this.sourceId = sourceId;
|
||||||
this.side = side;
|
this.side = side;
|
||||||
@ -164,6 +171,7 @@ export abstract class ArenaTag implements BaseArenaTag {
|
|||||||
*/
|
*/
|
||||||
loadTag<const T extends this>(source: BaseArenaTag & Pick<T, "tagType">): void {
|
loadTag<const T extends this>(source: BaseArenaTag & Pick<T, "tagType">): void {
|
||||||
this.turnCount = source.turnCount;
|
this.turnCount = source.turnCount;
|
||||||
|
this.maxDuration = source.maxDuration;
|
||||||
this.sourceMove = source.sourceMove;
|
this.sourceMove = source.sourceMove;
|
||||||
this.sourceId = source.sourceId;
|
this.sourceId = source.sourceId;
|
||||||
this.side = source.side;
|
this.side = source.side;
|
||||||
@ -1590,6 +1598,145 @@ export class SuppressAbilitiesTag extends SerializableArenaTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface containing data related to a queued healing effect from
|
||||||
|
* {@link https://bulbapedia.bulbagarden.net/wiki/Healing_Wish_(move) | Healing Wish}
|
||||||
|
* or {@link https://bulbapedia.bulbagarden.net/wiki/Lunar_Dance_(move) | Lunar Dance}.
|
||||||
|
*/
|
||||||
|
interface PendingHealEffect {
|
||||||
|
/** The {@linkcode Pokemon.id | PID} of the {@linkcode Pokemon} that created the effect. */
|
||||||
|
readonly sourceId: number;
|
||||||
|
/** The {@linkcode MoveId} of the move that created the effect. */
|
||||||
|
readonly moveId: MoveId;
|
||||||
|
/** If `true`, also restores the target's PP when the effect activates. */
|
||||||
|
readonly restorePP: boolean;
|
||||||
|
/** The message to display when the effect activates */
|
||||||
|
readonly healMessage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arena tag to contain stored healing effects, namely from
|
||||||
|
* {@link https://bulbapedia.bulbagarden.net/wiki/Healing_Wish_(move) | Healing Wish}
|
||||||
|
* and {@link https://bulbapedia.bulbagarden.net/wiki/Lunar_Dance_(move) | Lunar Dance}.
|
||||||
|
* When a damaged Pokemon first enters the effect's {@linkcode BattlerIndex | field position},
|
||||||
|
* their HP is fully restored, and they are cured of any non-volatile status condition.
|
||||||
|
* If the effect is from Lunar Dance, their PP is also restored.
|
||||||
|
*/
|
||||||
|
export class PendingHealTag extends SerializableArenaTag {
|
||||||
|
public readonly tagType = ArenaTagType.PENDING_HEAL;
|
||||||
|
/** All pending healing effects, organized by {@linkcode BattlerIndex} */
|
||||||
|
public readonly pendingHeals: Partial<Record<BattlerIndex, PendingHealEffect[]>> = {};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a pending healing effect to the field. Effects under the same move *and*
|
||||||
|
* target index as an existing effect are ignored.
|
||||||
|
* @param targetIndex - The {@linkcode BattlerIndex} under which the effect applies
|
||||||
|
* @param healEffect - The {@linkcode PendingHealEffect | data} for the pending heal effect
|
||||||
|
*/
|
||||||
|
public queueHeal(targetIndex: BattlerIndex, healEffect: PendingHealEffect): void {
|
||||||
|
const existingHealEffects = this.pendingHeals[targetIndex];
|
||||||
|
if (existingHealEffects) {
|
||||||
|
if (!existingHealEffects.some(he => he.moveId === healEffect.moveId)) {
|
||||||
|
existingHealEffects.push(healEffect);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.pendingHeals[targetIndex] = [healEffect];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Removes default on-remove message */
|
||||||
|
override onRemove(_arena: Arena): void {}
|
||||||
|
|
||||||
|
/** This arena tag is removed at the end of the turn if no pending healing effects are on the field */
|
||||||
|
override lapse(_arena: Arena): boolean {
|
||||||
|
for (const key in this.pendingHeals) {
|
||||||
|
if (this.pendingHeals[key].length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a pending healing effect on the given target index. If an effect is found for
|
||||||
|
* the index, the Pokemon at that index is healed to full HP, is cured of any non-volatile status,
|
||||||
|
* and has its PP fully restored (if the effect is from Lunar Dance).
|
||||||
|
* @param arena - The {@linkcode Arena} containing this tag
|
||||||
|
* @param simulated - If `true`, suppresses changes to game state
|
||||||
|
* @param pokemon - The {@linkcode Pokemon} receiving the healing effect
|
||||||
|
* @returns `true` if the target Pokemon was healed by this effect
|
||||||
|
* @todo This should also be called when a Pokemon moves into a new position via Ally Switch
|
||||||
|
*/
|
||||||
|
override apply(arena: Arena, simulated: boolean, pokemon: Pokemon): boolean {
|
||||||
|
const targetIndex = pokemon.getBattlerIndex();
|
||||||
|
const targetEffects = this.pendingHeals[targetIndex];
|
||||||
|
|
||||||
|
if (targetEffects == null || targetEffects.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const healEffect = targetEffects.find(effect => this.canApply(effect, pokemon));
|
||||||
|
|
||||||
|
if (healEffect == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simulated) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sourceId, moveId, restorePP, healMessage } = healEffect;
|
||||||
|
const sourcePokemon = globalScene.getPokemonById(sourceId);
|
||||||
|
if (!sourcePokemon) {
|
||||||
|
console.warn(`Source of pending ${allMoves[moveId].name} effect is undefined!`);
|
||||||
|
targetEffects.splice(targetEffects.indexOf(healEffect), 1);
|
||||||
|
// Re-evaluate after the invalid heal effect is removed
|
||||||
|
return this.apply(arena, simulated, pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
globalScene.phaseManager.unshiftNew(
|
||||||
|
"PokemonHealPhase",
|
||||||
|
targetIndex,
|
||||||
|
pokemon.getMaxHp(),
|
||||||
|
healMessage,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
restorePP,
|
||||||
|
);
|
||||||
|
|
||||||
|
targetEffects.splice(targetEffects.indexOf(healEffect), 1);
|
||||||
|
|
||||||
|
return healEffect != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the given {@linkcode PendingHealEffect} can immediately heal
|
||||||
|
* the given target {@linkcode Pokemon}.
|
||||||
|
* @param healEffect - The {@linkcode PendingHealEffect} to evaluate
|
||||||
|
* @param pokemon - The {@linkcode Pokemon} to evaluate against
|
||||||
|
* @returns `true` if the Pokemon can be healed by the effect
|
||||||
|
*/
|
||||||
|
private canApply(healEffect: PendingHealEffect, pokemon: Pokemon): boolean {
|
||||||
|
return (
|
||||||
|
!pokemon.isFullHp()
|
||||||
|
|| pokemon.status != null
|
||||||
|
|| (healEffect.restorePP && pokemon.getMoveset().some(mv => mv.ppUsed > 0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override loadTag(source: BaseArenaTag & Pick<PendingHealTag, "tagType" | "pendingHeals">): void {
|
||||||
|
super.loadTag(source);
|
||||||
|
(this as Mutable<this>).pendingHeals = source.pendingHeals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter
|
// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter
|
||||||
export function getArenaTag(
|
export function getArenaTag(
|
||||||
tagType: ArenaTagType,
|
tagType: ArenaTagType,
|
||||||
@ -1653,6 +1800,8 @@ export function getArenaTag(
|
|||||||
return new FairyLockTag(turnCount, sourceId);
|
return new FairyLockTag(turnCount, sourceId);
|
||||||
case ArenaTagType.NEUTRALIZING_GAS:
|
case ArenaTagType.NEUTRALIZING_GAS:
|
||||||
return new SuppressAbilitiesTag(sourceId);
|
return new SuppressAbilitiesTag(sourceId);
|
||||||
|
case ArenaTagType.PENDING_HEAL:
|
||||||
|
return new PendingHealTag();
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1701,5 +1850,6 @@ export type ArenaTagTypeMap = {
|
|||||||
[ArenaTagType.GRASS_WATER_PLEDGE]: GrassWaterPledgeTag;
|
[ArenaTagType.GRASS_WATER_PLEDGE]: GrassWaterPledgeTag;
|
||||||
[ArenaTagType.FAIRY_LOCK]: FairyLockTag;
|
[ArenaTagType.FAIRY_LOCK]: FairyLockTag;
|
||||||
[ArenaTagType.NEUTRALIZING_GAS]: SuppressAbilitiesTag;
|
[ArenaTagType.NEUTRALIZING_GAS]: SuppressAbilitiesTag;
|
||||||
|
[ArenaTagType.PENDING_HEAL]: PendingHealTag;
|
||||||
[ArenaTagType.NONE]: NoneTag;
|
[ArenaTagType.NONE]: NoneTag;
|
||||||
};
|
};
|
||||||
|
@ -1119,7 +1119,7 @@ export const biomePokemonPools: BiomePokemonPools = {
|
|||||||
},
|
},
|
||||||
[BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.LUCARIO, SpeciesId.THROH, SpeciesId.SAWK, { 1: [ SpeciesId.PANCHAM ], 52: [ SpeciesId.PANGORO ] } ] },
|
[BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.LUCARIO, SpeciesId.THROH, SpeciesId.SAWK, { 1: [ SpeciesId.PANCHAM ], 52: [ SpeciesId.PANGORO ] } ] },
|
||||||
[BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONTOP, SpeciesId.GALLADE, SpeciesId.GALAR_FARFETCHD ] },
|
[BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONTOP, SpeciesId.GALLADE, SpeciesId.GALAR_FARFETCHD ] },
|
||||||
[BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERRAKION, { 1: [ SpeciesId.KUBFU ], 60: [ SpeciesId.URSHIFU] }, SpeciesId.GALAR_ZAPDOS ] },
|
[BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERRAKION, { 1: [ SpeciesId.KUBFU ], 60: [ SpeciesId.URSHIFU ] }, SpeciesId.GALAR_ZAPDOS ] },
|
||||||
[BiomePoolTier.BOSS]: {
|
[BiomePoolTier.BOSS]: {
|
||||||
[TimeOfDay.DAWN]: [],
|
[TimeOfDay.DAWN]: [],
|
||||||
[TimeOfDay.DAY]: [],
|
[TimeOfDay.DAY]: [],
|
||||||
@ -1128,7 +1128,7 @@ export const biomePokemonPools: BiomePokemonPools = {
|
|||||||
[TimeOfDay.ALL]: [ SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.HARIYAMA, SpeciesId.MEDICHAM, SpeciesId.LUCARIO, SpeciesId.TOXICROAK, SpeciesId.THROH, SpeciesId.SAWK, SpeciesId.SCRAFTY, SpeciesId.MIENSHAO, SpeciesId.BEWEAR, SpeciesId.GRAPPLOCT, SpeciesId.ANNIHILAPE ]
|
[TimeOfDay.ALL]: [ SpeciesId.HITMONLEE, SpeciesId.HITMONCHAN, SpeciesId.HARIYAMA, SpeciesId.MEDICHAM, SpeciesId.LUCARIO, SpeciesId.TOXICROAK, SpeciesId.THROH, SpeciesId.SAWK, SpeciesId.SCRAFTY, SpeciesId.MIENSHAO, SpeciesId.BEWEAR, SpeciesId.GRAPPLOCT, SpeciesId.ANNIHILAPE ]
|
||||||
},
|
},
|
||||||
[BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONTOP, SpeciesId.GALLADE, SpeciesId.PANGORO, SpeciesId.SIRFETCHD, SpeciesId.HISUI_DECIDUEYE ] },
|
[BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.HITMONTOP, SpeciesId.GALLADE, SpeciesId.PANGORO, SpeciesId.SIRFETCHD, SpeciesId.HISUI_DECIDUEYE ] },
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERRAKION, { 1: [ SpeciesId.KUBFU ], 60: [ SpeciesId.URSHIFU] } ] },
|
[BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.TERRAKION, { 1: [ SpeciesId.KUBFU ], 60: [ SpeciesId.URSHIFU ] } ] },
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZAMAZENTA, SpeciesId.GALAR_ZAPDOS ] }
|
[BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ZAMAZENTA, SpeciesId.GALAR_ZAPDOS ] }
|
||||||
},
|
},
|
||||||
[BiomeId.FACTORY]: {
|
[BiomeId.FACTORY]: {
|
||||||
@ -1597,10 +1597,10 @@ export const biomePokemonPools: BiomePokemonPools = {
|
|||||||
[BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.SOLOSIS ], 32: [ SpeciesId.DUOSION ], 41: [ SpeciesId.REUNICLUS ] } ] },
|
[BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.SOLOSIS ], 32: [ SpeciesId.DUOSION ], 41: [ SpeciesId.REUNICLUS ] } ] },
|
||||||
[BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, { 1: [ SpeciesId.PORYGON ], 30: [ SpeciesId.PORYGON2 ] } ] },
|
[BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.DITTO, { 1: [ SpeciesId.PORYGON ], 30: [ SpeciesId.PORYGON2 ] } ] },
|
||||||
[BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM ] },
|
[BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM ] },
|
||||||
[BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [SpeciesId.TYPE_NULL], 60: [ SpeciesId.SILVALLY ] } ] },
|
[BiomePoolTier.ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ { 1: [ SpeciesId.TYPE_NULL ], 60: [ SpeciesId.SILVALLY ] } ] },
|
||||||
[BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MUK, SpeciesId.ELECTRODE, SpeciesId.BRONZONG, SpeciesId.MAGNEZONE, SpeciesId.PORYGON_Z, SpeciesId.REUNICLUS, SpeciesId.KLINKLANG ] },
|
[BiomePoolTier.BOSS]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MUK, SpeciesId.ELECTRODE, SpeciesId.BRONZONG, SpeciesId.MAGNEZONE, SpeciesId.PORYGON_Z, SpeciesId.REUNICLUS, SpeciesId.KLINKLANG ] },
|
||||||
[BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] },
|
[BiomePoolTier.BOSS_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] },
|
||||||
[BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM, SpeciesId.ZYGARDE, { 1: [SpeciesId.TYPE_NULL], 60: [ SpeciesId.SILVALLY ] } ] },
|
[BiomePoolTier.BOSS_SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.ROTOM, SpeciesId.ZYGARDE, { 1: [ SpeciesId.TYPE_NULL ], 60: [ SpeciesId.SILVALLY ] } ] },
|
||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MEWTWO, SpeciesId.MIRAIDON ] }
|
[BiomePoolTier.BOSS_ULTRA_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [ SpeciesId.MEWTWO, SpeciesId.MIRAIDON ] }
|
||||||
},
|
},
|
||||||
[BiomeId.END]: {
|
[BiomeId.END]: {
|
||||||
@ -5627,10 +5627,12 @@ export function initBiomes() {
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ SpeciesId.TYPE_NULL, PokemonType.NORMAL, -1, [
|
[ SpeciesId.TYPE_NULL, PokemonType.NORMAL, -1, [
|
||||||
[ BiomeId.LABORATORY, BiomePoolTier.ULTRA_RARE ]
|
[ BiomeId.LABORATORY, BiomePoolTier.ULTRA_RARE ],
|
||||||
|
[ BiomeId.LABORATORY, BiomePoolTier.BOSS_SUPER_RARE ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ SpeciesId.SILVALLY, PokemonType.NORMAL, -1, [
|
[ SpeciesId.SILVALLY, PokemonType.NORMAL, -1, [
|
||||||
|
[ BiomeId.LABORATORY, BiomePoolTier.ULTRA_RARE ],
|
||||||
[ BiomeId.LABORATORY, BiomePoolTier.BOSS_SUPER_RARE ]
|
[ BiomeId.LABORATORY, BiomePoolTier.BOSS_SUPER_RARE ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -5773,10 +5775,12 @@ export function initBiomes() {
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ SpeciesId.POIPOLE, PokemonType.POISON, -1, [
|
[ SpeciesId.POIPOLE, PokemonType.POISON, -1, [
|
||||||
[ BiomeId.SWAMP, BiomePoolTier.ULTRA_RARE ]
|
[ BiomeId.SWAMP, BiomePoolTier.ULTRA_RARE ],
|
||||||
|
[ BiomeId.SWAMP, BiomePoolTier.BOSS_SUPER_RARE ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ SpeciesId.NAGANADEL, PokemonType.POISON, PokemonType.DRAGON, [
|
[ SpeciesId.NAGANADEL, PokemonType.POISON, PokemonType.DRAGON, [
|
||||||
|
[ BiomeId.SWAMP, BiomePoolTier.ULTRA_RARE ],
|
||||||
[ BiomeId.SWAMP, BiomePoolTier.BOSS_SUPER_RARE ]
|
[ BiomeId.SWAMP, BiomePoolTier.BOSS_SUPER_RARE ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -6165,10 +6169,12 @@ export function initBiomes() {
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ SpeciesId.KUBFU, PokemonType.FIGHTING, -1, [
|
[ SpeciesId.KUBFU, PokemonType.FIGHTING, -1, [
|
||||||
[ BiomeId.DOJO, BiomePoolTier.ULTRA_RARE ]
|
[ BiomeId.DOJO, BiomePoolTier.ULTRA_RARE ],
|
||||||
|
[ BiomeId.DOJO, BiomePoolTier.BOSS_SUPER_RARE ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ SpeciesId.URSHIFU, PokemonType.FIGHTING, PokemonType.DARK, [
|
[ SpeciesId.URSHIFU, PokemonType.FIGHTING, PokemonType.DARK, [
|
||||||
|
[ BiomeId.DOJO, BiomePoolTier.ULTRA_RARE ],
|
||||||
[ BiomeId.DOJO, BiomePoolTier.BOSS_SUPER_RARE ]
|
[ BiomeId.DOJO, BiomePoolTier.BOSS_SUPER_RARE ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -7209,7 +7215,8 @@ export function initBiomes() {
|
|||||||
],
|
],
|
||||||
[ TrainerType.SCIENTIST, [
|
[ TrainerType.SCIENTIST, [
|
||||||
[ BiomeId.DESERT, BiomePoolTier.COMMON ],
|
[ BiomeId.DESERT, BiomePoolTier.COMMON ],
|
||||||
[ BiomeId.RUINS, BiomePoolTier.COMMON ]
|
[ BiomeId.RUINS, BiomePoolTier.COMMON ],
|
||||||
|
[ BiomeId.LABORATORY, BiomePoolTier.COMMON ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ TrainerType.SMASHER, []],
|
[ TrainerType.SMASHER, []],
|
||||||
@ -7224,7 +7231,8 @@ export function initBiomes() {
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ TrainerType.SWIMMER, [
|
[ TrainerType.SWIMMER, [
|
||||||
[ BiomeId.SEA, BiomePoolTier.COMMON ]
|
[ BiomeId.SEA, BiomePoolTier.COMMON ],
|
||||||
|
[ BiomeId.SEABED, BiomePoolTier.COMMON ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
[ TrainerType.TWINS, [
|
[ TrainerType.TWINS, [
|
||||||
@ -7590,11 +7598,13 @@ export function initBiomes() {
|
|||||||
[ TrainerType.ALDER, []],
|
[ TrainerType.ALDER, []],
|
||||||
[ TrainerType.IRIS, []],
|
[ TrainerType.IRIS, []],
|
||||||
[ TrainerType.DIANTHA, []],
|
[ TrainerType.DIANTHA, []],
|
||||||
|
[ TrainerType.KUKUI, []],
|
||||||
[ TrainerType.HAU, []],
|
[ TrainerType.HAU, []],
|
||||||
|
[ TrainerType.LEON, []],
|
||||||
|
[ TrainerType.MUSTARD, []],
|
||||||
[ TrainerType.GEETA, []],
|
[ TrainerType.GEETA, []],
|
||||||
[ TrainerType.NEMONA, []],
|
[ TrainerType.NEMONA, []],
|
||||||
[ TrainerType.KIERAN, []],
|
[ TrainerType.KIERAN, []],
|
||||||
[ TrainerType.LEON, []],
|
|
||||||
[ TrainerType.RIVAL, []]
|
[ TrainerType.RIVAL, []]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -606,17 +606,7 @@ export class ShellTrapTag extends BattlerTag {
|
|||||||
|
|
||||||
// Trap should only be triggered by opponent's Physical moves
|
// Trap should only be triggered by opponent's Physical moves
|
||||||
if (phaseData?.move.category === MoveCategory.PHYSICAL && pokemon.isOpponent(phaseData.attacker)) {
|
if (phaseData?.move.category === MoveCategory.PHYSICAL && pokemon.isOpponent(phaseData.attacker)) {
|
||||||
const shellTrapPhaseIndex = globalScene.phaseManager.phaseQueue.findIndex(
|
globalScene.phaseManager.forceMoveNext((phase: MovePhase) => phase.pokemon === pokemon);
|
||||||
phase => phase.is("MovePhase") && phase.pokemon === pokemon,
|
|
||||||
);
|
|
||||||
const firstMovePhaseIndex = globalScene.phaseManager.phaseQueue.findIndex(phase => phase.is("MovePhase"));
|
|
||||||
|
|
||||||
// Only shift MovePhase timing if it's not already next up
|
|
||||||
if (shellTrapPhaseIndex !== -1 && shellTrapPhaseIndex !== firstMovePhaseIndex) {
|
|
||||||
const shellTrapMovePhase = globalScene.phaseManager.phaseQueue.splice(shellTrapPhaseIndex, 1)[0];
|
|
||||||
globalScene.phaseManager.prependToPhase(shellTrapMovePhase, "MovePhase");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.activated = true;
|
this.activated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1279,22 +1269,9 @@ export class EncoreTag extends MoveRestrictionBattlerTag {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const movePhase = globalScene.phaseManager.findPhase(m => m.is("MovePhase") && m.pokemon === pokemon);
|
|
||||||
if (movePhase) {
|
|
||||||
const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId);
|
const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId);
|
||||||
if (movesetMove) {
|
if (movesetMove) {
|
||||||
const lastMove = pokemon.getLastXMoves(1)[0];
|
globalScene.phaseManager.changePhaseMove((phase: MovePhase) => phase.pokemon === pokemon, movesetMove);
|
||||||
globalScene.phaseManager.tryReplacePhase(
|
|
||||||
m => m.is("MovePhase") && m.pokemon === pokemon,
|
|
||||||
globalScene.phaseManager.create(
|
|
||||||
"MovePhase",
|
|
||||||
pokemon,
|
|
||||||
lastMove.targets ?? [],
|
|
||||||
movesetMove,
|
|
||||||
MoveUseMode.NORMAL,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3578,6 +3555,25 @@ export class GrudgeTag extends SerializableBattlerTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag to allow the affected Pokemon's move to go first in its priority bracket.
|
||||||
|
* Used for {@link https://bulbapedia.bulbagarden.net/wiki/Quick_Draw_(Ability) | Quick Draw}
|
||||||
|
* and {@link https://bulbapedia.bulbagarden.net/wiki/Quick_Claw | Quick Claw}.
|
||||||
|
*/
|
||||||
|
export class BypassSpeedTag extends BattlerTag {
|
||||||
|
public override readonly tagType = BattlerTagType.BYPASS_SPEED;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(BattlerTagType.BYPASS_SPEED, BattlerTagLapseType.TURN_END, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
override canAdd(pokemon: Pokemon): boolean {
|
||||||
|
const bypass = new BooleanHolder(true);
|
||||||
|
applyAbAttrs("PreventBypassSpeedChanceAbAttr", { pokemon, bypass });
|
||||||
|
return bypass.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag used to heal the user of Psycho Shift of its status effect if Psycho Shift succeeds in transferring its status effect to the target Pokemon
|
* Tag used to heal the user of Psycho Shift of its status effect if Psycho Shift succeeds in transferring its status effect to the target Pokemon
|
||||||
*/
|
*/
|
||||||
@ -3626,6 +3622,41 @@ export class MagicCoatTag extends BattlerTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag associated with {@linkcode AbilityId.SUPREME_OVERLORD}
|
||||||
|
*/
|
||||||
|
export class SupremeOverlordTag extends AbilityBattlerTag {
|
||||||
|
public override readonly tagType = BattlerTagType.SUPREME_OVERLORD;
|
||||||
|
/** The number of faints at the time the user was sent out */
|
||||||
|
public readonly faintCount: number;
|
||||||
|
constructor() {
|
||||||
|
super(BattlerTagType.SUPREME_OVERLORD, AbilityId.SUPREME_OVERLORD, BattlerTagLapseType.FAINT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override onAdd(pokemon: Pokemon): boolean {
|
||||||
|
(this as Mutable<this>).faintCount = Math.min(
|
||||||
|
pokemon.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
globalScene.phaseManager.queueMessage(
|
||||||
|
i18next.t("battlerTags:supremeOverlordOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }),
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The damage multiplier for Supreme Overlord
|
||||||
|
*/
|
||||||
|
public getBoost(): number {
|
||||||
|
return 1 + 0.1 * this.faintCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override loadTag(source: BaseBattlerTag & Pick<SupremeOverlordTag, "tagType" | "faintCount">): void {
|
||||||
|
super.loadTag(source);
|
||||||
|
(this as Mutable<this>).faintCount = source.faintCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID.
|
* Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID.
|
||||||
* @param sourceId - The ID of the pokemon adding the tag
|
* @param sourceId - The ID of the pokemon adding the tag
|
||||||
@ -3826,6 +3857,10 @@ export function getBattlerTag(
|
|||||||
return new PsychoShiftTag();
|
return new PsychoShiftTag();
|
||||||
case BattlerTagType.MAGIC_COAT:
|
case BattlerTagType.MAGIC_COAT:
|
||||||
return new MagicCoatTag();
|
return new MagicCoatTag();
|
||||||
|
case BattlerTagType.SUPREME_OVERLORD:
|
||||||
|
return new SupremeOverlordTag();
|
||||||
|
case BattlerTagType.BYPASS_SPEED:
|
||||||
|
return new BypassSpeedTag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3960,4 +3995,6 @@ export type BattlerTagTypeMap = {
|
|||||||
[BattlerTagType.GRUDGE]: GrudgeTag;
|
[BattlerTagType.GRUDGE]: GrudgeTag;
|
||||||
[BattlerTagType.PSYCHO_SHIFT]: PsychoShiftTag;
|
[BattlerTagType.PSYCHO_SHIFT]: PsychoShiftTag;
|
||||||
[BattlerTagType.MAGIC_COAT]: MagicCoatTag;
|
[BattlerTagType.MAGIC_COAT]: MagicCoatTag;
|
||||||
|
[BattlerTagType.SUPREME_OVERLORD]: SupremeOverlordTag;
|
||||||
|
[BattlerTagType.BYPASS_SPEED]: BypassSpeedTag;
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@ import { PokemonSpecies } from "#data/pokemon-species";
|
|||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import type { Starter } from "#ui/starter-select-ui-handler";
|
import type { Starter } from "#types/save-data";
|
||||||
import { randSeedGauss, randSeedInt, randSeedItem } from "#utils/common";
|
import { randSeedGauss, randSeedInt, randSeedItem } from "#utils/common";
|
||||||
import { getEnumValues } from "#utils/enums";
|
import { getEnumValues } from "#utils/enums";
|
||||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
|
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
|
||||||
@ -66,8 +66,11 @@ function getDailyRunStarter(starterSpeciesForm: PokemonSpeciesForm, startingLeve
|
|||||||
const formIndex = starterSpeciesForm instanceof PokemonSpecies ? undefined : starterSpeciesForm.formIndex;
|
const formIndex = starterSpeciesForm instanceof PokemonSpecies ? undefined : starterSpeciesForm.formIndex;
|
||||||
const pokemon = globalScene.addPlayerPokemon(starterSpecies, startingLevel, undefined, formIndex);
|
const pokemon = globalScene.addPlayerPokemon(starterSpecies, startingLevel, undefined, formIndex);
|
||||||
const starter: Starter = {
|
const starter: Starter = {
|
||||||
species: starterSpecies,
|
speciesId: starterSpecies.speciesId,
|
||||||
dexAttr: pokemon.getDexAttr(),
|
shiny: pokemon.shiny,
|
||||||
|
variant: pokemon.variant,
|
||||||
|
formIndex: pokemon.formIndex,
|
||||||
|
ivs: pokemon.ivs,
|
||||||
abilityIndex: pokemon.abilityIndex,
|
abilityIndex: pokemon.abilityIndex,
|
||||||
passive: false,
|
passive: false,
|
||||||
nature: pokemon.getNature(),
|
nature: pokemon.getNature(),
|
||||||
|
@ -6,7 +6,7 @@ import { loggedInUser } from "#app/account";
|
|||||||
import type { GameMode } from "#app/game-mode";
|
import type { GameMode } from "#app/game-mode";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import type { EntryHazardTag } from "#data/arena-tag";
|
import type { EntryHazardTag, PendingHealTag } from "#data/arena-tag";
|
||||||
import { WeakenMoveTypeTag } from "#data/arena-tag";
|
import { WeakenMoveTypeTag } from "#data/arena-tag";
|
||||||
import { MoveChargeAnim } from "#data/battle-anims";
|
import { MoveChargeAnim } from "#data/battle-anims";
|
||||||
import {
|
import {
|
||||||
@ -18,6 +18,7 @@ import {
|
|||||||
ShellTrapTag,
|
ShellTrapTag,
|
||||||
StockpilingTag,
|
StockpilingTag,
|
||||||
SubstituteTag,
|
SubstituteTag,
|
||||||
|
SupremeOverlordTag,
|
||||||
TrappedTag,
|
TrappedTag,
|
||||||
TypeBoostTag,
|
TypeBoostTag,
|
||||||
} from "#data/battler-tags";
|
} from "#data/battler-tags";
|
||||||
@ -80,10 +81,8 @@ import { applyMoveAttrs } from "#moves/apply-attrs";
|
|||||||
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSketchMoves, invalidSleepTalkMoves } from "#moves/invalid-moves";
|
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSketchMoves, invalidSleepTalkMoves } from "#moves/invalid-moves";
|
||||||
import { frenzyMissFunc, getMoveTargets } from "#moves/move-utils";
|
import { frenzyMissFunc, getMoveTargets } from "#moves/move-utils";
|
||||||
import { PokemonMove } from "#moves/pokemon-move";
|
import { PokemonMove } from "#moves/pokemon-move";
|
||||||
import { MoveEndPhase } from "#phases/move-end-phase";
|
|
||||||
import { MovePhase } from "#phases/move-phase";
|
import { MovePhase } from "#phases/move-phase";
|
||||||
import { PokemonHealPhase } from "#phases/pokemon-heal-phase";
|
import { PokemonHealPhase } from "#phases/pokemon-heal-phase";
|
||||||
import { SwitchSummonPhase } from "#phases/switch-summon-phase";
|
|
||||||
import type { AttackMoveResult } from "#types/attack-move-result";
|
import type { AttackMoveResult } from "#types/attack-move-result";
|
||||||
import type { Localizable } from "#types/locales";
|
import type { Localizable } from "#types/locales";
|
||||||
import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString, MoveMessageFunc } from "#types/move-types";
|
import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString, MoveMessageFunc } from "#types/move-types";
|
||||||
@ -93,6 +92,7 @@ import { getEnumValues } from "#utils/enums";
|
|||||||
import { toCamelCase, toTitleCase } from "#utils/strings";
|
import { toCamelCase, toTitleCase } from "#utils/strings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { applyChallenges } from "#utils/challenge-utils";
|
import { applyChallenges } from "#utils/challenge-utils";
|
||||||
|
import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
||||||
import type { AbstractConstructor } from "#types/type-helpers";
|
import type { AbstractConstructor } from "#types/type-helpers";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -879,6 +879,8 @@ export abstract class Move implements Localizable {
|
|||||||
power.value *= 1.5;
|
power.value *= 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
power.value *= (source.getTag(BattlerTagType.SUPREME_OVERLORD) as SupremeOverlordTag | undefined)?.getBoost() ?? 1;
|
||||||
|
|
||||||
return power.value;
|
return power.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -888,6 +890,10 @@ export abstract class Move implements Localizable {
|
|||||||
applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority);
|
applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority);
|
||||||
applyAbAttrs("ChangeMovePriorityAbAttr", {pokemon: user, simulated, move: this, priority});
|
applyAbAttrs("ChangeMovePriorityAbAttr", {pokemon: user, simulated, move: this, priority});
|
||||||
|
|
||||||
|
if (user.getTag(BattlerTagType.BYPASS_SPEED)) {
|
||||||
|
priority.value += 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
return priority.value;
|
return priority.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2147,24 +2153,15 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't know which party member will be chosen, so pick the highest max HP in the party
|
// Add a tag to the field if it doesn't already exist, then queue a delayed healing effect in the user's current slot.
|
||||||
const party = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty();
|
globalScene.arena.addTag(ArenaTagType.PENDING_HEAL, 0, move.id, user.id); // Arguments after first go completely unused
|
||||||
const maxPartyMemberHp = party.map(p => p.getMaxHp()).reduce((maxHp: number, hp: number) => Math.max(hp, maxHp), 0);
|
const tag = globalScene.arena.getTag(ArenaTagType.PENDING_HEAL) as PendingHealTag;
|
||||||
|
tag.queueHeal(user.getBattlerIndex(), {
|
||||||
const pm = globalScene.phaseManager;
|
sourceId: user.id,
|
||||||
|
moveId: move.id,
|
||||||
pm.pushPhase(
|
restorePP: this.restorePP,
|
||||||
pm.create("PokemonHealPhase",
|
healMessage: i18next.t(this.moveMessage, { pokemonName: getPokemonNameWithAffix(user) }),
|
||||||
user.getBattlerIndex(),
|
});
|
||||||
maxPartyMemberHp,
|
|
||||||
i18next.t(this.moveMessage, { pokemonName: getPokemonNameWithAffix(user) }),
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
this.restorePP),
|
|
||||||
true);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3304,7 +3301,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
|
|||||||
|
|
||||||
const overridden = args[0] as BooleanHolder;
|
const overridden = args[0] as BooleanHolder;
|
||||||
|
|
||||||
const allyMovePhase = globalScene.phaseManager.findPhase<MovePhase>((phase) => phase.is("MovePhase") && phase.pokemon.isPlayer() === user.isPlayer());
|
const allyMovePhase = globalScene.phaseManager.getMovePhase((phase) => phase.pokemon.isPlayer() === user.isPlayer());
|
||||||
if (allyMovePhase) {
|
if (allyMovePhase) {
|
||||||
const allyMove = allyMovePhase.move.getMove();
|
const allyMove = allyMovePhase.move.getMove();
|
||||||
if (allyMove !== move && allyMove.hasAttr("AwaitCombinedPledgeAttr")) {
|
if (allyMove !== move && allyMove.hasAttr("AwaitCombinedPledgeAttr")) {
|
||||||
@ -3317,11 +3314,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Move the ally's MovePhase (if needed) so that the ally moves next
|
// Move the ally's MovePhase (if needed) so that the ally moves next
|
||||||
const allyMovePhaseIndex = globalScene.phaseManager.phaseQueue.indexOf(allyMovePhase);
|
globalScene.phaseManager.forceMoveNext((phase: MovePhase) => phase.pokemon === user.getAlly());
|
||||||
const firstMovePhaseIndex = globalScene.phaseManager.phaseQueue.findIndex((phase) => phase.is("MovePhase"));
|
|
||||||
if (allyMovePhaseIndex !== firstMovePhaseIndex) {
|
|
||||||
globalScene.phaseManager.prependToPhase(globalScene.phaseManager.phaseQueue.splice(allyMovePhaseIndex, 1)[0], "MovePhase");
|
|
||||||
}
|
|
||||||
|
|
||||||
overridden.value = true;
|
overridden.value = true;
|
||||||
return true;
|
return true;
|
||||||
@ -4556,28 +4549,7 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr {
|
|||||||
*/
|
*/
|
||||||
apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean {
|
apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean {
|
||||||
const power = args[0] as NumberHolder;
|
const power = args[0] as NumberHolder;
|
||||||
const enemy = user.getOpponent(0);
|
for (const p of globalScene.phaseManager.dynamicQueueManager.getLastTurnOrder().slice(0, -1).reverse()) {
|
||||||
const pokemonActed: Pokemon[] = [];
|
|
||||||
|
|
||||||
if (enemy?.turnData.acted) {
|
|
||||||
pokemonActed.push(enemy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (globalScene.currentBattle.double) {
|
|
||||||
const userAlly = user.getAlly();
|
|
||||||
const enemyAlly = enemy?.getAlly();
|
|
||||||
|
|
||||||
if (userAlly?.turnData.acted) {
|
|
||||||
pokemonActed.push(userAlly);
|
|
||||||
}
|
|
||||||
if (enemyAlly?.turnData.acted) {
|
|
||||||
pokemonActed.push(enemyAlly);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pokemonActed.sort((a, b) => b.turnData.order - a.turnData.order);
|
|
||||||
|
|
||||||
for (const p of pokemonActed) {
|
|
||||||
const [ lastMove ] = p.getLastXMoves(1);
|
const [ lastMove ] = p.getLastXMoves(1);
|
||||||
if (lastMove.result !== MoveResult.FAIL) {
|
if (lastMove.result !== MoveResult.FAIL) {
|
||||||
if ((lastMove.result === MoveResult.SUCCESS) && (lastMove.move === this.move)) {
|
if ((lastMove.result === MoveResult.SUCCESS) && (lastMove.move === this.move)) {
|
||||||
@ -4659,20 +4631,13 @@ export class CueNextRoundAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean {
|
override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean {
|
||||||
const nextRoundPhase = globalScene.phaseManager.findPhase<MovePhase>(phase =>
|
const nextRoundPhase = globalScene.phaseManager.getMovePhase(phase => phase.move.moveId === MoveId.ROUND);
|
||||||
phase.is("MovePhase") && phase.move.moveId === MoveId.ROUND
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!nextRoundPhase) {
|
if (!nextRoundPhase) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the phase queue so that the next Pokemon using Round moves next
|
globalScene.phaseManager.forceMoveNext(phase => phase.move.moveId === MoveId.ROUND);
|
||||||
const nextRoundIndex = globalScene.phaseManager.phaseQueue.indexOf(nextRoundPhase);
|
|
||||||
const nextMoveIndex = globalScene.phaseManager.phaseQueue.findIndex(phase => phase.is("MovePhase"));
|
|
||||||
if (nextRoundIndex !== nextMoveIndex) {
|
|
||||||
globalScene.phaseManager.prependToPhase(globalScene.phaseManager.phaseQueue.splice(nextRoundIndex, 1)[0], "MovePhase");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the corresponding Pokemon as having "joined the Round" (for doubling power later)
|
// Mark the corresponding Pokemon as having "joined the Round" (for doubling power later)
|
||||||
nextRoundPhase.pokemon.turnData.joinedRound = true;
|
nextRoundPhase.pokemon.turnData.joinedRound = true;
|
||||||
@ -6297,11 +6262,11 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
|
|||||||
// Handle cases where revived pokemon needs to get switched in on same turn
|
// Handle cases where revived pokemon needs to get switched in on same turn
|
||||||
if (allyPokemon.isFainted() || allyPokemon === pokemon) {
|
if (allyPokemon.isFainted() || allyPokemon === pokemon) {
|
||||||
// Enemy switch phase should be removed and replaced with the revived pkmn switching in
|
// Enemy switch phase should be removed and replaced with the revived pkmn switching in
|
||||||
globalScene.phaseManager.tryRemovePhase((phase: SwitchSummonPhase) => phase.is("SwitchSummonPhase") && phase.getPokemon() === pokemon);
|
globalScene.phaseManager.tryRemovePhase("SwitchSummonPhase", phase => phase.getFieldIndex() === slotIndex);
|
||||||
// If the pokemon being revived was alive earlier in the turn, cancel its move
|
// If the pokemon being revived was alive earlier in the turn, cancel its move
|
||||||
// (revived pokemon can't move in the turn they're brought back)
|
// (revived pokemon can't move in the turn they're brought back)
|
||||||
// TODO: might make sense to move this to `FaintPhase` after checking for Rev Seed (rather than handling it in the move)
|
// TODO: might make sense to move this to `FaintPhase` after checking for Rev Seed (rather than handling it in the move)
|
||||||
globalScene.phaseManager.findPhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel();
|
globalScene.phaseManager.getMovePhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel();
|
||||||
if (user.fieldPosition === FieldPosition.CENTER) {
|
if (user.fieldPosition === FieldPosition.CENTER) {
|
||||||
user.setFieldPosition(FieldPosition.LEFT);
|
user.setFieldPosition(FieldPosition.LEFT);
|
||||||
}
|
}
|
||||||
@ -6382,8 +6347,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
if (this.switchType === SwitchType.FORCE_SWITCH) {
|
if (this.switchType === SwitchType.FORCE_SWITCH) {
|
||||||
switchOutTarget.leaveField(true);
|
switchOutTarget.leaveField(true);
|
||||||
const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)];
|
const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)];
|
||||||
globalScene.phaseManager.prependNewToPhase(
|
globalScene.phaseManager.queueDeferred(
|
||||||
"MoveEndPhase",
|
|
||||||
"SwitchSummonPhase",
|
"SwitchSummonPhase",
|
||||||
this.switchType,
|
this.switchType,
|
||||||
switchOutTarget.getFieldIndex(),
|
switchOutTarget.getFieldIndex(),
|
||||||
@ -6393,7 +6357,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||||
globalScene.phaseManager.prependNewToPhase("MoveEndPhase",
|
globalScene.phaseManager.queueDeferred(
|
||||||
"SwitchPhase",
|
"SwitchPhase",
|
||||||
this.switchType,
|
this.switchType,
|
||||||
switchOutTarget.getFieldIndex(),
|
switchOutTarget.getFieldIndex(),
|
||||||
@ -6422,7 +6386,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
if (this.switchType === SwitchType.FORCE_SWITCH) {
|
if (this.switchType === SwitchType.FORCE_SWITCH) {
|
||||||
switchOutTarget.leaveField(true);
|
switchOutTarget.leaveField(true);
|
||||||
const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)];
|
const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)];
|
||||||
globalScene.phaseManager.prependNewToPhase("MoveEndPhase",
|
globalScene.phaseManager.queueDeferred(
|
||||||
"SwitchSummonPhase",
|
"SwitchSummonPhase",
|
||||||
this.switchType,
|
this.switchType,
|
||||||
switchOutTarget.getFieldIndex(),
|
switchOutTarget.getFieldIndex(),
|
||||||
@ -6432,7 +6396,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||||
globalScene.phaseManager.prependNewToPhase("MoveEndPhase",
|
globalScene.phaseManager.queueDeferred(
|
||||||
"SwitchSummonPhase",
|
"SwitchSummonPhase",
|
||||||
this.switchType,
|
this.switchType,
|
||||||
switchOutTarget.getFieldIndex(),
|
switchOutTarget.getFieldIndex(),
|
||||||
@ -6863,7 +6827,7 @@ class CallMoveAttr extends OverrideMoveEffectAttr {
|
|||||||
: moveTargets.targets[user.randBattleSeedInt(moveTargets.targets.length)]];
|
: moveTargets.targets[user.randBattleSeedInt(moveTargets.targets.length)]];
|
||||||
|
|
||||||
globalScene.phaseManager.unshiftNew("LoadMoveAnimPhase", move.id);
|
globalScene.phaseManager.unshiftNew("LoadMoveAnimPhase", move.id);
|
||||||
globalScene.phaseManager.unshiftNew("MovePhase", user, targets, new PokemonMove(move.id), MoveUseMode.FOLLOW_UP);
|
globalScene.phaseManager.unshiftNew("MovePhase", user, targets, new PokemonMove(move.id), MoveUseMode.FOLLOW_UP, MovePhaseTimingModifier.FIRST);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7095,7 +7059,7 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr {
|
|||||||
|
|
||||||
// Load the move's animation if we didn't already and unshift a new usage phase
|
// Load the move's animation if we didn't already and unshift a new usage phase
|
||||||
globalScene.phaseManager.unshiftNew("LoadMoveAnimPhase", moveId);
|
globalScene.phaseManager.unshiftNew("LoadMoveAnimPhase", moveId);
|
||||||
globalScene.phaseManager.unshiftNew("MovePhase", user, [ target.getBattlerIndex() ], new PokemonMove(moveId), MoveUseMode.FOLLOW_UP);
|
globalScene.phaseManager.unshiftNew("MovePhase", user, [ target.getBattlerIndex() ], new PokemonMove(moveId), MoveUseMode.FOLLOW_UP, MovePhaseTimingModifier.FIRST);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7179,7 +7143,7 @@ export class RepeatMoveAttr extends MoveEffectAttr {
|
|||||||
targetPokemonName: getPokemonNameWithAffix(target)
|
targetPokemonName: getPokemonNameWithAffix(target)
|
||||||
}));
|
}));
|
||||||
target.turnData.extraTurns++;
|
target.turnData.extraTurns++;
|
||||||
globalScene.phaseManager.appendNewToPhase("MoveEndPhase", "MovePhase", target, moveTargets, movesetMove, MoveUseMode.NORMAL);
|
globalScene.phaseManager.unshiftNew("MovePhase", target, moveTargets, movesetMove, MoveUseMode.NORMAL, MovePhaseTimingModifier.FIRST);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7952,12 +7916,7 @@ export class AfterYouAttr extends MoveEffectAttr {
|
|||||||
*/
|
*/
|
||||||
override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
|
override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
|
||||||
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:afterYou", { targetName: getPokemonNameWithAffix(target) }));
|
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:afterYou", { targetName: getPokemonNameWithAffix(target) }));
|
||||||
|
globalScene.phaseManager.forceMoveNext((phase: MovePhase) => phase.pokemon === target);
|
||||||
// Will find next acting phase of the targeted pokémon, delete it and queue it right after us.
|
|
||||||
const targetNextPhase = globalScene.phaseManager.findPhase<MovePhase>(phase => phase.pokemon === target);
|
|
||||||
if (targetNextPhase && globalScene.phaseManager.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) {
|
|
||||||
globalScene.phaseManager.prependToPhase(targetNextPhase, "MovePhase");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -7980,45 +7939,11 @@ export class ForceLastAttr extends MoveEffectAttr {
|
|||||||
override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
|
override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
|
||||||
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) }));
|
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) }));
|
||||||
|
|
||||||
// TODO: Refactor this to be more readable and less janky
|
globalScene.phaseManager.forceMoveLast((phase: MovePhase) => phase.pokemon === target);
|
||||||
const targetMovePhase = globalScene.phaseManager.findPhase<MovePhase>((phase) => phase.pokemon === target);
|
|
||||||
if (targetMovePhase && !targetMovePhase.isForcedLast() && globalScene.phaseManager.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) {
|
|
||||||
// Finding the phase to insert the move in front of -
|
|
||||||
// Either the end of the turn or in front of another, slower move which has also been forced last
|
|
||||||
const prependPhase = globalScene.phaseManager.findPhase((phase) =>
|
|
||||||
[ MovePhase, MoveEndPhase ].every(cls => !(phase instanceof cls))
|
|
||||||
|| (phase.is("MovePhase")) && phaseForcedSlower(phase, target, !!globalScene.arena.getTag(ArenaTagType.TRICK_ROOM))
|
|
||||||
);
|
|
||||||
if (prependPhase) {
|
|
||||||
globalScene.phaseManager.phaseQueue.splice(
|
|
||||||
globalScene.phaseManager.phaseQueue.indexOf(prependPhase),
|
|
||||||
0,
|
|
||||||
globalScene.phaseManager.create("MovePhase", target, [ ...targetMovePhase.targets ], targetMovePhase.move, targetMovePhase.useMode, true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a {@linkcode MovePhase} has been forced last and the corresponding pokemon is slower than {@linkcode target}.
|
|
||||||
|
|
||||||
* TODO:
|
|
||||||
- Make this a class method
|
|
||||||
- Make this look at speed order from TurnStartPhase
|
|
||||||
*/
|
|
||||||
const phaseForcedSlower = (phase: MovePhase, target: Pokemon, trickRoom: boolean): boolean => {
|
|
||||||
let slower: boolean;
|
|
||||||
// quashed pokemon still have speed ties
|
|
||||||
if (phase.pokemon.getEffectiveStat(Stat.SPD) === target.getEffectiveStat(Stat.SPD)) {
|
|
||||||
slower = !!target.randBattleSeedInt(2);
|
|
||||||
} else {
|
|
||||||
slower = !trickRoom ? phase.pokemon.getEffectiveStat(Stat.SPD) < target.getEffectiveStat(Stat.SPD) : phase.pokemon.getEffectiveStat(Stat.SPD) > target.getEffectiveStat(Stat.SPD);
|
|
||||||
}
|
|
||||||
return phase.isForcedLast() && slower;
|
|
||||||
};
|
|
||||||
|
|
||||||
const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !globalScene.arena.getTag(ArenaTagType.GRAVITY);
|
const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !globalScene.arena.getTag(ArenaTagType.GRAVITY);
|
||||||
|
|
||||||
const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune();
|
const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune();
|
||||||
@ -8042,7 +7967,7 @@ const userSleptOrComatoseCondition: MoveConditionFunc = (user) => user.status?.e
|
|||||||
|
|
||||||
const targetSleptOrComatoseCondition: MoveConditionFunc = (_user: Pokemon, target: Pokemon, _move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(AbilityId.COMATOSE);
|
const targetSleptOrComatoseCondition: MoveConditionFunc = (_user: Pokemon, target: Pokemon, _move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(AbilityId.COMATOSE);
|
||||||
|
|
||||||
const failIfLastCondition: MoveConditionFunc = () => globalScene.phaseManager.findPhase(phase => phase.is("MovePhase")) !== undefined;
|
const failIfLastCondition: MoveConditionFunc = () => globalScene.phaseManager.hasPhaseOfType("MovePhase");
|
||||||
|
|
||||||
const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => {
|
const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => {
|
||||||
const party: Pokemon[] = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty();
|
const party: Pokemon[] = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty();
|
||||||
|
@ -414,7 +414,7 @@ function summonPlayerPokemonAnimation(pokemon: PlayerPokemon): Promise<void> {
|
|||||||
pokemon.resetTurnData();
|
pokemon.resetTurnData();
|
||||||
|
|
||||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
||||||
globalScene.phaseManager.pushNew("PostSummonPhase", pokemon.getBattlerIndex());
|
globalScene.phaseManager.unshiftNew("PostSummonPhase", pokemon.getBattlerIndex());
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -669,7 +669,6 @@ function onGameOver() {
|
|||||||
|
|
||||||
// Clear any leftover battle phases
|
// Clear any leftover battle phases
|
||||||
globalScene.phaseManager.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue();
|
||||||
globalScene.phaseManager.clearPhaseQueueSplice();
|
|
||||||
|
|
||||||
// Return enemy Pokemon
|
// Return enemy Pokemon
|
||||||
const pokemon = globalScene.getEnemyPokemon();
|
const pokemon = globalScene.getEnemyPokemon();
|
||||||
|
@ -738,7 +738,7 @@ export function setEncounterRewards(
|
|||||||
if (customShopRewards) {
|
if (customShopRewards) {
|
||||||
globalScene.phaseManager.unshiftNew("SelectModifierPhase", 0, undefined, customShopRewards);
|
globalScene.phaseManager.unshiftNew("SelectModifierPhase", 0, undefined, customShopRewards);
|
||||||
} else {
|
} else {
|
||||||
globalScene.phaseManager.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase"));
|
globalScene.phaseManager.removeAllPhasesOfType("MysteryEncounterRewardsPhase");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eggRewards) {
|
if (eggRewards) {
|
||||||
@ -812,8 +812,7 @@ export function leaveEncounterWithoutBattle(
|
|||||||
encounterMode: MysteryEncounterMode = MysteryEncounterMode.NO_BATTLE,
|
encounterMode: MysteryEncounterMode = MysteryEncounterMode.NO_BATTLE,
|
||||||
) {
|
) {
|
||||||
globalScene.currentBattle.mysteryEncounter!.encounterMode = encounterMode;
|
globalScene.currentBattle.mysteryEncounter!.encounterMode = encounterMode;
|
||||||
globalScene.phaseManager.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue(true);
|
||||||
globalScene.phaseManager.clearPhaseQueueSplice();
|
|
||||||
handleMysteryEncounterVictory(addHealPhase);
|
handleMysteryEncounterVictory(addHealPhase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -826,7 +825,7 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu
|
|||||||
const allowedPkm = globalScene.getPlayerParty().filter(pkm => pkm.isAllowedInBattle());
|
const allowedPkm = globalScene.getPlayerParty().filter(pkm => pkm.isAllowedInBattle());
|
||||||
|
|
||||||
if (allowedPkm.length === 0) {
|
if (allowedPkm.length === 0) {
|
||||||
globalScene.phaseManager.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue(true);
|
||||||
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -869,7 +868,7 @@ export function handleMysteryEncounterBattleFailed(addHealPhase = false, doNotCo
|
|||||||
const allowedPkm = globalScene.getPlayerParty().filter(pkm => pkm.isAllowedInBattle());
|
const allowedPkm = globalScene.getPlayerParty().filter(pkm => pkm.isAllowedInBattle());
|
||||||
|
|
||||||
if (allowedPkm.length === 0) {
|
if (allowedPkm.length === 0) {
|
||||||
globalScene.phaseManager.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue(true);
|
||||||
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
|
||||||
import type { Phase } from "#app/phase";
|
|
||||||
import { TrickRoomTag } from "#data/arena-tag";
|
|
||||||
import { DynamicPhaseType } from "#enums/dynamic-phase-type";
|
|
||||||
import { Stat } from "#enums/stat";
|
|
||||||
import { ActivatePriorityQueuePhase } from "#phases/activate-priority-queue-phase";
|
|
||||||
import { PostSummonActivateAbilityPhase } from "#phases/post-summon-activate-ability-phase";
|
|
||||||
import type { PostSummonPhase } from "#phases/post-summon-phase";
|
|
||||||
import { BooleanHolder } from "#utils/common";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores a list of {@linkcode Phase}s
|
|
||||||
*
|
|
||||||
* Dynamically updates ordering to always pop the highest "priority", based on implementation of {@linkcode reorder}
|
|
||||||
*/
|
|
||||||
export abstract class PhasePriorityQueue {
|
|
||||||
protected abstract queue: Phase[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sorts the elements in the queue
|
|
||||||
*/
|
|
||||||
public abstract reorder(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls {@linkcode reorder} and shifts the queue
|
|
||||||
* @returns The front element of the queue after sorting
|
|
||||||
*/
|
|
||||||
public pop(): Phase | undefined {
|
|
||||||
this.reorder();
|
|
||||||
return this.queue.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a phase to the queue
|
|
||||||
* @param phase The phase to add
|
|
||||||
*/
|
|
||||||
public push(phase: Phase): void {
|
|
||||||
this.queue.push(phase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all phases from the queue
|
|
||||||
*/
|
|
||||||
public clear(): void {
|
|
||||||
this.queue.splice(0, this.queue.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to remove one or more Phases from the current queue.
|
|
||||||
* @param phaseFilter - The function to select phases for removal
|
|
||||||
* @param removeCount - The maximum number of phases to remove, or `all` to remove all matching phases;
|
|
||||||
* default `1`
|
|
||||||
* @returns The number of successfully removed phases
|
|
||||||
* @todo Remove this eventually once the patchwork bug this is used for is fixed
|
|
||||||
*/
|
|
||||||
public tryRemovePhase(phaseFilter: (phase: Phase) => boolean, removeCount: number | "all" = 1): number {
|
|
||||||
if (removeCount === "all") {
|
|
||||||
removeCount = this.queue.length;
|
|
||||||
} else if (removeCount < 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
let numRemoved = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
const phaseIndex = this.queue.findIndex(phaseFilter);
|
|
||||||
if (phaseIndex === -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.queue.splice(phaseIndex, 1);
|
|
||||||
numRemoved++;
|
|
||||||
} while (numRemoved < removeCount && this.queue.length > 0);
|
|
||||||
|
|
||||||
return numRemoved;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Priority Queue for {@linkcode PostSummonPhase} and {@linkcode PostSummonActivateAbilityPhase}
|
|
||||||
*
|
|
||||||
* Orders phases first by ability priority, then by the {@linkcode Pokemon}'s effective speed
|
|
||||||
*/
|
|
||||||
export class PostSummonPhasePriorityQueue extends PhasePriorityQueue {
|
|
||||||
protected override queue: PostSummonPhase[] = [];
|
|
||||||
|
|
||||||
public override reorder(): void {
|
|
||||||
this.queue.sort((phaseA: PostSummonPhase, phaseB: PostSummonPhase) => {
|
|
||||||
if (phaseA.getPriority() === phaseB.getPriority()) {
|
|
||||||
return (
|
|
||||||
(phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD))
|
|
||||||
* (isTrickRoom() ? -1 : 1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return phaseB.getPriority() - phaseA.getPriority();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override push(phase: PostSummonPhase): void {
|
|
||||||
super.push(phase);
|
|
||||||
this.queueAbilityPhase(phase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queues all necessary {@linkcode PostSummonActivateAbilityPhase}s for each pushed {@linkcode PostSummonPhase}
|
|
||||||
* @param phase The {@linkcode PostSummonPhase} that was pushed onto the queue
|
|
||||||
*/
|
|
||||||
private queueAbilityPhase(phase: PostSummonPhase): void {
|
|
||||||
const phasePokemon = phase.getPokemon();
|
|
||||||
|
|
||||||
phasePokemon.getAbilityPriorities().forEach((priority, idx) => {
|
|
||||||
this.queue.push(new PostSummonActivateAbilityPhase(phasePokemon.getBattlerIndex(), priority, !!idx));
|
|
||||||
globalScene.phaseManager.appendToPhase(
|
|
||||||
new ActivatePriorityQueuePhase(DynamicPhaseType.POST_SUMMON),
|
|
||||||
"ActivatePriorityQueuePhase",
|
|
||||||
(p: ActivatePriorityQueuePhase) => p.getType() === DynamicPhaseType.POST_SUMMON,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTrickRoom(): boolean {
|
|
||||||
const speedReversed = new BooleanHolder(false);
|
|
||||||
globalScene.arena.applyTags(TrickRoomTag, false, speedReversed);
|
|
||||||
return speedReversed.value;
|
|
||||||
}
|
|
@ -22,10 +22,12 @@ export interface SerializedTerrain {
|
|||||||
export class Terrain {
|
export class Terrain {
|
||||||
public terrainType: TerrainType;
|
public terrainType: TerrainType;
|
||||||
public turnsLeft: number;
|
public turnsLeft: number;
|
||||||
|
public maxDuration: number;
|
||||||
|
|
||||||
constructor(terrainType: TerrainType, turnsLeft?: number) {
|
constructor(terrainType: TerrainType, turnsLeft = 0, maxDuration: number = turnsLeft) {
|
||||||
this.terrainType = terrainType;
|
this.terrainType = terrainType;
|
||||||
this.turnsLeft = turnsLeft || 0;
|
this.turnsLeft = turnsLeft;
|
||||||
|
this.maxDuration = maxDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
lapse(): boolean {
|
lapse(): boolean {
|
||||||
|
@ -19,10 +19,12 @@ export interface SerializedWeather {
|
|||||||
export class Weather {
|
export class Weather {
|
||||||
public weatherType: WeatherType;
|
public weatherType: WeatherType;
|
||||||
public turnsLeft: number;
|
public turnsLeft: number;
|
||||||
|
public maxDuration: number;
|
||||||
|
|
||||||
constructor(weatherType: WeatherType, turnsLeft?: number) {
|
constructor(weatherType: WeatherType, turnsLeft = 0, maxDuration: number = turnsLeft) {
|
||||||
this.weatherType = weatherType;
|
this.weatherType = weatherType;
|
||||||
this.turnsLeft = !this.isImmutable() ? turnsLeft || 0 : 0;
|
this.turnsLeft = this.isImmutable() ? 0 : turnsLeft;
|
||||||
|
this.maxDuration = this.isImmutable() ? 0 : maxDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
lapse(): boolean {
|
lapse(): boolean {
|
||||||
|
187
src/dynamic-queue-manager.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import type { DynamicPhase, PhaseConditionFunc, PhaseString } from "#app/@types/phase-types";
|
||||||
|
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
import type { Pokemon } from "#app/field/pokemon";
|
||||||
|
import type { Phase } from "#app/phase";
|
||||||
|
import type { MovePhase } from "#app/phases/move-phase";
|
||||||
|
import { MovePhasePriorityQueue } from "#app/queues/move-phase-priority-queue";
|
||||||
|
import { PokemonPhasePriorityQueue } from "#app/queues/pokemon-phase-priority-queue";
|
||||||
|
import { PostSummonPhasePriorityQueue } from "#app/queues/post-summon-phase-priority-queue";
|
||||||
|
import type { PriorityQueue } from "#app/queues/priority-queue";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import type { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier";
|
||||||
|
|
||||||
|
// TODO: might be easier to define which phases should be dynamic instead
|
||||||
|
/** All phases which have defined a `getPokemon` method but should not be sorted dynamically */
|
||||||
|
const nonDynamicPokemonPhases: readonly PhaseString[] = [
|
||||||
|
"SummonPhase",
|
||||||
|
"CommandPhase",
|
||||||
|
"LearnMovePhase",
|
||||||
|
"MoveEffectPhase",
|
||||||
|
"MoveEndPhase",
|
||||||
|
"FaintPhase",
|
||||||
|
"DamageAnimPhase",
|
||||||
|
"VictoryPhase",
|
||||||
|
"PokemonHealPhase",
|
||||||
|
"WeatherEffectPhase",
|
||||||
|
"ShowAbilityPhase",
|
||||||
|
"HideAbilityPhase",
|
||||||
|
"ExpPhase",
|
||||||
|
"ShowPartyExpBarPhase",
|
||||||
|
"HidePartyExpBarPhase",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dynamic queue manager holds priority queues for phases which are queued as dynamic.
|
||||||
|
*
|
||||||
|
* Dynamic phases are generally those which hold a pokemon and are unshifted, not pushed. \
|
||||||
|
* Queues work by sorting their entries in speed order (and possibly with more complex ordering) before each time a phase is popped.
|
||||||
|
*
|
||||||
|
* As the holder, this structure is also used to access and modify queued phases.
|
||||||
|
* This is mostly used in redirection, cancellation, etc. of {@linkcode MovePhase}s.
|
||||||
|
*/
|
||||||
|
export class DynamicQueueManager {
|
||||||
|
/** Maps phase types to their corresponding queues */
|
||||||
|
private readonly dynamicPhaseMap: Map<PhaseString, PriorityQueue<Phase>>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.dynamicPhaseMap = new Map();
|
||||||
|
// PostSummon and Move phases have specialized queues
|
||||||
|
this.dynamicPhaseMap.set("PostSummonPhase", new PostSummonPhasePriorityQueue());
|
||||||
|
this.dynamicPhaseMap.set("MovePhase", new MovePhasePriorityQueue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Removes all phases from the manager */
|
||||||
|
public clearQueues(): void {
|
||||||
|
for (const queue of this.dynamicPhaseMap.values()) {
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new phase to the manager and creates the priority queue for it if one does not exist.
|
||||||
|
* @param phase - The {@linkcode Phase} to add
|
||||||
|
* @returns `true` if the phase was added, or `false` if it is not dynamic
|
||||||
|
*/
|
||||||
|
public queueDynamicPhase<T extends Phase>(phase: T): boolean {
|
||||||
|
if (!this.isDynamicPhase(phase)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.dynamicPhaseMap.has(phase.phaseName)) {
|
||||||
|
// TS can't figure out that T is dynamic at this point, but it does know that `typeof phase` is
|
||||||
|
this.dynamicPhaseMap.set(phase.phaseName, new PokemonPhasePriorityQueue<typeof phase>());
|
||||||
|
}
|
||||||
|
this.dynamicPhaseMap.get(phase.phaseName)?.push(phase);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the highest-priority (generally by speed) {@linkcode Phase} of the specified type
|
||||||
|
* @param type - The {@linkcode PhaseString | type} to pop
|
||||||
|
* @returns The popped {@linkcode Phase}, or `undefined` if none of the specified type exist
|
||||||
|
*/
|
||||||
|
public popNextPhase(type: PhaseString): Phase | undefined {
|
||||||
|
return this.dynamicPhaseMap.get(type)?.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if there is a queued dynamic {@linkcode Phase} meeting the conditions
|
||||||
|
* @param type - The {@linkcode PhaseString | type} of phase to search for
|
||||||
|
* @param condition - An optional {@linkcode PhaseConditionFunc} to add conditions to the search
|
||||||
|
* @returns Whether a matching phase exists
|
||||||
|
*/
|
||||||
|
public exists<T extends PhaseString>(type: T, condition?: PhaseConditionFunc<T>): boolean {
|
||||||
|
return !!this.dynamicPhaseMap.get(type)?.has(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds and removes a single queued {@linkcode Phase}
|
||||||
|
* @param type - The {@linkcode PhaseString | type} of phase to search for
|
||||||
|
* @param phaseFilter - A {@linkcode PhaseConditionFunc} to specify conditions for the phase
|
||||||
|
* @returns Whether a removal occurred
|
||||||
|
*/
|
||||||
|
public removePhase<T extends PhaseString>(type: T, condition?: PhaseConditionFunc<T>): boolean {
|
||||||
|
return !!this.dynamicPhaseMap.get(type)?.remove(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timing modifier of a move (i.e. to force it first or last)
|
||||||
|
* @param condition - A {@linkcode PhaseConditionFunc} to specify conditions for the move
|
||||||
|
* @param modifier - The {@linkcode MovePhaseTimingModifier} to switch the move to
|
||||||
|
*/
|
||||||
|
public setMoveTimingModifier(condition: PhaseConditionFunc<"MovePhase">, modifier: MovePhaseTimingModifier): void {
|
||||||
|
this.getMovePhaseQueue().setTimingModifier(condition, modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the {@linkcode MovePhase} meeting the condition and changes its move
|
||||||
|
* @param phaseCondition - The {@linkcode PhaseConditionFunc | condition} function
|
||||||
|
* @param move - The {@linkcode PokemonMove | move} to use in replacement
|
||||||
|
*/
|
||||||
|
public setMoveForPhase(condition: PhaseConditionFunc<"MovePhase">, move: PokemonMove): void {
|
||||||
|
this.getMovePhaseQueue().setMoveForPhase(condition, move);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects moves which were targeted at a {@linkcode Pokemon} that has been removed
|
||||||
|
* @param removedPokemon - The removed {@linkcode Pokemon}
|
||||||
|
* @param allyPokemon - The ally of the removed pokemon
|
||||||
|
*/
|
||||||
|
public redirectMoves(removedPokemon: Pokemon, allyPokemon: Pokemon): void {
|
||||||
|
this.getMovePhaseQueue().redirectMoves(removedPokemon, allyPokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a {@linkcode MovePhase} meeting the condition
|
||||||
|
* @param phaseCondition - The {@linkcode PhaseConditionFunc | condition} function
|
||||||
|
* @returns The MovePhase, or `undefined` if it does not exist
|
||||||
|
*/
|
||||||
|
public getMovePhase(condition: PhaseConditionFunc<"MovePhase">): MovePhase | undefined {
|
||||||
|
return this.getMovePhaseQueue().find(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds and cancels a {@linkcode MovePhase} meeting the condition
|
||||||
|
* @param phaseCondition - The {@linkcode PhaseConditionFunc | condition} function
|
||||||
|
*/
|
||||||
|
public cancelMovePhase(condition: PhaseConditionFunc<"MovePhase">): void {
|
||||||
|
this.getMovePhaseQueue().cancelMove(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the move order to a static array rather than a dynamic queue
|
||||||
|
* @param order - The order of {@linkcode BattlerIndex}s
|
||||||
|
*/
|
||||||
|
public setMoveOrder(order: BattlerIndex[]): void {
|
||||||
|
this.getMovePhaseQueue().setMoveOrder(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns An in-order array of {@linkcode Pokemon}, representing the turn order as played out in the most recent turn
|
||||||
|
*/
|
||||||
|
public getLastTurnOrder(): Pokemon[] {
|
||||||
|
return this.getMovePhaseQueue().getTurnOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears the stored `Move` turn order */
|
||||||
|
public clearLastTurnOrder(): void {
|
||||||
|
this.getMovePhaseQueue().clearTurnOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Internal helper to get the {@linkcode MovePhasePriorityQueue} */
|
||||||
|
private getMovePhaseQueue(): MovePhasePriorityQueue {
|
||||||
|
return this.dynamicPhaseMap.get("MovePhase") as MovePhasePriorityQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal helper to determine if a phase is dynamic.
|
||||||
|
* @param phase - The {@linkcode Phase} to check
|
||||||
|
* @returns Whether `phase` is dynamic
|
||||||
|
* @privateRemarks
|
||||||
|
* Currently, this checks that `phase` has a `getPokemon` method
|
||||||
|
* and is not blacklisted in `nonDynamicPokemonPhases`.
|
||||||
|
*/
|
||||||
|
private isDynamicPhase(phase: Phase): phase is DynamicPhase {
|
||||||
|
return typeof (phase as any).getPokemon === "function" && !nonDynamicPokemonPhases.includes(phase.phaseName);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
// TODO: rename to something else (this isn't used only for arena tags)
|
||||||
export enum ArenaTagSide {
|
export enum ArenaTagSide {
|
||||||
BOTH,
|
BOTH,
|
||||||
PLAYER,
|
PLAYER,
|
||||||
|
@ -34,4 +34,5 @@ export enum ArenaTagType {
|
|||||||
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
|
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
|
||||||
FAIRY_LOCK = "FAIRY_LOCK",
|
FAIRY_LOCK = "FAIRY_LOCK",
|
||||||
NEUTRALIZING_GAS = "NEUTRALIZING_GAS",
|
NEUTRALIZING_GAS = "NEUTRALIZING_GAS",
|
||||||
|
PENDING_HEAL = "PENDING_HEAL",
|
||||||
}
|
}
|
||||||
|
@ -94,4 +94,6 @@ export enum BattlerTagType {
|
|||||||
ENDURE_TOKEN = "ENDURE_TOKEN",
|
ENDURE_TOKEN = "ENDURE_TOKEN",
|
||||||
POWDER = "POWDER",
|
POWDER = "POWDER",
|
||||||
MAGIC_COAT = "MAGIC_COAT",
|
MAGIC_COAT = "MAGIC_COAT",
|
||||||
|
SUPREME_OVERLORD = "SUPREME_OVERLORD",
|
||||||
|
BYPASS_SPEED = "BYPASS_SPEED",
|
||||||
}
|
}
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
/**
|
|
||||||
* Enum representation of the phase types held by implementations of {@linkcode PhasePriorityQueue}.
|
|
||||||
*/
|
|
||||||
// TODO: We currently assume these are in order
|
|
||||||
export enum DynamicPhaseType {
|
|
||||||
POST_SUMMON,
|
|
||||||
}
|
|