Merge branch 'beta' into WorkingDiscardFunction
@ -4,7 +4,7 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
name: "only-type-imports",
|
name: "only-type-imports",
|
||||||
severity: "error",
|
severity: "error",
|
||||||
comment: "Files in enums and @types may only use type imports.",
|
comment: "Files in 'enums/' and '@types/' must only use type imports.",
|
||||||
from: {
|
from: {
|
||||||
path: ["(^|/)src/@types", "(^|/)src/enums"],
|
path: ["(^|/)src/@types", "(^|/)src/enums"],
|
||||||
},
|
},
|
||||||
@ -14,7 +14,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no-circular-at-runtime",
|
name: "no-circular-at-runtime",
|
||||||
severity: "warn",
|
severity: "error",
|
||||||
comment:
|
comment:
|
||||||
"This dependency is part of a circular relationship. You might want to revise " +
|
"This dependency is part of a circular relationship. You might want to revise " +
|
||||||
"your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ",
|
"your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ",
|
||||||
@ -34,7 +34,7 @@ module.exports = {
|
|||||||
"add an exception for it in your dependency-cruiser configuration. By default " +
|
"add an exception for it in your dependency-cruiser configuration. By default " +
|
||||||
"this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " +
|
"this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " +
|
||||||
"files (.d.ts), tsconfig.json and some of the babel and webpack configs.",
|
"files (.d.ts), tsconfig.json and some of the babel and webpack configs.",
|
||||||
severity: "warn",
|
severity: "error",
|
||||||
from: {
|
from: {
|
||||||
orphan: true,
|
orphan: true,
|
||||||
pathNot: [
|
pathNot: [
|
||||||
@ -42,8 +42,7 @@ module.exports = {
|
|||||||
"[.]d[.]ts$", // TypeScript declaration files
|
"[.]d[.]ts$", // TypeScript declaration files
|
||||||
"(^|/)tsconfig[.]json$", // TypeScript config
|
"(^|/)tsconfig[.]json$", // TypeScript config
|
||||||
"(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$", // other configs
|
"(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$", // other configs
|
||||||
// anything in src/@types
|
"(^|/)test/.+[.]setup[.]ts", // Vitest setup files
|
||||||
"(^|/)src/@types/",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
to: {},
|
to: {},
|
||||||
@ -53,7 +52,7 @@ module.exports = {
|
|||||||
comment:
|
comment:
|
||||||
"A module depends on a node core module that has been deprecated. Find an alternative - these are " +
|
"A module depends on a node core module that has been deprecated. Find an alternative - these are " +
|
||||||
"bound to exist - node doesn't deprecate lightly.",
|
"bound to exist - node doesn't deprecate lightly.",
|
||||||
severity: "warn",
|
severity: "error",
|
||||||
from: {},
|
from: {},
|
||||||
to: {
|
to: {
|
||||||
dependencyTypes: ["core"],
|
dependencyTypes: ["core"],
|
||||||
@ -86,7 +85,7 @@ module.exports = {
|
|||||||
comment:
|
comment:
|
||||||
"This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later " +
|
"This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later " +
|
||||||
"version of that module, or find an alternative. Deprecated modules are a security risk.",
|
"version of that module, or find an alternative. Deprecated modules are a security risk.",
|
||||||
severity: "warn",
|
severity: "error",
|
||||||
from: {},
|
from: {},
|
||||||
to: {
|
to: {
|
||||||
dependencyTypes: ["deprecated"],
|
dependencyTypes: ["deprecated"],
|
||||||
@ -122,7 +121,7 @@ module.exports = {
|
|||||||
"Likely this module depends on an external ('npm') package that occurs more than once " +
|
"Likely this module depends on an external ('npm') package that occurs more than once " +
|
||||||
"in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " +
|
"in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " +
|
||||||
"maintenance problems later on.",
|
"maintenance problems later on.",
|
||||||
severity: "warn",
|
severity: "error",
|
||||||
from: {},
|
from: {},
|
||||||
to: {
|
to: {
|
||||||
moreThanOneDependencyType: true,
|
moreThanOneDependencyType: true,
|
||||||
@ -133,7 +132,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/* rules you might want to tweak for your specific situation: */
|
// rules you might want to tweak for your specific situation:
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "not-to-spec",
|
name: "not-to-spec",
|
||||||
@ -188,7 +187,7 @@ module.exports = {
|
|||||||
"in your package.json. This makes sense if your package is e.g. a plugin, but in " +
|
"in your package.json. This makes sense if your package is e.g. a plugin, but in " +
|
||||||
"other cases - maybe not so much. If the use of a peer dependency is intentional " +
|
"other cases - maybe not so much. If the use of a peer dependency is intentional " +
|
||||||
"add an exception to your dependency-cruiser configuration.",
|
"add an exception to your dependency-cruiser configuration.",
|
||||||
severity: "warn",
|
severity: "error",
|
||||||
from: {},
|
from: {},
|
||||||
to: {
|
to: {
|
||||||
dependencyTypes: ["npm-peer"],
|
dependencyTypes: ["npm-peer"],
|
||||||
@ -196,6 +195,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
|
exclude: ["src/plugins/vite/*", "src/vite.env.d.ts"],
|
||||||
/* Which modules not to follow further when encountered */
|
/* Which modules not to follow further when encountered */
|
||||||
doNotFollow: {
|
doNotFollow: {
|
||||||
/* path: an array of regular expressions in strings to match against */
|
/* path: an array of regular expressions in strings to match against */
|
||||||
@ -235,7 +235,7 @@ module.exports = {
|
|||||||
true: also detect dependencies that only exist before typescript-to-javascript compilation
|
true: also detect dependencies that only exist before typescript-to-javascript compilation
|
||||||
"specify": for each dependency identify whether it only exists before compilation or also after
|
"specify": for each dependency identify whether it only exists before compilation or also after
|
||||||
*/
|
*/
|
||||||
// tsPreCompilationDeps: false,
|
tsPreCompilationDeps: true,
|
||||||
|
|
||||||
/* list of extensions to scan that aren't javascript or compile-to-javascript.
|
/* list of extensions to scan that aren't javascript or compile-to-javascript.
|
||||||
Empty by default. Only put extensions in here that you want to take into
|
Empty by default. Only put extensions in here that you want to take into
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
VITE_BYPASS_LOGIN=1
|
VITE_BYPASS_LOGIN=1
|
||||||
VITE_BYPASS_TUTORIAL=0
|
VITE_BYPASS_TUTORIAL=0
|
||||||
VITE_SERVER_URL=http://localhost:8001
|
VITE_SERVER_URL=http://localhost:8001
|
||||||
|
# IDs for discord/google auth go unused due to VITE_BYPASS_LOGIN
|
||||||
VITE_DISCORD_CLIENT_ID=1234567890
|
VITE_DISCORD_CLIENT_ID=1234567890
|
||||||
VITE_GOOGLE_CLIENT_ID=1234567890
|
VITE_GOOGLE_CLIENT_ID=1234567890
|
||||||
VITE_I18N_DEBUG=0
|
VITE_I18N_DEBUG=0
|
||||||
|
9
.github/pull_request_template.md
vendored
@ -14,13 +14,16 @@ Make sure the title includes categorization (choose the one that best fits):
|
|||||||
- [Balance]: If the PR is related to game balance
|
- [Balance]: If the PR is related to game balance
|
||||||
- [Challenge]: If the PR is adding or modifying challenges
|
- [Challenge]: If the PR is adding or modifying challenges
|
||||||
- [Refactor]: If the PR is primarily rewriting existing code
|
- [Refactor]: If the PR is primarily rewriting existing code
|
||||||
- [Docs]: If the PR is just adding or modifying documentation (such as tsdocs/code comments)
|
- [Dev]: If the PR is primarily changing something pertaining to development (lefthook hooks, linter rules, etc.)
|
||||||
|
- [i18n]: If the PR is primarily adding/changing locale keys or key usage (may come with an associated locales PR)
|
||||||
|
- [Docs]: If the PR is adding or modifying documentation (such as tsdocs/code comments)
|
||||||
- [GitHub]: For changes to GitHub workflows/templates/etc
|
- [GitHub]: For changes to GitHub workflows/templates/etc
|
||||||
- [Misc]: If no other category fits the PR
|
- [Misc]: If no other category fits the PR
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Make sure that this PR is not overlapping with someone else's work
|
Make sure that this PR is not overlapping with someone else's work
|
||||||
Please try to keep the PR self-contained (and small)
|
Please try to keep the PR self-contained (and small!)
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## What are the changes the user will see?
|
## What are the changes the user will see?
|
||||||
@ -66,7 +69,7 @@ Do the reviewers need to do something special in order to test your changes?
|
|||||||
- [ ] Have I provided a clear explanation of the changes?
|
- [ ] Have I provided a clear explanation of the changes?
|
||||||
- [ ] Have I tested the changes manually?
|
- [ ] Have I tested the changes manually?
|
||||||
- [ ] Are all unit tests still passing? (`npm run test:silent`)
|
- [ ] Are all unit tests still passing? (`npm run test:silent`)
|
||||||
- [ ] Have I created new automated tests (`npm run create-test`) or updated existing tests related to the PR's changes?
|
- [ ] Have I created new automated tests (`npm run test:create`) or updated existing tests related to the PR's changes?
|
||||||
- [ ] Have I provided screenshots/videos of the changes (if applicable)?
|
- [ ] Have I provided screenshots/videos of the changes (if applicable)?
|
||||||
- [ ] Have I made sure that any UI change works for both UI themes (default and legacy)?
|
- [ ] Have I made sure that any UI change works for both UI themes (default and legacy)?
|
||||||
|
|
||||||
|
42
.github/workflows/linting.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
name: Linting
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- beta
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- beta
|
||||||
|
merge_group:
|
||||||
|
types: [checks_requested]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-linters:
|
||||||
|
name: Run linters
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install Node.js dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run ESLint
|
||||||
|
run: npm run eslint-ci
|
||||||
|
|
||||||
|
- name: Lint with Biome
|
||||||
|
run: npm run biome-ci
|
||||||
|
|
||||||
|
- name: Check dependencies with depcruise
|
||||||
|
run: npm run depcruise
|
41
.github/workflows/quality.yml
vendored
@ -1,41 +0,0 @@
|
|||||||
name: Biome Code Quality
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Trigger the workflow on push or pull request,
|
|
||||||
# but only for the main branch
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main # Trigger on push events to the main branch
|
|
||||||
- beta # Trigger on push events to the beta branch
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main # Trigger on pull request events targeting the main branch
|
|
||||||
- beta # Trigger on pull request events targeting the beta branch
|
|
||||||
merge_group:
|
|
||||||
types: [checks_requested]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run-linters: # Define a job named "run-linters"
|
|
||||||
name: Run linters # Human-readable name for the job
|
|
||||||
runs-on: ubuntu-latest # Specify the latest Ubuntu runner for the job
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out Git repository # Step to check out the repository
|
|
||||||
uses: actions/checkout@v4 # Use the checkout action version 4
|
|
||||||
with:
|
|
||||||
submodules: 'recursive'
|
|
||||||
|
|
||||||
- name: Set up Node.js # Step to set up Node.js environment
|
|
||||||
uses: actions/setup-node@v4 # Use the setup-node action version 4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install Node.js dependencies # Step to install Node.js dependencies
|
|
||||||
run: npm ci # Use 'npm ci' to install dependencies
|
|
||||||
|
|
||||||
- name: eslint # Step to run linters
|
|
||||||
run: npm run eslint-ci
|
|
||||||
|
|
||||||
- name: Lint with Biome # Step to run linters
|
|
||||||
run: npm run biome-ci
|
|
33
biome.jsonc
@ -28,10 +28,8 @@
|
|||||||
".vscode/*",
|
".vscode/*",
|
||||||
"*.css", // TODO?
|
"*.css", // TODO?
|
||||||
"*.html", // TODO?
|
"*.html", // TODO?
|
||||||
"src/overrides.ts",
|
|
||||||
// TODO: these files are too big and complex, ignore them until their respective refactors
|
// TODO: these files are too big and complex, ignore them until their respective refactors
|
||||||
"src/data/moves/move.ts",
|
"src/data/moves/move.ts",
|
||||||
"src/data/abilities/ability.ts",
|
|
||||||
|
|
||||||
// this file is just too big:
|
// this file is just too big:
|
||||||
"src/data/balance/tms.ts"
|
"src/data/balance/tms.ts"
|
||||||
@ -42,17 +40,14 @@
|
|||||||
// TODO: Remove if we ever get down to 0 circular imports
|
// TODO: Remove if we ever get down to 0 circular imports
|
||||||
"organizeImports": { "enabled": false },
|
"organizeImports": { "enabled": false },
|
||||||
"linter": {
|
"linter": {
|
||||||
"ignore": [
|
|
||||||
"src/phases/move-effect-phase.ts" // TODO: unignore after move-effect-phase refactor
|
|
||||||
],
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true,
|
"recommended": true,
|
||||||
"correctness": {
|
"correctness": {
|
||||||
"noUndeclaredVariables": "off",
|
"noUndeclaredVariables": "off",
|
||||||
"noUnusedVariables": "error",
|
"noUnusedVariables": "error",
|
||||||
"noSwitchDeclarations": "warn", // TODO: refactor and make this an error
|
"noSwitchDeclarations": "error",
|
||||||
"noVoidTypeReturn": "warn", // TODO: Refactor and make this an error
|
"noVoidTypeReturn": "error",
|
||||||
"noUnusedImports": "error"
|
"noUnusedImports": "error"
|
||||||
},
|
},
|
||||||
"style": {
|
"style": {
|
||||||
@ -89,7 +84,7 @@
|
|||||||
"useLiteralKeys": "off",
|
"useLiteralKeys": "off",
|
||||||
"noForEach": "off", // Foreach vs for of is not that simple.
|
"noForEach": "off", // Foreach vs for of is not that simple.
|
||||||
"noUselessSwitchCase": "off", // Explicit > Implicit
|
"noUselessSwitchCase": "off", // Explicit > Implicit
|
||||||
"noUselessConstructor": "warn", // TODO: Refactor and make this an error
|
"noUselessConstructor": "error",
|
||||||
"noBannedTypes": "warn" // TODO: Refactor and make this an error
|
"noBannedTypes": "warn" // TODO: Refactor and make this an error
|
||||||
},
|
},
|
||||||
"nursery": {
|
"nursery": {
|
||||||
@ -124,6 +119,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Overrides to prevent unused import removal inside `overrides.ts` and enums files (for TSDoc linkcodes)
|
||||||
|
{
|
||||||
|
"include": ["src/overrides.ts", "src/enums/*"],
|
||||||
|
"linter": {
|
||||||
|
"rules": {
|
||||||
|
"correctness": {
|
||||||
|
"noUnusedImports": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"include": ["src/overrides.ts"],
|
||||||
|
"linter": {
|
||||||
|
"rules": {
|
||||||
|
"style": {
|
||||||
|
"useImportType": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,172 +0,0 @@
|
|||||||
/**
|
|
||||||
* This script creates a test boilerplate file in the appropriate
|
|
||||||
* directory based on the type selected.
|
|
||||||
* @example npm run create-test
|
|
||||||
*/
|
|
||||||
|
|
||||||
import fs from "fs";
|
|
||||||
import inquirer from "inquirer";
|
|
||||||
import path from "path";
|
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
|
|
||||||
// Get the directory name of the current module file
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
const typeChoices = ["Move", "Ability", "Item", "Mystery Encounter"];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompts the user to select a type via list.
|
|
||||||
* @returns {Promise<{selectedOption: string}>} the selected type
|
|
||||||
*/
|
|
||||||
async function promptTestType() {
|
|
||||||
const typeAnswer = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: "list",
|
|
||||||
name: "selectedOption",
|
|
||||||
message: "What type of test would you like to create:",
|
|
||||||
choices: [...typeChoices, "EXIT"],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (typeAnswer.selectedOption === "EXIT") {
|
|
||||||
console.log("Exiting...");
|
|
||||||
return process.exit();
|
|
||||||
}
|
|
||||||
if (!typeChoices.includes(typeAnswer.selectedOption)) {
|
|
||||||
console.error(`Please provide a valid type (${typeChoices.join(", ")})!`);
|
|
||||||
return await promptTestType();
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeAnswer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompts the user to provide a file name.
|
|
||||||
* @param {string} selectedType
|
|
||||||
* @returns {Promise<{userInput: string}>} the selected file name
|
|
||||||
*/
|
|
||||||
async function promptFileName(selectedType) {
|
|
||||||
const fileNameAnswer = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
name: "userInput",
|
|
||||||
message: `Please provide the name of the ${selectedType}:`,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!fileNameAnswer.userInput || fileNameAnswer.userInput.trim().length === 0) {
|
|
||||||
console.error("Please provide a valid file name!");
|
|
||||||
return await promptFileName(selectedType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileNameAnswer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the interactive create-test "CLI"
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async function runInteractive() {
|
|
||||||
const typeAnswer = await promptTestType();
|
|
||||||
const fileNameAnswer = await promptFileName(typeAnswer.selectedOption);
|
|
||||||
|
|
||||||
const type = typeAnswer.selectedOption.toLowerCase();
|
|
||||||
// Convert fileName from kebab-case or camelCase to snake_case
|
|
||||||
const fileName = fileNameAnswer.userInput
|
|
||||||
.replace(/-+/g, "_") // Convert kebab-case (dashes) to underscores
|
|
||||||
.replace(/([a-z])([A-Z])/g, "$1_$2") // Convert camelCase to snake_case
|
|
||||||
.replace(/\s+/g, "_") // Replace spaces with underscores
|
|
||||||
.toLowerCase(); // Ensure all lowercase
|
|
||||||
// Format the description for the test case
|
|
||||||
|
|
||||||
const formattedName = fileName.replace(/_/g, " ").replace(/\b\w/g, char => char.toUpperCase());
|
|
||||||
// Determine the directory based on the type
|
|
||||||
let dir;
|
|
||||||
let description;
|
|
||||||
switch (type) {
|
|
||||||
case "move":
|
|
||||||
dir = path.join(__dirname, "test", "moves");
|
|
||||||
description = `Moves - ${formattedName}`;
|
|
||||||
break;
|
|
||||||
case "ability":
|
|
||||||
dir = path.join(__dirname, "test", "abilities");
|
|
||||||
description = `Abilities - ${formattedName}`;
|
|
||||||
break;
|
|
||||||
case "item":
|
|
||||||
dir = path.join(__dirname, "test", "items");
|
|
||||||
description = `Items - ${formattedName}`;
|
|
||||||
break;
|
|
||||||
case "mystery encounter":
|
|
||||||
dir = path.join(__dirname, "test", "mystery-encounter", "encounters");
|
|
||||||
description = `Mystery Encounter - ${formattedName}`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error(`Invalid type. Please use one of the following: ${typeChoices.join(", ")}.`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define the content template
|
|
||||||
const content = `import { Abilities } from "#enums/abilities";
|
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
|
||||||
import Phaser from "phaser";
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
||||||
|
|
||||||
describe("${description}", () => {
|
|
||||||
let phaserGame: Phaser.Game;
|
|
||||||
let game: GameManager;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
phaserGame = new Phaser.Game({
|
|
||||||
type: Phaser.HEADLESS,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
game.phaseInterceptor.restoreOg();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
game = new GameManager(phaserGame);
|
|
||||||
game.override
|
|
||||||
.moveset([ Moves.SPLASH ])
|
|
||||||
.ability(Abilities.BALL_FETCH)
|
|
||||||
.battleType("single")
|
|
||||||
.disableCrits()
|
|
||||||
.enemySpecies(Species.MAGIKARP)
|
|
||||||
.enemyAbility(Abilities.BALL_FETCH)
|
|
||||||
.enemyMoveset(Moves.SPLASH);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should do X", async () => {
|
|
||||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
|
||||||
|
|
||||||
expect(true).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Ensure the directory exists
|
|
||||||
if (!fs.existsSync(dir)) {
|
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the file with the given name
|
|
||||||
const filePath = path.join(dir, `${fileName}.test.ts`);
|
|
||||||
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
console.error(`File "${fileName}.test.ts" already exists.`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the template content to the file
|
|
||||||
fs.writeFileSync(filePath, content, "utf8");
|
|
||||||
|
|
||||||
console.log(`File created at: ${filePath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
runInteractive();
|
|
@ -145,6 +145,5 @@
|
|||||||
</div>
|
</div>
|
||||||
<script type="module" src="./src/main.ts"></script>
|
<script type="module" src="./src/main.ts"></script>
|
||||||
<script src="./src/touch-controls.ts" type="module"></script>
|
<script src="./src/touch-controls.ts" type="module"></script>
|
||||||
<script src="./src/debug.js" type="module"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -12,3 +12,8 @@ post-merge:
|
|||||||
commands:
|
commands:
|
||||||
update-submodules:
|
update-submodules:
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
post-checkout:
|
||||||
|
commands:
|
||||||
|
update-submodules:
|
||||||
|
run: git submodule update --init --recursive
|
@ -13,15 +13,15 @@
|
|||||||
"test:cov": "vitest run --coverage --no-isolate",
|
"test:cov": "vitest run --coverage --no-isolate",
|
||||||
"test:watch": "vitest watch --coverage --no-isolate",
|
"test:watch": "vitest watch --coverage --no-isolate",
|
||||||
"test:silent": "vitest run --silent --no-isolate",
|
"test:silent": "vitest run --silent --no-isolate",
|
||||||
|
"test:create": "node scripts/create-test/create-test.js",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"eslint": "eslint --fix .",
|
"eslint": "eslint --fix .",
|
||||||
"eslint-ci": "eslint .",
|
"eslint-ci": "eslint .",
|
||||||
"biome": "biome check --write --changed --no-errors-on-unmatched",
|
"biome": "biome check --write --changed --no-errors-on-unmatched",
|
||||||
"biome-ci": "biome ci --diagnostic-level=error --reporter=github --changed --no-errors-on-unmatched",
|
"biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched",
|
||||||
"docs": "typedoc",
|
"docs": "typedoc",
|
||||||
"depcruise": "depcruise src",
|
"depcruise": "depcruise src test",
|
||||||
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",
|
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",
|
||||||
"create-test": "node ./create-test-boilerplate.js",
|
|
||||||
"postinstall": "npx lefthook install && npx lefthook run post-merge",
|
"postinstall": "npx lefthook install && npx lefthook run post-merge",
|
||||||
"update-version:patch": "npm version patch --force --no-git-tag-version",
|
"update-version:patch": "npm version patch --force --no-git-tag-version",
|
||||||
"update-version:minor": "npm version minor --force --no-git-tag-version",
|
"update-version:minor": "npm version minor --force --no-git-tag-version",
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 5.5 KiB |
147
scripts/create-test/create-test.js
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* This script creates a test boilerplate file in the appropriate
|
||||||
|
* directory based on the type selected.
|
||||||
|
* @example npm run test:create
|
||||||
|
*/
|
||||||
|
|
||||||
|
import chalk from "chalk";
|
||||||
|
import inquirer from "inquirer";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
//#region Constants
|
||||||
|
|
||||||
|
const version = "2.0.1";
|
||||||
|
// Get the directory name of the current module file
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const projectRoot = path.join(__dirname, "..", "..");
|
||||||
|
const boilerplateFilePath = path.join(__dirname, "test-boilerplate.ts");
|
||||||
|
const choices = [
|
||||||
|
{ label: "Move", dir: "moves" },
|
||||||
|
{ label: "Ability", dir: "abilities" },
|
||||||
|
{ label: "Item", dir: "items" },
|
||||||
|
{ label: "Mystery Encounter", dir: "mystery-encounter/encounters" },
|
||||||
|
{ label: "Utils", dir: "utils" },
|
||||||
|
{ label: "UI", dir: "ui" },
|
||||||
|
];
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region Functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path to a given folder in the test directory
|
||||||
|
* @param {...string} folders the subfolders to append to the base path
|
||||||
|
* @returns {string} the path to the requested folder
|
||||||
|
*/
|
||||||
|
function getTestFolderPath(...folders) {
|
||||||
|
return path.join(projectRoot, "test", ...folders);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts the user to select a type via list.
|
||||||
|
* @returns {Promise<{selectedOption: {label: string, dir: string}}>} the selected type
|
||||||
|
*/
|
||||||
|
async function promptTestType() {
|
||||||
|
const typeAnswer = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "list",
|
||||||
|
name: "selectedOption",
|
||||||
|
message: "What type of test would you like to create?",
|
||||||
|
choices: [...choices.map(choice => ({ name: choice.label, value: choice })), "EXIT"],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (typeAnswer.selectedOption === "EXIT") {
|
||||||
|
console.log("Exiting...");
|
||||||
|
return process.exit();
|
||||||
|
}
|
||||||
|
if (!choices.some(choice => choice.dir === typeAnswer.selectedOption.dir)) {
|
||||||
|
console.error(`Please provide a valid type: (${choices.map(choice => choice.label).join(", ")})!`);
|
||||||
|
return await promptTestType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts the user to provide a file name.
|
||||||
|
* @param {string} selectedType
|
||||||
|
* @returns {Promise<{userInput: string}>} the selected file name
|
||||||
|
*/
|
||||||
|
async function promptFileName(selectedType) {
|
||||||
|
/** @type {{userInput: string}} */
|
||||||
|
const fileNameAnswer = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "userInput",
|
||||||
|
message: `Please provide the name of the ${selectedType}:`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!fileNameAnswer.userInput || fileNameAnswer.userInput.trim().length === 0) {
|
||||||
|
console.error("Please provide a valid file name!");
|
||||||
|
return await promptFileName(selectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileNameAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the interactive test:create "CLI"
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function runInteractive() {
|
||||||
|
console.group(chalk.grey(`Create Test - v${version}\n`));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const typeAnswer = await promptTestType();
|
||||||
|
const fileNameAnswer = await promptFileName(typeAnswer.selectedOption.label);
|
||||||
|
|
||||||
|
const type = typeAnswer.selectedOption;
|
||||||
|
// Convert fileName from snake_case or camelCase to kebab-case
|
||||||
|
const fileName = fileNameAnswer.userInput
|
||||||
|
.replace(/_+/g, "-") // Convert snake_case (underscore) to kebab-case (dashes)
|
||||||
|
.replace(/([a-z])([A-Z])/g, "$1-$2") // Convert camelCase to kebab-case
|
||||||
|
.replace(/\s+/g, "-") // Replace spaces with dashes
|
||||||
|
.toLowerCase(); // Ensure all lowercase
|
||||||
|
// Format the description for the test case
|
||||||
|
|
||||||
|
const formattedName = fileName.replace(/-/g, " ").replace(/\b\w/g, char => char.toUpperCase());
|
||||||
|
// Determine the directory based on the type
|
||||||
|
const dir = getTestFolderPath(type.dir);
|
||||||
|
const description = `${type.label} - ${formattedName}`;
|
||||||
|
|
||||||
|
// Define the content template
|
||||||
|
const content = fs.readFileSync(boilerplateFilePath, "utf8").replace("{{description}}", description);
|
||||||
|
|
||||||
|
// Ensure the directory exists
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the file with the given name
|
||||||
|
const filePath = path.join(dir, `${fileName}.test.ts`);
|
||||||
|
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
console.error(chalk.red.bold(`\n✗ File "${fileName}.test.ts" already exists!\n`));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the template content to the file
|
||||||
|
fs.writeFileSync(filePath, content, "utf8");
|
||||||
|
|
||||||
|
console.log(chalk.green.bold(`\n✔ File created at: test/${type.dir}/${fileName}.test.ts\n`));
|
||||||
|
console.groupEnd();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(chalk.red("✗ Error: ", err.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region Run
|
||||||
|
|
||||||
|
runInteractive();
|
||||||
|
|
||||||
|
//#endregion
|
43
scripts/create-test/test-boilerplate.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("{{description}}", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.ability(AbilityId.BALL_FETCH)
|
||||||
|
.battleStyle("single")
|
||||||
|
.criticalHits(false)
|
||||||
|
.enemySpecies(SpeciesId.MAGIKARP)
|
||||||
|
.enemyAbility(AbilityId.BALL_FETCH)
|
||||||
|
.enemyMoveset(MoveId.SPLASH)
|
||||||
|
.startingLevel(100)
|
||||||
|
.enemyLevel(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should do XYZ", async () => {
|
||||||
|
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||||
|
|
||||||
|
game.move.use(MoveId.SPLASH);
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
|
expect(true).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
@ -1,11 +1,27 @@
|
|||||||
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
import type { AbAttr } from "#app/data/abilities/ability";
|
||||||
import type Move from "#app/data/moves/move";
|
import type Move from "#app/data/moves/move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { BattleStat } from "#enums/stat";
|
import type { BattleStat } from "#enums/stat";
|
||||||
|
import type { AbAttrConstructorMap } from "#app/data/abilities/ability";
|
||||||
|
|
||||||
export type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => void;
|
// Intentionally re-export all types from the ability attributes module
|
||||||
export type AbAttrSuccessFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean;
|
export type * from "#app/data/abilities/ability";
|
||||||
|
|
||||||
|
export type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean, ...args: any[]) => void;
|
||||||
|
export type AbAttrSuccessFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean, ...args: any[]) => boolean;
|
||||||
export type AbAttrCondition = (pokemon: Pokemon) => boolean;
|
export type AbAttrCondition = (pokemon: Pokemon) => boolean;
|
||||||
export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;
|
export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;
|
||||||
export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
|
export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
|
||||||
export type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean;
|
export type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all ability attribute class names as strings
|
||||||
|
*/
|
||||||
|
export type AbAttrString = keyof AbAttrConstructorMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of ability attribute class names to an instance of the class.
|
||||||
|
*/
|
||||||
|
export type AbAttrMap = {
|
||||||
|
[K in keyof AbAttrConstructorMap]: InstanceType<AbAttrConstructorMap[K]>;
|
||||||
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/**
|
export interface DexData {
|
||||||
* Dex entry for a single Pokemon Species
|
[key: number]: DexEntry;
|
||||||
*/
|
}
|
||||||
|
|
||||||
export interface DexEntry {
|
export interface DexEntry {
|
||||||
seenAttr: bigint;
|
seenAttr: bigint;
|
||||||
caughtAttr: bigint;
|
caughtAttr: bigint;
|
||||||
@ -10,7 +11,3 @@ export interface DexEntry {
|
|||||||
hatchedCount: number;
|
hatchedCount: number;
|
||||||
ivs: number[];
|
ivs: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DexData {
|
|
||||||
[key: number]: DexEntry;
|
|
||||||
}
|
|
@ -2,9 +2,6 @@ export interface Localizable {
|
|||||||
localize(): void;
|
localize(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TranslationEntries {
|
|
||||||
[key: string]: string | { [key: string]: string };
|
|
||||||
}
|
|
||||||
export interface SimpleTranslationEntries {
|
export interface SimpleTranslationEntries {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}
|
}
|
32
src/@types/modifier-types.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Re-exports of all the types defined in the modifier module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import type { ModifierConstructorMap } from "#app/modifier/modifier";
|
||||||
|
import type { ModifierType, WeightedModifierType } from "#app/modifier/modifier-type";
|
||||||
|
export type ModifierTypeFunc = () => ModifierType;
|
||||||
|
export type WeightedModifierTypeWeightFunc = (party: Pokemon[], rerollCount?: number) => number;
|
||||||
|
|
||||||
|
export type { ModifierConstructorMap } from "#app/modifier/modifier";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of modifier names to their respective instance types
|
||||||
|
*/
|
||||||
|
export type ModifierInstanceMap = {
|
||||||
|
[K in keyof ModifierConstructorMap]: InstanceType<ModifierConstructorMap[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all modifier constructors.
|
||||||
|
*/
|
||||||
|
export type ModifierClass = ModifierConstructorMap[keyof ModifierConstructorMap];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all modifier names as strings.
|
||||||
|
*/
|
||||||
|
export type ModifierString = keyof ModifierConstructorMap;
|
||||||
|
|
||||||
|
export type ModifierPool = {
|
||||||
|
[tier: string]: WeightedModifierType[];
|
||||||
|
};
|
56
src/@types/move-types.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import type {
|
||||||
|
AttackMove,
|
||||||
|
StatusMove,
|
||||||
|
SelfStatusMove,
|
||||||
|
ChargingAttackMove,
|
||||||
|
ChargingSelfStatusMove,
|
||||||
|
MoveAttrConstructorMap,
|
||||||
|
MoveAttr,
|
||||||
|
} from "#app/data/moves/move";
|
||||||
|
|
||||||
|
export type MoveAttrFilter = (attr: MoveAttr) => boolean;
|
||||||
|
|
||||||
|
export type * from "#app/data/moves/move";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of move subclass names to their respective classes.
|
||||||
|
* Does not include the ChargeMove subclasses. For that, use `ChargingMoveClassMap`.
|
||||||
|
*
|
||||||
|
* @privateremarks
|
||||||
|
* The `never` field (`declare private _: never`) in some classes is necessary
|
||||||
|
* to ensure typescript does not improperly narrow a failed `is` guard to `never`.
|
||||||
|
*
|
||||||
|
* For example, if we did not have the never, and wrote
|
||||||
|
* ```
|
||||||
|
* function Foo(move: Move) {
|
||||||
|
* if (move.is("AttackMove")) {
|
||||||
|
*
|
||||||
|
* } else if (move.is("StatusMove")) { // typescript errors on the `is`, saying that `move` is `never`
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export type MoveClassMap = {
|
||||||
|
AttackMove: AttackMove;
|
||||||
|
StatusMove: StatusMove;
|
||||||
|
SelfStatusMove: SelfStatusMove;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all move subclass names
|
||||||
|
*/
|
||||||
|
export type MoveKindString = "AttackMove" | "StatusMove" | "SelfStatusMove";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of move attribute names to attribute instances.
|
||||||
|
*/
|
||||||
|
export type MoveAttrMap = {
|
||||||
|
[K in keyof MoveAttrConstructorMap]: InstanceType<MoveAttrConstructorMap[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all move attribute names as strings.
|
||||||
|
*/
|
||||||
|
export type MoveAttrString = keyof MoveAttrMap;
|
||||||
|
|
||||||
|
export type ChargingMove = ChargingAttackMove | ChargingSelfStatusMove;
|
25
src/@types/phase-types.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import type { PhaseConstructorMap } from "#app/phase-manager";
|
||||||
|
|
||||||
|
// Intentionally 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.
|
||||||
|
export type * from "#app/phase-manager";
|
||||||
|
|
||||||
|
// This file includes helpful types for the phase system.
|
||||||
|
// 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 = {
|
||||||
|
[K in keyof PhaseConstructorMap]: InstanceType<PhaseConstructorMap[K]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all phase constructors.
|
||||||
|
*/
|
||||||
|
export type PhaseClass = PhaseConstructorMap[keyof PhaseConstructorMap];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union type of all phase names as strings.
|
||||||
|
*/
|
||||||
|
export type PhaseString = keyof PhaseMap;
|
@ -2,8 +2,8 @@ import type { EnemyPokemon } from "#app/field/pokemon";
|
|||||||
import type { PersistentModifier } from "#app/modifier/modifier";
|
import type { PersistentModifier } from "#app/modifier/modifier";
|
||||||
import type { PartyMemberStrength } from "#enums/party-member-strength";
|
import type { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import type { SpeciesId } from "#enums/species-id";
|
import type { SpeciesId } from "#enums/species-id";
|
||||||
import type { TrainerConfig } from "./trainer-config";
|
import type { TrainerConfig } from "../data/trainers/trainer-config";
|
||||||
import type { TrainerPartyTemplate } from "./TrainerPartyTemplate";
|
import type { TrainerPartyTemplate } from "../data/trainers/TrainerPartyTemplate";
|
||||||
|
|
||||||
export type PartyTemplateFunc = () => TrainerPartyTemplate;
|
export type PartyTemplateFunc = () => TrainerPartyTemplate;
|
||||||
export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;
|
export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;
|
@ -4,7 +4,8 @@ import type Pokemon from "#app/field/pokemon";
|
|||||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
|
import { allSpecies } from "#app/data/data-lists";
|
||||||
import {
|
import {
|
||||||
fixedInt,
|
fixedInt,
|
||||||
getIvsFromId,
|
getIvsFromId,
|
||||||
@ -58,35 +59,29 @@ import {
|
|||||||
getEnemyModifierTypesForWave,
|
getEnemyModifierTypesForWave,
|
||||||
getLuckString,
|
getLuckString,
|
||||||
getLuckTextTint,
|
getLuckTextTint,
|
||||||
getModifierPoolForType,
|
|
||||||
getModifierType,
|
|
||||||
getPartyLuckValue,
|
getPartyLuckValue,
|
||||||
ModifierPoolType,
|
|
||||||
modifierTypes,
|
|
||||||
PokemonHeldItemModifierType,
|
PokemonHeldItemModifierType,
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { getModifierType } from "./utils/modifier-utils";
|
||||||
|
import { modifierTypes } from "./data/data-lists";
|
||||||
|
import { getModifierPoolForType } from "./utils/modifier-utils";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import AbilityBar from "#app/ui/ability-bar";
|
import AbilityBar from "#app/ui/ability-bar";
|
||||||
import {
|
import { applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs } from "./data/abilities/apply-ab-attrs";
|
||||||
applyAbAttrs,
|
|
||||||
applyPostBattleInitAbAttrs,
|
|
||||||
applyPostItemLostAbAttrs,
|
|
||||||
BlockItemTheftAbAttr,
|
|
||||||
DoubleBattleChanceAbAttr,
|
|
||||||
PostBattleInitAbAttr,
|
|
||||||
PostItemLostAbAttr,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { allAbilities } from "./data/data-lists";
|
import { allAbilities } from "./data/data-lists";
|
||||||
import type { FixedBattleConfig } from "#app/battle";
|
import type { FixedBattleConfig } from "#app/battle";
|
||||||
import Battle from "#app/battle";
|
import Battle from "#app/battle";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import type { GameMode } from "#app/game-mode";
|
import type { GameMode } from "#app/game-mode";
|
||||||
import { GameModes, getGameMode } from "#app/game-mode";
|
import { getGameMode } from "#app/game-mode";
|
||||||
|
import { GameModes } from "#enums/game-modes";
|
||||||
import FieldSpritePipeline from "#app/pipelines/field-sprite";
|
import FieldSpritePipeline from "#app/pipelines/field-sprite";
|
||||||
import SpritePipeline from "#app/pipelines/sprite";
|
import SpritePipeline from "#app/pipelines/sprite";
|
||||||
import PartyExpBar from "#app/ui/party-exp-bar";
|
import PartyExpBar from "#app/ui/party-exp-bar";
|
||||||
import type { TrainerSlot } from "./enums/trainer-slot";
|
import type { TrainerSlot } from "./enums/trainer-slot";
|
||||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import type TrainerData from "#app/system/trainer-data";
|
import type TrainerData from "#app/system/trainer-data";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
@ -101,14 +96,12 @@ import type UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
|||||||
import { addUiThemeOverrides } from "#app/ui/ui-theme";
|
import { addUiThemeOverrides } from "#app/ui/ui-theme";
|
||||||
import type PokemonData from "#app/system/pokemon-data";
|
import type PokemonData from "#app/system/pokemon-data";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import type { SpeciesFormChange, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
|
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||||
import {
|
import type { SpeciesFormChangeTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||||
FormChangeItem,
|
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||||
pokemonFormChanges,
|
import { SpeciesFormChangeTimeOfDayTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||||
SpeciesFormChangeManualTrigger,
|
import { SpeciesFormChangeManualTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||||
SpeciesFormChangeTimeOfDayTrigger,
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
} from "#app/data/pokemon-forms";
|
|
||||||
import { FormChangePhase } from "#app/phases/form-change-phase";
|
|
||||||
import { getTypeRgb } from "#app/data/type";
|
import { getTypeRgb } from "#app/data/type";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
|
import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
|
||||||
@ -120,7 +113,7 @@ import { SceneBase } from "#app/scene-base";
|
|||||||
import CandyBar from "#app/ui/candy-bar";
|
import CandyBar from "#app/ui/candy-bar";
|
||||||
import type { Variant } from "#app/sprites/variant";
|
import type { Variant } from "#app/sprites/variant";
|
||||||
import { variantData, clearVariantData } from "#app/sprites/variant";
|
import { variantData, clearVariantData } from "#app/sprites/variant";
|
||||||
import type { Localizable } from "#app/interfaces/locales";
|
import type { Localizable } from "#app/@types/locales";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { InputsController } from "#app/inputs-controller";
|
import { InputsController } from "#app/inputs-controller";
|
||||||
import { UiInputs } from "#app/ui-inputs";
|
import { UiInputs } from "#app/ui-inputs";
|
||||||
@ -142,50 +135,32 @@ import i18next from "i18next";
|
|||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { battleSpecDialogue } from "#app/data/dialogue";
|
import { battleSpecDialogue } from "#app/data/dialogue";
|
||||||
import { LoadingScene } from "#app/loading-scene";
|
import { LoadingScene } from "#app/loading-scene";
|
||||||
import { LevelCapPhase } from "#app/phases/level-cap-phase";
|
import type { MovePhase } from "#app/phases/move-phase";
|
||||||
import { LoginPhase } from "#app/phases/login-phase";
|
|
||||||
import { MessagePhase } from "#app/phases/message-phase";
|
|
||||||
import { MovePhase } from "#app/phases/move-phase";
|
|
||||||
import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
|
|
||||||
import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
|
|
||||||
import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase";
|
|
||||||
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
|
||||||
import { ReturnPhase } from "#app/phases/return-phase";
|
|
||||||
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
|
|
||||||
import { SummonPhase } from "#app/phases/summon-phase";
|
|
||||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
|
||||||
import { TitlePhase } from "#app/phases/title-phase";
|
|
||||||
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
|
||||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
|
||||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
||||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
import { allMysteryEncounters, mysteryEncountersByBiome } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import {
|
import {
|
||||||
allMysteryEncounters,
|
|
||||||
ANTI_VARIANCE_WEIGHT_MODIFIER,
|
ANTI_VARIANCE_WEIGHT_MODIFIER,
|
||||||
AVERAGE_ENCOUNTERS_PER_RUN_TARGET,
|
AVERAGE_ENCOUNTERS_PER_RUN_TARGET,
|
||||||
BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT,
|
BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT,
|
||||||
MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT,
|
MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT,
|
||||||
mysteryEncountersByBiome,
|
} from "./constants";
|
||||||
} from "#app/data/mystery-encounters/mystery-encounters";
|
|
||||||
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||||
import { ExpPhase } from "#app/phases/exp-phase";
|
|
||||||
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
|
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
|
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
|
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { initGlobalScene } from "#app/global-scene";
|
import { initGlobalScene } from "#app/global-scene";
|
||||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
|
||||||
import { HideAbilityPhase } from "#app/phases/hide-ability-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 { timedEventManager } from "./global-event-manager";
|
import { timedEventManager } from "./global-event-manager";
|
||||||
import { starterColors } from "./global-vars/starter-colors";
|
import { starterColors } from "./global-vars/starter-colors";
|
||||||
import { startingWave } from "./starting-wave";
|
import { startingWave } from "./starting-wave";
|
||||||
|
import { PhaseManager } from "./phase-manager";
|
||||||
|
|
||||||
const DEBUG_RNG = false;
|
const DEBUG_RNG = false;
|
||||||
|
|
||||||
@ -298,18 +273,8 @@ export default class BattleScene extends SceneBase {
|
|||||||
public gameData: GameData;
|
public gameData: GameData;
|
||||||
public sessionSlotId: number;
|
public sessionSlotId: number;
|
||||||
|
|
||||||
/** PhaseQueue: dequeue/remove the first element to get the next phase */
|
/** Manager for the phases active in the battle scene */
|
||||||
public phaseQueue: Phase[];
|
public readonly phaseManager: PhaseManager;
|
||||||
public conditionalQueue: Array<[() => boolean, Phase]>;
|
|
||||||
/** PhaseQueuePrepend: is a temp storage of what will be added to PhaseQueue */
|
|
||||||
private phaseQueuePrepend: Phase[];
|
|
||||||
|
|
||||||
/** overrides default of inserting phases to end of phaseQueuePrepend array, useful or inserting Phases "out of order" */
|
|
||||||
private phaseQueuePrependSpliceIndex: number;
|
|
||||||
private nextCommandPhaseQueue: Phase[];
|
|
||||||
|
|
||||||
private currentPhase: Phase | null;
|
|
||||||
private standbyPhase: Phase | null;
|
|
||||||
public field: Phaser.GameObjects.Container;
|
public field: Phaser.GameObjects.Container;
|
||||||
public fieldUI: Phaser.GameObjects.Container;
|
public fieldUI: Phaser.GameObjects.Container;
|
||||||
public charSprite: CharSprite;
|
public charSprite: CharSprite;
|
||||||
@ -397,11 +362,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("battle");
|
super("battle");
|
||||||
this.phaseQueue = [];
|
this.phaseManager = new PhaseManager();
|
||||||
this.phaseQueuePrepend = [];
|
|
||||||
this.conditionalQueue = [];
|
|
||||||
this.phaseQueuePrependSpliceIndex = -1;
|
|
||||||
this.nextCommandPhaseQueue = [];
|
|
||||||
this.eventManager = new TimedEventManager();
|
this.eventManager = new TimedEventManager();
|
||||||
this.updateGameInfo();
|
this.updateGameInfo();
|
||||||
initGlobalScene(this);
|
initGlobalScene(this);
|
||||||
@ -717,10 +678,10 @@ export default class BattleScene extends SceneBase {
|
|||||||
).then(() => loadMoveAnimAssets(defaultMoves, true)),
|
).then(() => loadMoveAnimAssets(defaultMoves, true)),
|
||||||
this.initStarterColors(),
|
this.initStarterColors(),
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
this.pushPhase(new LoginPhase());
|
this.phaseManager.pushNew("LoginPhase");
|
||||||
this.pushPhase(new TitlePhase());
|
this.phaseManager.pushNew("TitlePhase");
|
||||||
|
|
||||||
this.shiftPhase();
|
this.phaseManager.shiftPhase();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -812,6 +773,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add a `getPartyOnSide` function for getting the party of a pokemon
|
||||||
public getPlayerParty(): PlayerPokemon[] {
|
public getPlayerParty(): PlayerPokemon[] {
|
||||||
return this.party;
|
return this.party;
|
||||||
}
|
}
|
||||||
@ -899,9 +861,9 @@ export default class BattleScene extends SceneBase {
|
|||||||
if (allyPokemon?.isActive(true)) {
|
if (allyPokemon?.isActive(true)) {
|
||||||
let targetingMovePhase: MovePhase;
|
let targetingMovePhase: MovePhase;
|
||||||
do {
|
do {
|
||||||
targetingMovePhase = this.findPhase(
|
targetingMovePhase = this.phaseManager.findPhase(
|
||||||
mp =>
|
mp =>
|
||||||
mp instanceof MovePhase &&
|
mp.is("MovePhase") &&
|
||||||
mp.targets.length === 1 &&
|
mp.targets.length === 1 &&
|
||||||
mp.targets[0] === removedPokemon.getBattlerIndex() &&
|
mp.targets[0] === removedPokemon.getBattlerIndex() &&
|
||||||
mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
|
mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
|
||||||
@ -1277,7 +1239,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
duration: 250,
|
duration: 250,
|
||||||
ease: "Sine.easeInOut",
|
ease: "Sine.easeInOut",
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
this.clearPhaseQueue();
|
this.phaseManager.clearPhaseQueue();
|
||||||
|
|
||||||
this.ui.freeUIData();
|
this.ui.freeUIData();
|
||||||
this.uiContainer.remove(this.ui, true);
|
this.uiContainer.remove(this.ui, true);
|
||||||
@ -1294,7 +1256,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
||||||
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
||||||
for (const p of playerField) {
|
for (const p of playerField) {
|
||||||
applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance);
|
applyAbAttrs("DoubleBattleChanceAbAttr", p, null, false, doubleChance);
|
||||||
}
|
}
|
||||||
return Math.max(doubleChance.value, 1);
|
return Math.max(doubleChance.value, 1);
|
||||||
}
|
}
|
||||||
@ -1450,7 +1412,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lastBattle?.double && !newDouble) {
|
if (lastBattle?.double && !newDouble) {
|
||||||
this.tryRemovePhase(p => p instanceof SwitchPhase);
|
this.phaseManager.tryRemovePhase((p: Phase) => p.is("SwitchPhase"));
|
||||||
for (const p of this.getPlayerField()) {
|
for (const p of this.getPlayerField()) {
|
||||||
p.lapseTag(BattlerTagType.COMMANDED);
|
p.lapseTag(BattlerTagType.COMMANDED);
|
||||||
}
|
}
|
||||||
@ -1492,14 +1454,14 @@ export default class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
playerField.forEach((pokemon, p) => {
|
playerField.forEach((pokemon, p) => {
|
||||||
if (pokemon.isOnField()) {
|
if (pokemon.isOnField()) {
|
||||||
this.pushPhase(new ReturnPhase(p));
|
this.phaseManager.pushNew("ReturnPhase", p);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const pokemon of this.getPlayerParty()) {
|
for (const pokemon of this.getPlayerParty()) {
|
||||||
pokemon.resetBattleAndWaveData();
|
pokemon.resetBattleAndWaveData();
|
||||||
pokemon.resetTera();
|
pokemon.resetTera();
|
||||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon);
|
||||||
if (
|
if (
|
||||||
pokemon.hasSpecies(SpeciesId.TERAPAGOS) ||
|
pokemon.hasSpecies(SpeciesId.TERAPAGOS) ||
|
||||||
(this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)
|
(this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)
|
||||||
@ -1509,7 +1471,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.trainer.visible) {
|
if (!this.trainer.visible) {
|
||||||
this.pushPhase(new ShowTrainerPhase());
|
this.phaseManager.pushNew("ShowTrainerPhase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1518,13 +1480,13 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.gameMode.hasRandomBiomes && !isNewBiome) {
|
if (!this.gameMode.hasRandomBiomes && !isNewBiome) {
|
||||||
this.pushPhase(new NextEncounterPhase());
|
this.phaseManager.pushNew("NextEncounterPhase");
|
||||||
} else {
|
} else {
|
||||||
this.pushPhase(new NewBiomeEncounterPhase());
|
this.phaseManager.pushNew("NewBiomeEncounterPhase");
|
||||||
|
|
||||||
const newMaxExpLevel = this.getMaxExpLevel();
|
const newMaxExpLevel = this.getMaxExpLevel();
|
||||||
if (newMaxExpLevel > maxExpLevel) {
|
if (newMaxExpLevel > maxExpLevel) {
|
||||||
this.pushPhase(new LevelCapPhase());
|
this.phaseManager.pushNew("LevelCapPhase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1589,7 +1551,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(
|
const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(
|
||||||
this.getCurrentPhase()?.constructor.name ?? "",
|
this.phaseManager.getCurrentPhase()?.phaseName ?? "",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -1674,6 +1636,9 @@ export default class BattleScene extends SceneBase {
|
|||||||
case SpeciesId.TATSUGIRI:
|
case SpeciesId.TATSUGIRI:
|
||||||
case SpeciesId.PALDEA_TAUROS:
|
case SpeciesId.PALDEA_TAUROS:
|
||||||
return randSeedInt(species.forms.length);
|
return randSeedInt(species.forms.length);
|
||||||
|
case SpeciesId.MAUSHOLD:
|
||||||
|
case SpeciesId.DUDUNSPARCE:
|
||||||
|
return !randSeedInt(4) ? 1 : 0;
|
||||||
case SpeciesId.PIKACHU:
|
case SpeciesId.PIKACHU:
|
||||||
if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) {
|
if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) {
|
||||||
return 0; // Ban Cosplay and Partner Pika from Trainers before wave 30
|
return 0; // Ban Cosplay and Partner Pika from Trainers before wave 30
|
||||||
@ -2130,12 +2095,15 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMaxExpLevel(ignoreLevelCap = false): number {
|
getMaxExpLevel(ignoreLevelCap = false): number {
|
||||||
if (Overrides.LEVEL_CAP_OVERRIDE > 0) {
|
const capOverride = Overrides.LEVEL_CAP_OVERRIDE ?? 0;
|
||||||
return Overrides.LEVEL_CAP_OVERRIDE;
|
if (capOverride > 0) {
|
||||||
|
return capOverride;
|
||||||
}
|
}
|
||||||
if (ignoreLevelCap || Overrides.LEVEL_CAP_OVERRIDE < 0) {
|
|
||||||
|
if (ignoreLevelCap || capOverride < 0) {
|
||||||
return Number.MAX_SAFE_INTEGER;
|
return Number.MAX_SAFE_INTEGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
const waveIndex = Math.ceil((this.currentBattle?.waveIndex || 1) / 10) * 10;
|
const waveIndex = Math.ceil((this.currentBattle?.waveIndex || 1) / 10) * 10;
|
||||||
const difficultyWaveIndex = this.gameMode.getWaveForDifficulty(waveIndex);
|
const difficultyWaveIndex = this.gameMode.getWaveForDifficulty(waveIndex);
|
||||||
const baseLevel = (1 + difficultyWaveIndex / 2 + Math.pow(difficultyWaveIndex / 25, 2)) * 1.2;
|
const baseLevel = (1 + difficultyWaveIndex / 2 + Math.pow(difficultyWaveIndex / 25, 2)) * 1.2;
|
||||||
@ -2617,286 +2585,6 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Phase Functions */
|
|
||||||
getCurrentPhase(): Phase | null {
|
|
||||||
return this.currentPhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
getStandbyPhase(): Phase | null {
|
|
||||||
return this.standbyPhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a phase to the conditional queue and ensures it is executed only when the specified condition is met.
|
|
||||||
*
|
|
||||||
* This method allows deferring the execution of a phase until certain conditions are met, which is useful for handling
|
|
||||||
* situations like abilities and entry hazards that depend on specific game states.
|
|
||||||
*
|
|
||||||
* @param {Phase} phase - The phase to be added to the conditional queue.
|
|
||||||
* @param {() => boolean} condition - A function that returns a boolean indicating whether the phase should be executed.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
pushConditionalPhase(phase: Phase, condition: () => boolean): void {
|
|
||||||
this.conditionalQueue.push([condition, phase]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a phase to nextCommandPhaseQueue, as long as boolean passed in is false
|
|
||||||
* @param phase {@linkcode Phase} the phase to add
|
|
||||||
* @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue
|
|
||||||
*/
|
|
||||||
pushPhase(phase: Phase, defer = false): void {
|
|
||||||
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds Phase(s) to the end of phaseQueuePrepend, or at phaseQueuePrependSpliceIndex
|
|
||||||
* @param phases {@linkcode Phase} the phase(s) to add
|
|
||||||
*/
|
|
||||||
unshiftPhase(...phases: Phase[]): void {
|
|
||||||
if (this.phaseQueuePrependSpliceIndex === -1) {
|
|
||||||
this.phaseQueuePrepend.push(...phases);
|
|
||||||
} else {
|
|
||||||
this.phaseQueuePrepend.splice(this.phaseQueuePrependSpliceIndex, 0, ...phases);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the phaseQueue
|
|
||||||
*/
|
|
||||||
clearPhaseQueue(): void {
|
|
||||||
this.phaseQueue.splice(0, this.phaseQueue.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all phase-related stuff, including all phase queues, the current and standby phases, and a splice index
|
|
||||||
*/
|
|
||||||
clearAllPhases(): void {
|
|
||||||
for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) {
|
|
||||||
queue.splice(0, queue.length);
|
|
||||||
}
|
|
||||||
this.currentPhase = null;
|
|
||||||
this.standbyPhase = null;
|
|
||||||
this.clearPhaseQueueSplice();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by function unshiftPhase(), sets index to start inserting at current length instead of the end of the array, useful if phaseQueuePrepend gets longer with Phases
|
|
||||||
*/
|
|
||||||
setPhaseQueueSplice(): void {
|
|
||||||
this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets phaseQueuePrependSpliceIndex to -1, implies that calls to unshiftPhase will insert at end of phaseQueuePrepend
|
|
||||||
*/
|
|
||||||
clearPhaseQueueSplice(): void {
|
|
||||||
this.phaseQueuePrependSpliceIndex = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is called by each Phase implementations "end()" by default
|
|
||||||
* We dump everything from phaseQueuePrepend to the start of of phaseQueue
|
|
||||||
* then removes first Phase and starts it
|
|
||||||
*/
|
|
||||||
shiftPhase(): void {
|
|
||||||
if (this.standbyPhase) {
|
|
||||||
this.currentPhase = this.standbyPhase;
|
|
||||||
this.standbyPhase = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.phaseQueuePrependSpliceIndex > -1) {
|
|
||||||
this.clearPhaseQueueSplice();
|
|
||||||
}
|
|
||||||
if (this.phaseQueuePrepend.length) {
|
|
||||||
while (this.phaseQueuePrepend.length) {
|
|
||||||
const poppedPhase = this.phaseQueuePrepend.pop();
|
|
||||||
if (poppedPhase) {
|
|
||||||
this.phaseQueue.unshift(poppedPhase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this.phaseQueue.length) {
|
|
||||||
this.populatePhaseQueue();
|
|
||||||
// Clear the conditionalQueue if there are no phases left in the phaseQueue
|
|
||||||
this.conditionalQueue = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentPhase = this.phaseQueue.shift() ?? null;
|
|
||||||
|
|
||||||
// Check if there are any conditional phases queued
|
|
||||||
if (this.conditionalQueue?.length) {
|
|
||||||
// Retrieve the first conditional phase from the queue
|
|
||||||
const conditionalPhase = this.conditionalQueue.shift();
|
|
||||||
// Evaluate the condition associated with the phase
|
|
||||||
if (conditionalPhase?.[0]()) {
|
|
||||||
// If the condition is met, add the phase to the phase queue
|
|
||||||
this.pushPhase(conditionalPhase[1]);
|
|
||||||
} else if (conditionalPhase) {
|
|
||||||
// If the condition is not met, re-add the phase back to the front of the conditional queue
|
|
||||||
this.conditionalQueue.unshift(conditionalPhase);
|
|
||||||
} else {
|
|
||||||
console.warn("condition phase is undefined/null!", conditionalPhase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.currentPhase) {
|
|
||||||
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
|
|
||||||
this.currentPhase.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
overridePhase(phase: Phase): boolean {
|
|
||||||
if (this.standbyPhase) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.standbyPhase = this.currentPhase;
|
|
||||||
this.currentPhase = phase;
|
|
||||||
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
|
|
||||||
phase.start();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a specific {@linkcode Phase} in the phase queue.
|
|
||||||
*
|
|
||||||
* @param phaseFilter filter function to use to find the wanted phase
|
|
||||||
* @returns the found phase or undefined if none found
|
|
||||||
*/
|
|
||||||
findPhase<P extends Phase = Phase>(phaseFilter: (phase: P) => boolean): P | undefined {
|
|
||||||
return this.phaseQueue.find(phaseFilter) as P;
|
|
||||||
}
|
|
||||||
|
|
||||||
tryReplacePhase(phaseFilter: (phase: Phase) => boolean, phase: Phase): boolean {
|
|
||||||
const phaseIndex = this.phaseQueue.findIndex(phaseFilter);
|
|
||||||
if (phaseIndex > -1) {
|
|
||||||
this.phaseQueue[phaseIndex] = phase;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
tryRemovePhase(phaseFilter: (phase: Phase) => boolean): boolean {
|
|
||||||
const phaseIndex = this.phaseQueue.findIndex(phaseFilter);
|
|
||||||
if (phaseIndex > -1) {
|
|
||||||
this.phaseQueue.splice(phaseIndex, 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will search for a specific phase in {@linkcode phaseQueuePrepend} via filter, and remove the first result if a match is found.
|
|
||||||
* @param phaseFilter filter function
|
|
||||||
*/
|
|
||||||
tryRemoveUnshiftedPhase(phaseFilter: (phase: Phase) => boolean): boolean {
|
|
||||||
const phaseIndex = this.phaseQueuePrepend.findIndex(phaseFilter);
|
|
||||||
if (phaseIndex > -1) {
|
|
||||||
this.phaseQueuePrepend.splice(phaseIndex, 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase()
|
|
||||||
* @param phase {@linkcode Phase} the phase to be added
|
|
||||||
* @param targetPhase {@linkcode Phase} the type of phase to search for in phaseQueue
|
|
||||||
* @returns boolean if a targetPhase was found and added
|
|
||||||
*/
|
|
||||||
prependToPhase(phase: Phase | Phase[], targetPhase: Constructor<Phase>): boolean {
|
|
||||||
if (!Array.isArray(phase)) {
|
|
||||||
phase = [phase];
|
|
||||||
}
|
|
||||||
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase);
|
|
||||||
|
|
||||||
if (targetIndex !== -1) {
|
|
||||||
this.phaseQueue.splice(targetIndex, 0, ...phase);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
this.unshiftPhase(...phase);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()}
|
|
||||||
* @param phase {@linkcode Phase} the phase(s) to be added
|
|
||||||
* @param targetPhase {@linkcode Phase} the type of phase to search for in {@linkcode phaseQueue}
|
|
||||||
* @returns `true` if a `targetPhase` was found to append to
|
|
||||||
*/
|
|
||||||
appendToPhase(phase: Phase | Phase[], targetPhase: Constructor<Phase>): boolean {
|
|
||||||
if (!Array.isArray(phase)) {
|
|
||||||
phase = [phase];
|
|
||||||
}
|
|
||||||
const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase);
|
|
||||||
|
|
||||||
if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) {
|
|
||||||
this.phaseQueue.splice(targetIndex + 1, 0, ...phase);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
this.unshiftPhase(...phase);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue
|
|
||||||
* @param message string for MessagePhase
|
|
||||||
* @param callbackDelay optional param for MessagePhase constructor
|
|
||||||
* @param prompt optional param for MessagePhase constructor
|
|
||||||
* @param promptDelay optional param for MessagePhase constructor
|
|
||||||
* @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue
|
|
||||||
*/
|
|
||||||
queueMessage(
|
|
||||||
message: string,
|
|
||||||
callbackDelay?: number | null,
|
|
||||||
prompt?: boolean | null,
|
|
||||||
promptDelay?: number | null,
|
|
||||||
defer?: boolean | null,
|
|
||||||
) {
|
|
||||||
const phase = new MessagePhase(message, callbackDelay, prompt, promptDelay);
|
|
||||||
if (!defer) {
|
|
||||||
// adds to the end of PhaseQueuePrepend
|
|
||||||
this.unshiftPhase(phase);
|
|
||||||
} else {
|
|
||||||
//remember that pushPhase adds it to nextCommandPhaseQueue
|
|
||||||
this.pushPhase(phase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queues an ability bar flyout phase
|
|
||||||
* @param pokemon The pokemon who has the ability
|
|
||||||
* @param passive Whether the ability is a passive
|
|
||||||
* @param show Whether to show or hide the bar
|
|
||||||
*/
|
|
||||||
public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void {
|
|
||||||
this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase());
|
|
||||||
this.clearPhaseQueueSplice();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides the ability bar if it is currently visible
|
|
||||||
*/
|
|
||||||
public hideAbilityBar(): void {
|
|
||||||
if (this.abilityBar.isVisible()) {
|
|
||||||
this.unshiftPhase(new HideAbilityPhase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order)
|
|
||||||
*/
|
|
||||||
populatePhaseQueue(): void {
|
|
||||||
if (this.nextCommandPhaseQueue.length) {
|
|
||||||
this.phaseQueue.push(...this.nextCommandPhaseQueue);
|
|
||||||
this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length);
|
|
||||||
}
|
|
||||||
this.phaseQueue.push(new TurnInitPhase());
|
|
||||||
}
|
|
||||||
|
|
||||||
addMoney(amount: number): void {
|
addMoney(amount: number): void {
|
||||||
this.money = Math.min(this.money + amount, Number.MAX_SAFE_INTEGER);
|
this.money = Math.min(this.money + amount, Number.MAX_SAFE_INTEGER);
|
||||||
this.updateMoneyText();
|
this.updateMoneyText();
|
||||||
@ -2944,7 +2632,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
} else if (!virtual) {
|
} else if (!virtual) {
|
||||||
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier);
|
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier);
|
||||||
this.queueMessage(
|
this.phaseManager.queueMessage(
|
||||||
i18next.t("battle:itemStackFull", {
|
i18next.t("battle:itemStackFull", {
|
||||||
fullItemName: modifier.type.name,
|
fullItemName: modifier.type.name,
|
||||||
itemName: defaultModifierType.name,
|
itemName: defaultModifierType.name,
|
||||||
@ -3055,7 +2743,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
|
|
||||||
if (source && source.isPlayer() !== target.isPlayer()) {
|
if (source && source.isPlayer() !== target.isPlayer()) {
|
||||||
applyAbAttrs(BlockItemTheftAbAttr, source, cancelled);
|
applyAbAttrs("BlockItemTheftAbAttr", source, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
@ -3089,19 +2777,19 @@ export default class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
const removeOld = itemModifier.stackCount === 0;
|
const removeOld = itemModifier.stackCount === 0;
|
||||||
|
|
||||||
if (!removeOld || !source || this.removeModifier(itemModifier, !source.isPlayer())) {
|
if (!removeOld || !source || this.removeModifier(itemModifier, source.isEnemy())) {
|
||||||
const addModifier = () => {
|
const addModifier = () => {
|
||||||
if (!matchingModifier || this.removeModifier(matchingModifier, !target.isPlayer())) {
|
if (!matchingModifier || this.removeModifier(matchingModifier, target.isEnemy())) {
|
||||||
if (target.isPlayer()) {
|
if (target.isPlayer()) {
|
||||||
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant);
|
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant);
|
||||||
if (source && itemLost) {
|
if (source && itemLost) {
|
||||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);
|
applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
this.addEnemyModifier(newItemModifier, ignoreUpdate, instant);
|
this.addEnemyModifier(newItemModifier, ignoreUpdate, instant);
|
||||||
if (source && itemLost) {
|
if (source && itemLost) {
|
||||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);
|
applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3137,7 +2825,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
|
|
||||||
if (source && source.isPlayer() !== target.isPlayer()) {
|
if (source && source.isPlayer() !== target.isPlayer()) {
|
||||||
applyAbAttrs(BlockItemTheftAbAttr, source, cancelled);
|
applyAbAttrs("BlockItemTheftAbAttr", source, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
@ -3292,6 +2980,13 @@ export default class BattleScene extends SceneBase {
|
|||||||
) {
|
) {
|
||||||
modifiers.splice(m--, 1);
|
modifiers.splice(m--, 1);
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
modifier instanceof PokemonHeldItemModifier &&
|
||||||
|
!isNullOrUndefined(modifier.getSpecies()) &&
|
||||||
|
!this.getPokemonById(modifier.pokemonId)?.hasSpecies(modifier.getSpecies()!)
|
||||||
|
) {
|
||||||
|
modifiers.splice(m--, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (const modifier of modifiers) {
|
for (const modifier of modifiers) {
|
||||||
if (modifier instanceof PersistentModifier) {
|
if (modifier instanceof PersistentModifier) {
|
||||||
@ -3508,17 +3203,17 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
if (matchingFormChange) {
|
if (matchingFormChange) {
|
||||||
let phase: Phase;
|
let phase: Phase;
|
||||||
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) {
|
if (pokemon.isPlayer() && !matchingFormChange.quiet) {
|
||||||
phase = new FormChangePhase(pokemon, matchingFormChange, modal);
|
phase = this.phaseManager.create("FormChangePhase", pokemon, matchingFormChange, modal);
|
||||||
} else {
|
} else {
|
||||||
phase = new QuietFormChangePhase(pokemon, matchingFormChange);
|
phase = this.phaseManager.create("QuietFormChangePhase", pokemon, matchingFormChange);
|
||||||
}
|
}
|
||||||
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) {
|
if (pokemon.isPlayer() && !matchingFormChange.quiet && modal) {
|
||||||
this.overridePhase(phase);
|
this.phaseManager.overridePhase(phase);
|
||||||
} else if (delayed) {
|
} else if (delayed) {
|
||||||
this.pushPhase(phase);
|
this.phaseManager.pushPhase(phase);
|
||||||
} else {
|
} else {
|
||||||
this.unshiftPhase(phase);
|
this.phaseManager.unshiftPhase(phase);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3533,11 +3228,12 @@ export default class BattleScene extends SceneBase {
|
|||||||
fieldAssets?: Phaser.GameObjects.Sprite[],
|
fieldAssets?: Phaser.GameObjects.Sprite[],
|
||||||
delayed = false,
|
delayed = false,
|
||||||
): boolean {
|
): boolean {
|
||||||
const phase: Phase = new PokemonAnimPhase(battleAnimType, pokemon, fieldAssets);
|
const phaseManager = this.phaseManager;
|
||||||
|
const phase: Phase = phaseManager.create("PokemonAnimPhase", battleAnimType, pokemon, fieldAssets);
|
||||||
if (delayed) {
|
if (delayed) {
|
||||||
this.pushPhase(phase);
|
phaseManager.pushPhase(phase);
|
||||||
} else {
|
} else {
|
||||||
this.unshiftPhase(phase);
|
phaseManager.unshiftPhase(phase);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3582,9 +3278,8 @@ export default class BattleScene extends SceneBase {
|
|||||||
gameMode: this.currentBattle ? this.gameMode.getName() : "Title",
|
gameMode: this.currentBattle ? this.gameMode.getName() : "Title",
|
||||||
biome: this.currentBattle ? getBiomeName(this.arena.biomeType) : "",
|
biome: this.currentBattle ? getBiomeName(this.arena.biomeType) : "",
|
||||||
wave: this.currentBattle?.waveIndex ?? 0,
|
wave: this.currentBattle?.waveIndex ?? 0,
|
||||||
party: this.party
|
party:
|
||||||
? this.party.map(p => {
|
this.party?.map(p => ({
|
||||||
return {
|
|
||||||
name: p.name,
|
name: p.name,
|
||||||
form: p.getFormKey(),
|
form: p.getFormKey(),
|
||||||
types: p.getTypes().map(type => PokemonType[type]),
|
types: p.getTypes().map(type => PokemonType[type]),
|
||||||
@ -3594,9 +3289,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
currentHP: p.hp,
|
currentHP: p.hp,
|
||||||
maxHP: p.getMaxHp(),
|
maxHP: p.getMaxHp(),
|
||||||
status: p.status?.effect ? StatusEffect[p.status.effect] : "",
|
status: p.status?.effect ? StatusEffect[p.status.effect] : "",
|
||||||
};
|
})) ?? [], // TODO: review if this can be nullish
|
||||||
})
|
|
||||||
: [],
|
|
||||||
modeChain: this.ui?.getModeChain() ?? [],
|
modeChain: this.ui?.getModeChain() ?? [],
|
||||||
};
|
};
|
||||||
(window as any).gameInfo = gameInfo;
|
(window as any).gameInfo = gameInfo;
|
||||||
@ -3614,7 +3307,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
activePokemon = activePokemon.concat(this.getEnemyParty());
|
activePokemon = activePokemon.concat(this.getEnemyParty());
|
||||||
for (const p of activePokemon) {
|
for (const p of activePokemon) {
|
||||||
keys.push(p.getSpriteKey(true));
|
keys.push(p.getSpriteKey(true));
|
||||||
if (p instanceof PlayerPokemon) {
|
if (p.isPlayer()) {
|
||||||
keys.push(p.getBattleSpriteKey(true, true));
|
keys.push(p.getBattleSpriteKey(true, true));
|
||||||
}
|
}
|
||||||
keys.push(p.species.getCryKey(p.formIndex));
|
keys.push(p.species.getCryKey(p.formIndex));
|
||||||
@ -3630,7 +3323,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
* @param pokemon The (enemy) pokemon
|
* @param pokemon The (enemy) pokemon
|
||||||
*/
|
*/
|
||||||
initFinalBossPhaseTwo(pokemon: Pokemon): void {
|
initFinalBossPhaseTwo(pokemon: Pokemon): void {
|
||||||
if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
|
if (pokemon.isEnemy() && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
|
||||||
this.fadeOutBgm(fixedInt(2000), false);
|
this.fadeOutBgm(fixedInt(2000), false);
|
||||||
this.ui.showDialogue(
|
this.ui.showDialogue(
|
||||||
battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin,
|
battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin,
|
||||||
@ -3648,19 +3341,19 @@ export default class BattleScene extends SceneBase {
|
|||||||
this.currentBattle.double = true;
|
this.currentBattle.double = true;
|
||||||
const availablePartyMembers = this.getPlayerParty().filter(p => p.isAllowedInBattle());
|
const availablePartyMembers = this.getPlayerParty().filter(p => p.isAllowedInBattle());
|
||||||
if (availablePartyMembers.length > 1) {
|
if (availablePartyMembers.length > 1) {
|
||||||
this.pushPhase(new ToggleDoublePositionPhase(true));
|
this.phaseManager.pushNew("ToggleDoublePositionPhase", true);
|
||||||
if (!availablePartyMembers[1].isOnField()) {
|
if (!availablePartyMembers[1].isOnField()) {
|
||||||
this.pushPhase(new SummonPhase(1));
|
this.phaseManager.pushNew("SummonPhase", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.shiftPhase();
|
this.phaseManager.shiftPhase();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.shiftPhase();
|
this.phaseManager.shiftPhase();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3772,10 +3465,10 @@ export default class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
if (exp) {
|
if (exp) {
|
||||||
const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
|
const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
|
||||||
this.unshiftPhase(
|
this.phaseManager.unshiftPhase(
|
||||||
expPartyMembers[pm].isOnField()
|
expPartyMembers[pm].isOnField()
|
||||||
? new ExpPhase(partyMemberIndex, exp)
|
? this.phaseManager.create("ExpPhase", partyMemberIndex, exp)
|
||||||
: new ShowPartyExpBarPhase(partyMemberIndex, exp),
|
: this.phaseManager.create("ShowPartyExpBarPhase", partyMemberIndex, exp),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3823,17 +3516,13 @@ export default class BattleScene extends SceneBase {
|
|||||||
sessionEncounterRate +
|
sessionEncounterRate +
|
||||||
Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
|
Math.min(currentRunDiffFromAvg * ANTI_VARIANCE_WEIGHT_MODIFIER, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT / 2);
|
||||||
|
|
||||||
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE)
|
const successRate = Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE ?? favoredEncounterRate;
|
||||||
? favoredEncounterRate
|
|
||||||
: Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE!;
|
|
||||||
|
|
||||||
// If the most recent ME was 3 or fewer waves ago, can never spawn a ME
|
// MEs can only spawn 3 or more waves after the previous ME, barring overrides
|
||||||
const canSpawn =
|
const canSpawn =
|
||||||
encounteredEvents.length === 0 ||
|
encounteredEvents.length === 0 || waveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex > 3;
|
||||||
waveIndex - encounteredEvents[encounteredEvents.length - 1].waveIndex > 3 ||
|
|
||||||
!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE);
|
|
||||||
|
|
||||||
if (canSpawn) {
|
if (canSpawn || Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE !== null) {
|
||||||
let roll = MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT;
|
let roll = MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT;
|
||||||
// Always rolls the check on the same offset to ensure no RNG changes from reloading session
|
// Always rolls the check on the same offset to ensure no RNG changes from reloading session
|
||||||
this.executeWithSeedOffset(
|
this.executeWithSeedOffset(
|
||||||
@ -3979,16 +3668,13 @@ export default class BattleScene extends SceneBase {
|
|||||||
if (previousEncounter !== null && encounterType === previousEncounter) {
|
if (previousEncounter !== null && encounterType === previousEncounter) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (
|
return !(
|
||||||
this.mysteryEncounterSaveData.encounteredEvents.length > 0 &&
|
this.mysteryEncounterSaveData.encounteredEvents.length > 0 &&
|
||||||
encounterCandidate.maxAllowedEncounters &&
|
encounterCandidate.maxAllowedEncounters &&
|
||||||
encounterCandidate.maxAllowedEncounters > 0 &&
|
encounterCandidate.maxAllowedEncounters > 0 &&
|
||||||
this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >=
|
this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >=
|
||||||
encounterCandidate.maxAllowedEncounters
|
encounterCandidate.maxAllowedEncounters
|
||||||
) {
|
);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
})
|
||||||
.map(m => allMysteryEncounters[m]);
|
.map(m => allMysteryEncounters[m]);
|
||||||
// Decrement tier
|
// Decrement tier
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { Command } from "./ui/command-ui-handler";
|
import type { Command } from "#enums/command";
|
||||||
import {
|
import {
|
||||||
randomString,
|
randomString,
|
||||||
getEnumValues,
|
getEnumValues,
|
||||||
@ -8,10 +8,12 @@ import {
|
|||||||
shiftCharCodes,
|
shiftCharCodes,
|
||||||
randSeedItem,
|
randSeedItem,
|
||||||
randInt,
|
randInt,
|
||||||
|
randSeedFloat,
|
||||||
} from "#app/utils/common";
|
} from "#app/utils/common";
|
||||||
import Trainer, { TrainerVariant } from "./field/trainer";
|
import Trainer from "./field/trainer";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import type { GameMode } from "./game-mode";
|
import type { GameMode } from "./game-mode";
|
||||||
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
import { MoneyMultiplierModifier, type PokemonHeldItemModifier } from "./modifier/modifier";
|
||||||
import type { PokeballType } from "#enums/pokeball";
|
import type { PokeballType } from "#enums/pokeball";
|
||||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
@ -28,18 +30,11 @@ import i18next from "#app/plugins/i18n";
|
|||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import type { CustomModifierSettings } from "#app/modifier/modifier-type";
|
import type { CustomModifierSettings } from "#app/modifier/modifier-type";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||||
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
export enum BattlerIndex {
|
|
||||||
ATTACKER = -1,
|
|
||||||
PLAYER,
|
|
||||||
PLAYER_2,
|
|
||||||
ENEMY,
|
|
||||||
ENEMY_2,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TurnCommand {
|
export interface TurnCommand {
|
||||||
command: Command;
|
command: Command;
|
||||||
@ -150,7 +145,7 @@ export default class Battle {
|
|||||||
randSeedGaussForLevel(value: number): number {
|
randSeedGaussForLevel(value: number): number {
|
||||||
let rand = 0;
|
let rand = 0;
|
||||||
for (let i = value; i > 0; i--) {
|
for (let i = value; i > 0; i--) {
|
||||||
rand += Phaser.Math.RND.realInRange(0, 1);
|
rand += randSeedFloat();
|
||||||
}
|
}
|
||||||
return rand / value;
|
return rand / value;
|
||||||
}
|
}
|
||||||
@ -178,7 +173,7 @@ export default class Battle {
|
|||||||
this.postBattleLoot.push(
|
this.postBattleLoot.push(
|
||||||
...globalScene
|
...globalScene
|
||||||
.findModifiers(
|
.findModifiers(
|
||||||
m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable,
|
m => m.is("PokemonHeldItemModifier") && m.pokemonId === enemyPokemon.id && m.isTransferable,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.map(i => {
|
.map(i => {
|
||||||
@ -205,7 +200,7 @@ export default class Battle {
|
|||||||
const message = i18next.t("battle:moneyPickedUp", {
|
const message = i18next.t("battle:moneyPickedUp", {
|
||||||
moneyAmount: formattedMoneyAmount,
|
moneyAmount: formattedMoneyAmount,
|
||||||
});
|
});
|
||||||
globalScene.queueMessage(message, undefined, true);
|
globalScene.phaseManager.queueMessage(message, undefined, true);
|
||||||
|
|
||||||
globalScene.currentBattle.moneyScattered = 0;
|
globalScene.currentBattle.moneyScattered = 0;
|
||||||
}
|
}
|
||||||
|
@ -197,10 +197,7 @@ export function canIAssignThisKey(config, key) {
|
|||||||
export function canIOverrideThisSetting(config, settingName) {
|
export function canIOverrideThisSetting(config, settingName) {
|
||||||
const key = getKeyWithSettingName(config, settingName);
|
const key = getKeyWithSettingName(config, settingName);
|
||||||
// || isTheLatestBind(config, settingName) no longer needed since action and cancel are protected
|
// || isTheLatestBind(config, settingName) no longer needed since action and cancel are protected
|
||||||
if (config.blacklist?.includes(key)) {
|
return !config.blacklist?.includes(key);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canIDeleteThisKey(config, key) {
|
export function canIDeleteThisKey(config, key) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
|
||||||
/** The maximum size of the player's party */
|
/** The maximum size of the player's party */
|
||||||
export const PLAYER_PARTY_MAX_SIZE: number = 6;
|
export const PLAYER_PARTY_MAX_SIZE: number = 6;
|
||||||
|
|
||||||
@ -17,3 +19,78 @@ export const CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180
|
|||||||
|
|
||||||
/** The raw percentage power boost for type boost items*/
|
/** The raw percentage power boost for type boost items*/
|
||||||
export const TYPE_BOOST_ITEM_BOOST_PERCENT = 20;
|
export const TYPE_BOOST_ITEM_BOOST_PERCENT = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default species that a new player can choose from
|
||||||
|
*/
|
||||||
|
export const defaultStarterSpecies: SpeciesId[] = [
|
||||||
|
SpeciesId.BULBASAUR,
|
||||||
|
SpeciesId.CHARMANDER,
|
||||||
|
SpeciesId.SQUIRTLE,
|
||||||
|
SpeciesId.CHIKORITA,
|
||||||
|
SpeciesId.CYNDAQUIL,
|
||||||
|
SpeciesId.TOTODILE,
|
||||||
|
SpeciesId.TREECKO,
|
||||||
|
SpeciesId.TORCHIC,
|
||||||
|
SpeciesId.MUDKIP,
|
||||||
|
SpeciesId.TURTWIG,
|
||||||
|
SpeciesId.CHIMCHAR,
|
||||||
|
SpeciesId.PIPLUP,
|
||||||
|
SpeciesId.SNIVY,
|
||||||
|
SpeciesId.TEPIG,
|
||||||
|
SpeciesId.OSHAWOTT,
|
||||||
|
SpeciesId.CHESPIN,
|
||||||
|
SpeciesId.FENNEKIN,
|
||||||
|
SpeciesId.FROAKIE,
|
||||||
|
SpeciesId.ROWLET,
|
||||||
|
SpeciesId.LITTEN,
|
||||||
|
SpeciesId.POPPLIO,
|
||||||
|
SpeciesId.GROOKEY,
|
||||||
|
SpeciesId.SCORBUNNY,
|
||||||
|
SpeciesId.SOBBLE,
|
||||||
|
SpeciesId.SPRIGATITO,
|
||||||
|
SpeciesId.FUECOCO,
|
||||||
|
SpeciesId.QUAXLY,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
|
||||||
|
*/
|
||||||
|
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The divisor for determining ME spawns, defines the "maximum" weight required for a spawn
|
||||||
|
* If spawn_weight === MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, 100% chance to spawn a ME
|
||||||
|
*/
|
||||||
|
export const MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT = 256;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When an ME spawn roll fails, WEIGHT_INCREMENT_ON_SPAWN_MISS is added to future rolls for ME spawn checks.
|
||||||
|
* These values are cleared whenever the next ME spawns, and spawn weight returns to BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT
|
||||||
|
*/
|
||||||
|
export const WEIGHT_INCREMENT_ON_SPAWN_MISS = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the target average for total ME spawns in a single Classic run.
|
||||||
|
* Used by anti-variance mechanic to check whether a run is above or below the target on a given wave.
|
||||||
|
*/
|
||||||
|
export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will increase/decrease the chance of spawning a ME based on the current run's total MEs encountered vs AVERAGE_ENCOUNTERS_PER_RUN_TARGET
|
||||||
|
* Example:
|
||||||
|
* AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 17 (expects avg 1 ME every 10 floors)
|
||||||
|
* ANTI_VARIANCE_WEIGHT_MODIFIER = 15
|
||||||
|
*
|
||||||
|
* On wave 20, if 1 ME has been encountered, the difference from expected average is 0 MEs.
|
||||||
|
* So anti-variance adds 0/256 to the spawn weight check for ME spawn.
|
||||||
|
*
|
||||||
|
* On wave 20, if 0 MEs have been encountered, the difference from expected average is 1 ME.
|
||||||
|
* So anti-variance adds 15/256 to the spawn weight check for ME spawn.
|
||||||
|
*
|
||||||
|
* On wave 20, if 2 MEs have been encountered, the difference from expected average is -1 ME.
|
||||||
|
* So anti-variance adds -15/256 to the spawn weight check for ME spawn.
|
||||||
|
*/
|
||||||
|
export const ANTI_VARIANCE_WEIGHT_MODIFIER = 15;
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
import type { AbAttrCondition } from "#app/@types/ability-types";
|
|
||||||
import type Pokemon from "#app/field/pokemon";
|
|
||||||
import type { BooleanHolder } from "#app/utils/common";
|
|
||||||
|
|
||||||
export abstract class AbAttr {
|
|
||||||
public showAbility: boolean;
|
|
||||||
private extraCondition: AbAttrCondition;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param showAbility - Whether to show this ability as a flyout during battle; default `true`.
|
|
||||||
* Should be kept in parity with mainline where possible.
|
|
||||||
*/
|
|
||||||
constructor(showAbility = true) {
|
|
||||||
this.showAbility = showAbility;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies ability effects without checking conditions
|
|
||||||
* @param _pokemon - The pokemon to apply this ability to
|
|
||||||
* @param _passive - Whether or not the ability is a passive
|
|
||||||
* @param _simulated - Whether the call is simulated
|
|
||||||
* @param _args - Extra args passed to the function. Handled by child classes.
|
|
||||||
* @see {@linkcode canApply}
|
|
||||||
*/
|
|
||||||
apply(
|
|
||||||
_pokemon: Pokemon,
|
|
||||||
_passive: boolean,
|
|
||||||
_simulated: boolean,
|
|
||||||
_cancelled: BooleanHolder | null,
|
|
||||||
_args: any[],
|
|
||||||
): void {}
|
|
||||||
|
|
||||||
getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCondition(): AbAttrCondition | null {
|
|
||||||
return this.extraCondition || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
addCondition(condition: AbAttrCondition): AbAttr {
|
|
||||||
this.extraCondition = condition;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a boolean describing whether the ability can be applied under current conditions
|
|
||||||
* @param _pokemon - The pokemon to apply this ability to
|
|
||||||
* @param _passive - Whether or not the ability is a passive
|
|
||||||
* @param _simulated - Whether the call is simulated
|
|
||||||
* @param _args - Extra args passed to the function. Handled by child classes.
|
|
||||||
* @returns `true` if the ability can be applied, `false` otherwise
|
|
||||||
* @see {@linkcode apply}
|
|
||||||
*/
|
|
||||||
canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
import { AbilityId } from "#enums/ability-id";
|
|
||||||
import type { AbAttrCondition } from "#app/@types/ability-types";
|
|
||||||
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
|
||||||
import i18next from "i18next";
|
|
||||||
import type { Localizable } from "#app/interfaces/locales";
|
|
||||||
import type { Constructor } from "#app/utils/common";
|
|
||||||
|
|
||||||
export class Ability implements Localizable {
|
|
||||||
public id: AbilityId;
|
|
||||||
|
|
||||||
private nameAppend: string;
|
|
||||||
public name: string;
|
|
||||||
public description: string;
|
|
||||||
public generation: number;
|
|
||||||
public isBypassFaint: boolean;
|
|
||||||
public isIgnorable: boolean;
|
|
||||||
public isSuppressable = true;
|
|
||||||
public isCopiable = true;
|
|
||||||
public isReplaceable = true;
|
|
||||||
public attrs: AbAttr[];
|
|
||||||
public conditions: AbAttrCondition[];
|
|
||||||
|
|
||||||
constructor(id: AbilityId, generation: number) {
|
|
||||||
this.id = id;
|
|
||||||
|
|
||||||
this.nameAppend = "";
|
|
||||||
this.generation = generation;
|
|
||||||
this.attrs = [];
|
|
||||||
this.conditions = [];
|
|
||||||
|
|
||||||
this.isSuppressable = true;
|
|
||||||
this.isCopiable = true;
|
|
||||||
this.isReplaceable = true;
|
|
||||||
|
|
||||||
this.localize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public get isSwappable(): boolean {
|
|
||||||
return this.isCopiable && this.isReplaceable;
|
|
||||||
}
|
|
||||||
localize(): void {
|
|
||||||
const i18nKey = AbilityId[this.id]
|
|
||||||
.split("_")
|
|
||||||
.filter(f => f)
|
|
||||||
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
|
||||||
.join("") as string;
|
|
||||||
|
|
||||||
this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : "";
|
|
||||||
this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all ability attributes that match `attrType`
|
|
||||||
* @param attrType any attribute that extends {@linkcode AbAttr}
|
|
||||||
* @returns Array of attributes that match `attrType`, Empty Array if none match.
|
|
||||||
*/
|
|
||||||
getAttrs<T extends AbAttr>(attrType: Constructor<T>): T[] {
|
|
||||||
return this.attrs.filter((a): a is T => a instanceof attrType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an ability has an attribute that matches `attrType`
|
|
||||||
* @param attrType any attribute that extends {@linkcode AbAttr}
|
|
||||||
* @returns true if the ability has attribute `attrType`
|
|
||||||
*/
|
|
||||||
hasAttr<T extends AbAttr>(attrType: Constructor<T>): boolean {
|
|
||||||
return this.attrs.some(attr => attr instanceof attrType);
|
|
||||||
}
|
|
||||||
|
|
||||||
attr<T extends Constructor<AbAttr>>(AttrType: T, ...args: ConstructorParameters<T>): Ability {
|
|
||||||
const attr = new AttrType(...args);
|
|
||||||
this.attrs.push(attr);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
conditionalAttr<T extends Constructor<AbAttr>>(
|
|
||||||
condition: AbAttrCondition,
|
|
||||||
AttrType: T,
|
|
||||||
...args: ConstructorParameters<T>
|
|
||||||
): Ability {
|
|
||||||
const attr = new AttrType(...args);
|
|
||||||
attr.addCondition(condition);
|
|
||||||
this.attrs.push(attr);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bypassFaint(): Ability {
|
|
||||||
this.isBypassFaint = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ignorable(): Ability {
|
|
||||||
this.isIgnorable = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsuppressable(): Ability {
|
|
||||||
this.isSuppressable = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
uncopiable(): Ability {
|
|
||||||
this.isCopiable = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unreplaceable(): Ability {
|
|
||||||
this.isReplaceable = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
condition(condition: AbAttrCondition): Ability {
|
|
||||||
this.conditions.push(condition);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
partial(): this {
|
|
||||||
this.nameAppend += " (P)";
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unimplemented(): this {
|
|
||||||
this.nameAppend += " (N)";
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal flag used for developers to document edge cases. When using this, please be sure to document the edge case.
|
|
||||||
* @returns the ability
|
|
||||||
*/
|
|
||||||
edgeCase(): this {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
832
src/data/abilities/apply-ab-attrs.ts
Normal file
@ -0,0 +1,832 @@
|
|||||||
|
import type { AbAttrApplyFunc, AbAttrMap, AbAttrString, AbAttrSuccessFunc } from "#app/@types/ability-types";
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { BooleanHolder, NumberHolder } from "#app/utils/common";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import type { HitResult } from "#enums/hit-result";
|
||||||
|
import type { BattleStat, Stat } from "#enums/stat";
|
||||||
|
import type { StatusEffect } from "#enums/status-effect";
|
||||||
|
import type { WeatherType } from "#enums/weather-type";
|
||||||
|
import type { BattlerTag } from "../battler-tags";
|
||||||
|
import type Move from "../moves/move";
|
||||||
|
import type { PokemonMove } from "../moves/pokemon-move";
|
||||||
|
import type { TerrainType } from "../terrain";
|
||||||
|
import type { Weather } from "../weather";
|
||||||
|
import type {
|
||||||
|
PostBattleInitAbAttr,
|
||||||
|
PreDefendAbAttr,
|
||||||
|
PostDefendAbAttr,
|
||||||
|
PostMoveUsedAbAttr,
|
||||||
|
StatMultiplierAbAttr,
|
||||||
|
AllyStatMultiplierAbAttr,
|
||||||
|
PostSetStatusAbAttr,
|
||||||
|
PostDamageAbAttr,
|
||||||
|
FieldMultiplyStatAbAttr,
|
||||||
|
PreAttackAbAttr,
|
||||||
|
ExecutedMoveAbAttr,
|
||||||
|
PostAttackAbAttr,
|
||||||
|
PostKnockOutAbAttr,
|
||||||
|
PostVictoryAbAttr,
|
||||||
|
PostSummonAbAttr,
|
||||||
|
PreSummonAbAttr,
|
||||||
|
PreSwitchOutAbAttr,
|
||||||
|
PreLeaveFieldAbAttr,
|
||||||
|
PreStatStageChangeAbAttr,
|
||||||
|
PostStatStageChangeAbAttr,
|
||||||
|
PreSetStatusAbAttr,
|
||||||
|
PreApplyBattlerTagAbAttr,
|
||||||
|
PreWeatherEffectAbAttr,
|
||||||
|
PreWeatherDamageAbAttr,
|
||||||
|
PostTurnAbAttr,
|
||||||
|
PostWeatherChangeAbAttr,
|
||||||
|
PostWeatherLapseAbAttr,
|
||||||
|
PostTerrainChangeAbAttr,
|
||||||
|
CheckTrappedAbAttr,
|
||||||
|
PostBattleAbAttr,
|
||||||
|
PostFaintAbAttr,
|
||||||
|
PostItemLostAbAttr,
|
||||||
|
} from "./ability";
|
||||||
|
|
||||||
|
function applySingleAbAttrs<T extends AbAttrString>(
|
||||||
|
pokemon: Pokemon,
|
||||||
|
passive: boolean,
|
||||||
|
attrType: T,
|
||||||
|
applyFunc: AbAttrApplyFunc<AbAttrMap[T]>,
|
||||||
|
successFunc: AbAttrSuccessFunc<AbAttrMap[T]>,
|
||||||
|
args: any[],
|
||||||
|
gainedMidTurn = false,
|
||||||
|
simulated = false,
|
||||||
|
messages: string[] = [],
|
||||||
|
) {
|
||||||
|
if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
|
||||||
|
if (
|
||||||
|
gainedMidTurn &&
|
||||||
|
ability.getAttrs(attrType).some(attr => {
|
||||||
|
attr.is("PostSummonAbAttr") && !attr.shouldActivateOnGain();
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attr of ability.getAttrs(attrType)) {
|
||||||
|
const condition = attr.getCondition();
|
||||||
|
let abShown = false;
|
||||||
|
if ((condition && !condition(pokemon)) || !successFunc(attr, passive)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalScene.phaseManager.setPhaseQueueSplice();
|
||||||
|
|
||||||
|
if (attr.showAbility && !simulated) {
|
||||||
|
globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, true);
|
||||||
|
abShown = true;
|
||||||
|
}
|
||||||
|
const message = attr.getTriggerMessage(pokemon, ability.name, args);
|
||||||
|
if (message) {
|
||||||
|
if (!simulated) {
|
||||||
|
globalScene.phaseManager.queueMessage(message);
|
||||||
|
}
|
||||||
|
messages.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFunc(attr, passive);
|
||||||
|
|
||||||
|
if (abShown) {
|
||||||
|
globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!simulated) {
|
||||||
|
pokemon.waveData.abilitiesApplied.add(ability.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAbAttrsInternal<T extends AbAttrString>(
|
||||||
|
attrType: T,
|
||||||
|
pokemon: Pokemon | null,
|
||||||
|
applyFunc: AbAttrApplyFunc<AbAttrMap[T]>,
|
||||||
|
successFunc: AbAttrSuccessFunc<AbAttrMap[T]>,
|
||||||
|
args: any[],
|
||||||
|
simulated = false,
|
||||||
|
messages: string[] = [],
|
||||||
|
gainedMidTurn = false,
|
||||||
|
) {
|
||||||
|
for (const passive of [false, true]) {
|
||||||
|
if (pokemon) {
|
||||||
|
applySingleAbAttrs(pokemon, passive, attrType, applyFunc, successFunc, args, gainedMidTurn, simulated, messages);
|
||||||
|
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyAbAttrs<T extends AbAttrString>(
|
||||||
|
attrType: T,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
cancelled: BooleanHolder | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal<T>(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
// @ts-expect-error: TODO: fix the error on `cancelled`
|
||||||
|
(attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args),
|
||||||
|
(attr, passive) => attr.canApply(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Improve the type signatures of the following methods / refactor the apply methods
|
||||||
|
|
||||||
|
export function applyPostBattleInitAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostBattleInitAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostBattleInitAbAttr).applyPostBattleInit(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostBattleInitAbAttr).canApplyPostBattleInit(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreDefendAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreDefendAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
attacker: Pokemon,
|
||||||
|
move: Move | null,
|
||||||
|
cancelled: BooleanHolder | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreDefendAbAttr).applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreDefendAbAttr).canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostDefendAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostDefendAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
attacker: Pokemon,
|
||||||
|
move: Move,
|
||||||
|
hitResult: HitResult | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostDefendAbAttr).applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostDefendAbAttr).canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostMoveUsedAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostMoveUsedAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
move: PokemonMove,
|
||||||
|
source: Pokemon,
|
||||||
|
targets: BattlerIndex[],
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, _passive) => (attr as PostMoveUsedAbAttr).applyPostMoveUsed(pokemon, move, source, targets, simulated, args),
|
||||||
|
(attr, _passive) =>
|
||||||
|
(attr as PostMoveUsedAbAttr).canApplyPostMoveUsed(pokemon, move, source, targets, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends StatMultiplierAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
stat: BattleStat,
|
||||||
|
statValue: NumberHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as StatMultiplierAbAttr).applyStatStage(pokemon, passive, simulated, stat, statValue, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as StatMultiplierAbAttr).canApplyStatStage(pokemon, passive, simulated, stat, statValue, args),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies an ally's Stat multiplier attribute
|
||||||
|
* @param attrType - {@linkcode AllyStatMultiplierAbAttr} should always be AllyStatMultiplierAbAttr for the time being
|
||||||
|
* @param pokemon - The {@linkcode Pokemon} with the ability
|
||||||
|
* @param stat - The type of the checked {@linkcode Stat}
|
||||||
|
* @param statValue - {@linkcode NumberHolder} containing the value of the checked stat
|
||||||
|
* @param checkedPokemon - The {@linkcode Pokemon} with the checked stat
|
||||||
|
* @param ignoreAbility - Whether or not the ability should be ignored by the pokemon or its move.
|
||||||
|
* @param args - unused
|
||||||
|
*/
|
||||||
|
export function applyAllyStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends AllyStatMultiplierAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
stat: BattleStat,
|
||||||
|
statValue: NumberHolder,
|
||||||
|
simulated = false,
|
||||||
|
checkedPokemon: Pokemon,
|
||||||
|
ignoreAbility: boolean,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as AllyStatMultiplierAbAttr).applyAllyStat(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
simulated,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
checkedPokemon,
|
||||||
|
ignoreAbility,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as AllyStatMultiplierAbAttr).canApplyAllyStat(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
simulated,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
checkedPokemon,
|
||||||
|
ignoreAbility,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostSetStatusAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostSetStatusAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
effect: StatusEffect,
|
||||||
|
sourcePokemon?: Pokemon | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostSetStatusAbAttr).applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostSetStatusAbAttr).canApplyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostDamageAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostDamageAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
damage: number,
|
||||||
|
_passive: boolean,
|
||||||
|
simulated = false,
|
||||||
|
args: any[],
|
||||||
|
source?: Pokemon,
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostDamageAbAttr).applyPostDamage(pokemon, damage, passive, simulated, args, source),
|
||||||
|
(attr, passive) => (attr as PostDamageAbAttr).canApplyPostDamage(pokemon, damage, passive, simulated, args, source),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Applies a field Stat multiplier attribute
|
||||||
|
* @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being
|
||||||
|
* @param pokemon {@linkcode Pokemon} the Pokemon applying this ability
|
||||||
|
* @param stat {@linkcode Stat} the type of the checked stat
|
||||||
|
* @param statValue {@linkcode NumberHolder} the value of the checked stat
|
||||||
|
* @param checkedPokemon {@linkcode Pokemon} the Pokemon with the checked stat
|
||||||
|
* @param hasApplied {@linkcode BooleanHolder} whether or not a FieldMultiplyBattleStatAbAttr has already affected this stat
|
||||||
|
* @param args unused
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function applyFieldStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends FieldMultiplyStatAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
stat: Stat,
|
||||||
|
statValue: NumberHolder,
|
||||||
|
checkedPokemon: Pokemon,
|
||||||
|
hasApplied: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as FieldMultiplyStatAbAttr).applyFieldStat(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
simulated,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
checkedPokemon,
|
||||||
|
hasApplied,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as FieldMultiplyStatAbAttr).canApplyFieldStat(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
simulated,
|
||||||
|
stat,
|
||||||
|
statValue,
|
||||||
|
checkedPokemon,
|
||||||
|
hasApplied,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreAttackAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreAttackAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
defender: Pokemon | null,
|
||||||
|
move: Move,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PreAttackAbAttr).applyPreAttack(pokemon, passive, simulated, defender, move, args),
|
||||||
|
(attr, passive) => (attr as PreAttackAbAttr).canApplyPreAttack(pokemon, passive, simulated, defender, move, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyExecutedMoveAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends ExecutedMoveAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
attr => (attr as ExecutedMoveAbAttr).applyExecutedMove(pokemon, simulated),
|
||||||
|
attr => (attr as ExecutedMoveAbAttr).canApplyExecutedMove(pokemon, simulated),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostAttackAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostAttackAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
defender: Pokemon,
|
||||||
|
move: Move,
|
||||||
|
hitResult: HitResult | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostAttackAbAttr).applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostAttackAbAttr).canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostKnockOutAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostKnockOutAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
knockedOut: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostKnockOutAbAttr).applyPostKnockOut(pokemon, passive, simulated, knockedOut, args),
|
||||||
|
(attr, passive) => (attr as PostKnockOutAbAttr).canApplyPostKnockOut(pokemon, passive, simulated, knockedOut, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostVictoryAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostVictoryAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostVictoryAbAttr).applyPostVictory(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostVictoryAbAttr).canApplyPostVictory(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostSummonAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostSummonAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
passive = false,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applySingleAbAttrs(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
attrType,
|
||||||
|
(attr, passive) => (attr as PostSummonAbAttr).applyPostSummon(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostSummonAbAttr).canApplyPostSummon(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
false,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreSummonAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreSummonAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PreSummonAbAttr).applyPreSummon(pokemon, passive, args),
|
||||||
|
(attr, passive) => (attr as PreSummonAbAttr).canApplyPreSummon(pokemon, passive, args),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreSwitchOutAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreSwitchOutAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PreSwitchOutAbAttr).applyPreSwitchOut(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PreSwitchOutAbAttr).canApplyPreSwitchOut(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreLeaveFieldAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreLeaveFieldAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PreLeaveFieldAbAttr).applyPreLeaveField(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PreLeaveFieldAbAttr).canApplyPreLeaveField(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreStatStageChangeAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreStatStageChangeAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon | null,
|
||||||
|
stat: BattleStat,
|
||||||
|
cancelled: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreStatStageChangeAbAttr).applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreStatStageChangeAbAttr).canApplyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostStatStageChangeAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostStatStageChangeAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
stats: BattleStat[],
|
||||||
|
stages: number,
|
||||||
|
selfTarget: boolean,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, _passive) =>
|
||||||
|
(attr as PostStatStageChangeAbAttr).applyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args),
|
||||||
|
(attr, _passive) =>
|
||||||
|
(attr as PostStatStageChangeAbAttr).canApplyPostStatStageChange(
|
||||||
|
pokemon,
|
||||||
|
simulated,
|
||||||
|
stats,
|
||||||
|
stages,
|
||||||
|
selfTarget,
|
||||||
|
args,
|
||||||
|
),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreSetStatusAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreSetStatusAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
effect: StatusEffect | undefined,
|
||||||
|
cancelled: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreSetStatusAbAttr).applyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreSetStatusAbAttr).canApplyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreApplyBattlerTagAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreApplyBattlerTagAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
tag: BattlerTag,
|
||||||
|
cancelled: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreApplyBattlerTagAbAttr).applyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreApplyBattlerTagAbAttr).canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPreWeatherEffectAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PreWeatherEffectAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
weather: Weather | null,
|
||||||
|
cancelled: BooleanHolder,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreWeatherDamageAbAttr).applyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PreWeatherDamageAbAttr).canApplyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostTurnAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostTurnAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostTurnAbAttr).applyPostTurn(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostTurnAbAttr).canApplyPostTurn(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostWeatherChangeAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostWeatherChangeAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
weather: WeatherType,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostWeatherChangeAbAttr).applyPostWeatherChange(pokemon, passive, simulated, weather, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostWeatherChangeAbAttr).canApplyPostWeatherChange(pokemon, passive, simulated, weather, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostWeatherLapseAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostWeatherLapseAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
weather: Weather | null,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostWeatherLapseAbAttr).applyPostWeatherLapse(pokemon, passive, simulated, weather, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostWeatherLapseAbAttr).canApplyPostWeatherLapse(pokemon, passive, simulated, weather, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostTerrainChangeAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostTerrainChangeAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
terrain: TerrainType,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostTerrainChangeAbAttr).applyPostTerrainChange(pokemon, passive, simulated, terrain, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostTerrainChangeAbAttr).canApplyPostTerrainChange(pokemon, passive, simulated, terrain, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyCheckTrappedAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends CheckTrappedAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
trapped: BooleanHolder,
|
||||||
|
otherPokemon: Pokemon,
|
||||||
|
messages: string[],
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as CheckTrappedAbAttr).applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as CheckTrappedAbAttr).canApplyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
messages,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostBattleAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostBattleAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) => (attr as PostBattleAbAttr).applyPostBattle(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => (attr as PostBattleAbAttr).canApplyPostBattle(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostFaintAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostFaintAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
attacker?: Pokemon,
|
||||||
|
move?: Move,
|
||||||
|
hitResult?: HitResult,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostFaintAbAttr).applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||||
|
(attr, passive) =>
|
||||||
|
(attr as PostFaintAbAttr).canApplyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||||
|
args,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPostItemLostAbAttrs<K extends AbAttrString>(
|
||||||
|
attrType: AbAttrMap[K] extends PostItemLostAbAttr ? K : never,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
simulated = false,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyAbAttrsInternal(
|
||||||
|
attrType,
|
||||||
|
pokemon,
|
||||||
|
(attr, _passive) => (attr as PostItemLostAbAttr).applyPostItemLost(pokemon, simulated, args),
|
||||||
|
(attr, _passive) => (attr as PostItemLostAbAttr).canApplyPostItemLost(pokemon, simulated, args),
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies abilities when they become active mid-turn (ability switch)
|
||||||
|
*
|
||||||
|
* Ignores passives as they don't change and shouldn't be reapplied when main abilities change
|
||||||
|
*/
|
||||||
|
export function applyOnGainAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void {
|
||||||
|
applySingleAbAttrs(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
"PostSummonAbAttr",
|
||||||
|
(attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args),
|
||||||
|
(attr, passive) => attr.canApplyPostSummon(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Applies ability attributes which activate when the ability is lost or suppressed (i.e. primal weather)
|
||||||
|
*/
|
||||||
|
export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void {
|
||||||
|
applySingleAbAttrs(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
"PreLeaveFieldAbAttr",
|
||||||
|
(attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [...args, true]),
|
||||||
|
(attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, [...args, true]),
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
|
||||||
|
applySingleAbAttrs(
|
||||||
|
pokemon,
|
||||||
|
passive,
|
||||||
|
"IllusionBreakAbAttr",
|
||||||
|
(attr, passive) => attr.apply(pokemon, passive, simulated, null, args),
|
||||||
|
(attr, passive) => attr.canApply(pokemon, passive, simulated, args),
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
simulated,
|
||||||
|
);
|
||||||
|
}
|
@ -7,35 +7,20 @@ import { MoveTarget } from "#enums/MoveTarget";
|
|||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { HitResult } from "#app/field/pokemon";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import {
|
import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "./abilities/apply-ab-attrs";
|
||||||
BlockNonDirectDamageAbAttr,
|
|
||||||
InfiltratorAbAttr,
|
|
||||||
PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr,
|
|
||||||
ProtectStatAbAttr,
|
|
||||||
applyAbAttrs,
|
|
||||||
applyOnGainAbAttrs,
|
|
||||||
applyOnLoseAbAttrs,
|
|
||||||
} from "#app/data/abilities/ability";
|
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim } from "#app/data/battle-anims";
|
||||||
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
|
||||||
|
|
||||||
export enum ArenaTagSide {
|
|
||||||
BOTH,
|
|
||||||
PLAYER,
|
|
||||||
ENEMY,
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class ArenaTag {
|
export abstract class ArenaTag {
|
||||||
constructor(
|
constructor(
|
||||||
@ -54,7 +39,7 @@ export abstract class ArenaTag {
|
|||||||
|
|
||||||
onRemove(_arena: Arena, quiet = false): void {
|
onRemove(_arena: Arena, quiet = false): void {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
{ moveName: this.getMoveName() },
|
{ moveName: this.getMoveName() },
|
||||||
@ -126,7 +111,7 @@ export class MistTag extends ArenaTag {
|
|||||||
const source = globalScene.getPokemonById(this.sourceId);
|
const source = globalScene.getPokemonById(this.sourceId);
|
||||||
|
|
||||||
if (!quiet && source) {
|
if (!quiet && source) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:mistOnAdd", {
|
i18next.t("arenaTag:mistOnAdd", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||||
}),
|
}),
|
||||||
@ -152,7 +137,7 @@ export class MistTag extends ArenaTag {
|
|||||||
if (attacker) {
|
if (attacker) {
|
||||||
const bypassed = new BooleanHolder(false);
|
const bypassed = new BooleanHolder(false);
|
||||||
// TODO: Allow this to be simulated
|
// TODO: Allow this to be simulated
|
||||||
applyAbAttrs(InfiltratorAbAttr, attacker, null, false, bypassed);
|
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||||
if (bypassed.value) {
|
if (bypassed.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -161,7 +146,7 @@ export class MistTag extends ArenaTag {
|
|||||||
cancelled.value = true;
|
cancelled.value = true;
|
||||||
|
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:mistApply"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mistApply"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -217,7 +202,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
|
|||||||
): boolean {
|
): boolean {
|
||||||
if (this.weakenedCategories.includes(moveCategory)) {
|
if (this.weakenedCategories.includes(moveCategory)) {
|
||||||
const bypassed = new BooleanHolder(false);
|
const bypassed = new BooleanHolder(false);
|
||||||
applyAbAttrs(InfiltratorAbAttr, attacker, null, false, bypassed);
|
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||||
if (bypassed.value) {
|
if (bypassed.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -239,7 +224,7 @@ class ReflectTag extends WeakenMoveScreenTag {
|
|||||||
|
|
||||||
onAdd(_arena: Arena, quiet = false): void {
|
onAdd(_arena: Arena, quiet = false): void {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -259,7 +244,7 @@ class LightScreenTag extends WeakenMoveScreenTag {
|
|||||||
|
|
||||||
onAdd(_arena: Arena, quiet = false): void {
|
onAdd(_arena: Arena, quiet = false): void {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -282,7 +267,7 @@ class AuroraVeilTag extends WeakenMoveScreenTag {
|
|||||||
|
|
||||||
onAdd(_arena: Arena, quiet = false): void {
|
onAdd(_arena: Arena, quiet = false): void {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -318,7 +303,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
{ moveName: super.getMoveName() },
|
{ moveName: super.getMoveName() },
|
||||||
@ -355,7 +340,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
|||||||
isProtected.value = true;
|
isProtected.value = true;
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
new CommonBattleAnim(CommonAnim.PROTECT, defender).play();
|
new CommonBattleAnim(CommonAnim.PROTECT, defender).play();
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:conditionalProtectApply", {
|
i18next.t("arenaTag:conditionalProtectApply", {
|
||||||
moveName: super.getMoveName(),
|
moveName: super.getMoveName(),
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(defender),
|
pokemonNameWithAffix: getPokemonNameWithAffix(defender),
|
||||||
@ -381,9 +366,9 @@ export class ConditionalProtectTag extends ArenaTag {
|
|||||||
*/
|
*/
|
||||||
const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
|
const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
|
||||||
const move = allMoves[moveId];
|
const move = allMoves[moveId];
|
||||||
const effectPhase = globalScene.getCurrentPhase();
|
const effectPhase = globalScene.phaseManager.getCurrentPhase();
|
||||||
|
|
||||||
if (effectPhase instanceof MoveEffectPhase) {
|
if (effectPhase?.is("MoveEffectPhase")) {
|
||||||
const attacker = effectPhase.getUserPokemon();
|
const attacker = effectPhase.getUserPokemon();
|
||||||
if (attacker) {
|
if (attacker) {
|
||||||
return move.getPriority(attacker) > 0;
|
return move.getPriority(attacker) > 0;
|
||||||
@ -458,7 +443,7 @@ class MatBlockTag extends ConditionalProtectTag {
|
|||||||
if (this.sourceId) {
|
if (this.sourceId) {
|
||||||
const source = globalScene.getPokemonById(this.sourceId);
|
const source = globalScene.getPokemonById(this.sourceId);
|
||||||
if (source) {
|
if (source) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:matBlockOnAdd", {
|
i18next.t("arenaTag:matBlockOnAdd", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||||
}),
|
}),
|
||||||
@ -517,7 +502,7 @@ export class NoCritTag extends ArenaTag {
|
|||||||
|
|
||||||
/** Queues a message upon adding this effect to the field */
|
/** Queues a message upon adding this effect to the field */
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, {
|
i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, {
|
||||||
moveName: this.getMoveName(),
|
moveName: this.getMoveName(),
|
||||||
}),
|
}),
|
||||||
@ -527,7 +512,7 @@ export class NoCritTag extends ArenaTag {
|
|||||||
/** Queues a message upon removing this effect from the field */
|
/** Queues a message upon removing this effect from the field */
|
||||||
onRemove(_arena: Arena): void {
|
onRemove(_arena: Arena): void {
|
||||||
const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct?
|
const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct?
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:noCritOnRemove", {
|
i18next.t("arenaTag:noCritOnRemove", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined),
|
pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined),
|
||||||
moveName: this.getMoveName(),
|
moveName: this.getMoveName(),
|
||||||
@ -567,8 +552,8 @@ class WishTag extends ArenaTag {
|
|||||||
onRemove(_arena: Arena): void {
|
onRemove(_arena: Arena): void {
|
||||||
const target = globalScene.getField()[this.battlerIndex];
|
const target = globalScene.getField()[this.battlerIndex];
|
||||||
if (target?.isActive(true)) {
|
if (target?.isActive(true)) {
|
||||||
globalScene.queueMessage(this.triggerMessage);
|
globalScene.phaseManager.queueMessage(this.triggerMessage);
|
||||||
globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false));
|
globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), this.healHp, null, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -621,11 +606,11 @@ class MudSportTag extends WeakenMoveTypeTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(_arena: Arena): void {
|
onRemove(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:mudSportOnRemove"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mudSportOnRemove"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,11 +624,11 @@ class WaterSportTag extends WeakenMoveTypeTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(_arena: Arena): void {
|
onRemove(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:waterSportOnRemove"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:waterSportOnRemove"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,7 +644,7 @@ export class IonDelugeTag extends ArenaTag {
|
|||||||
|
|
||||||
/** Queues an on-add message */
|
/** Queues an on-add message */
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd"));
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(_arena: Arena): void {} // Removes default on-remove message
|
onRemove(_arena: Arena): void {} // Removes default on-remove message
|
||||||
@ -758,7 +743,7 @@ class SpikesTag extends ArenaTrapTag {
|
|||||||
|
|
||||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||||
if (!quiet && source) {
|
if (!quiet && source) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:spikesOnAdd", {
|
i18next.t("arenaTag:spikesOnAdd", {
|
||||||
moveName: this.getMoveName(),
|
moveName: this.getMoveName(),
|
||||||
opponentDesc: source.getOpponentDescriptor(),
|
opponentDesc: source.getOpponentDescriptor(),
|
||||||
@ -773,7 +758,7 @@ class SpikesTag extends ArenaTrapTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
if (simulated || cancelled.value) {
|
if (simulated || cancelled.value) {
|
||||||
return !cancelled.value;
|
return !cancelled.value;
|
||||||
}
|
}
|
||||||
@ -781,7 +766,7 @@ class SpikesTag extends ArenaTrapTag {
|
|||||||
const damageHpRatio = 1 / (10 - 2 * this.layers);
|
const damageHpRatio = 1 / (10 - 2 * this.layers);
|
||||||
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
||||||
|
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:spikesActivateTrap", {
|
i18next.t("arenaTag:spikesActivateTrap", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
}),
|
}),
|
||||||
@ -811,7 +796,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
|||||||
|
|
||||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||||
if (!quiet && source) {
|
if (!quiet && source) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:toxicSpikesOnAdd", {
|
i18next.t("arenaTag:toxicSpikesOnAdd", {
|
||||||
moveName: this.getMoveName(),
|
moveName: this.getMoveName(),
|
||||||
opponentDesc: source.getOpponentDescriptor(),
|
opponentDesc: source.getOpponentDescriptor(),
|
||||||
@ -834,7 +819,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
|||||||
if (pokemon.isOfType(PokemonType.POISON)) {
|
if (pokemon.isOfType(PokemonType.POISON)) {
|
||||||
this.neutralized = true;
|
this.neutralized = true;
|
||||||
if (globalScene.arena.removeTag(this.tagType)) {
|
if (globalScene.arena.removeTag(this.tagType)) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:toxicSpikesActivateTrapPoison", {
|
i18next.t("arenaTag:toxicSpikesActivateTrapPoison", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
moveName: this.getMoveName(),
|
moveName: this.getMoveName(),
|
||||||
@ -891,8 +876,13 @@ export class DelayedAttackTag extends ArenaTag {
|
|||||||
const ret = super.lapse(arena);
|
const ret = super.lapse(arena);
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
globalScene.unshiftPhase(
|
// TODO: This should not add to move history (for Spite)
|
||||||
new MoveEffectPhase(this.sourceId!, [this.targetIndex], allMoves[this.sourceMove!], false, true),
|
globalScene.phaseManager.unshiftNew(
|
||||||
|
"MoveEffectPhase",
|
||||||
|
this.sourceId!,
|
||||||
|
[this.targetIndex],
|
||||||
|
allMoves[this.sourceMove!],
|
||||||
|
MoveUseMode.FOLLOW_UP,
|
||||||
); // TODO: are those bangs correct?
|
); // TODO: are those bangs correct?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -917,7 +907,7 @@ class StealthRockTag extends ArenaTrapTag {
|
|||||||
|
|
||||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||||
if (!quiet && source) {
|
if (!quiet && source) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:stealthRockOnAdd", {
|
i18next.t("arenaTag:stealthRockOnAdd", {
|
||||||
opponentDesc: source.getOpponentDescriptor(),
|
opponentDesc: source.getOpponentDescriptor(),
|
||||||
}),
|
}),
|
||||||
@ -956,7 +946,7 @@ class StealthRockTag extends ArenaTrapTag {
|
|||||||
|
|
||||||
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||||
if (cancelled.value) {
|
if (cancelled.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -971,7 +961,7 @@ class StealthRockTag extends ArenaTrapTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:stealthRockActivateTrap", {
|
i18next.t("arenaTag:stealthRockActivateTrap", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
}),
|
}),
|
||||||
@ -1001,7 +991,7 @@ class StickyWebTag extends ArenaTrapTag {
|
|||||||
super.onAdd(arena);
|
super.onAdd(arena);
|
||||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||||
if (!quiet && source) {
|
if (!quiet && source) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:stickyWebOnAdd", {
|
i18next.t("arenaTag:stickyWebOnAdd", {
|
||||||
moveName: this.getMoveName(),
|
moveName: this.getMoveName(),
|
||||||
opponentDesc: source.getOpponentDescriptor(),
|
opponentDesc: source.getOpponentDescriptor(),
|
||||||
@ -1013,21 +1003,21 @@ class StickyWebTag extends ArenaTrapTag {
|
|||||||
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
||||||
if (pokemon.isGrounded()) {
|
if (pokemon.isGrounded()) {
|
||||||
const cancelled = new BooleanHolder(false);
|
const cancelled = new BooleanHolder(false);
|
||||||
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
|
applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled);
|
||||||
|
|
||||||
if (simulated) {
|
if (simulated) {
|
||||||
return !cancelled.value;
|
return !cancelled.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cancelled.value) {
|
if (!cancelled.value) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:stickyWebActivateTrap", {
|
i18next.t("arenaTag:stickyWebActivateTrap", {
|
||||||
pokemonName: pokemon.getNameToRender(),
|
pokemonName: pokemon.getNameToRender(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const stages = new NumberHolder(-1);
|
const stages = new NumberHolder(-1);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(
|
"StatStageChangePhase",
|
||||||
pokemon.getBattlerIndex(),
|
pokemon.getBattlerIndex(),
|
||||||
false,
|
false,
|
||||||
[Stat.SPD],
|
[Stat.SPD],
|
||||||
@ -1038,7 +1028,6 @@ class StickyWebTag extends ArenaTrapTag {
|
|||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1074,7 +1063,7 @@ export class TrickRoomTag extends ArenaTag {
|
|||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||||
if (source) {
|
if (source) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:trickRoomOnAdd", {
|
i18next.t("arenaTag:trickRoomOnAdd", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||||
}),
|
}),
|
||||||
@ -1083,7 +1072,7 @@ export class TrickRoomTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRemove(_arena: Arena): void {
|
onRemove(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnRemove"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:trickRoomOnRemove"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1098,7 +1087,7 @@ export class GravityTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
||||||
globalScene.getField(true).forEach(pokemon => {
|
globalScene.getField(true).forEach(pokemon => {
|
||||||
if (pokemon !== null) {
|
if (pokemon !== null) {
|
||||||
pokemon.removeTag(BattlerTagType.FLOATING);
|
pokemon.removeTag(BattlerTagType.FLOATING);
|
||||||
@ -1111,7 +1100,7 @@ export class GravityTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRemove(_arena: Arena): void {
|
onRemove(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:gravityOnRemove"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnRemove"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,7 +1116,7 @@ class TailwindTag extends ArenaTag {
|
|||||||
|
|
||||||
onAdd(_arena: Arena, quiet = false): void {
|
onAdd(_arena: Arena, quiet = false): void {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -1136,31 +1125,33 @@ class TailwindTag extends ArenaTag {
|
|||||||
|
|
||||||
const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable!
|
const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable!
|
||||||
const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? [];
|
const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? [];
|
||||||
|
const phaseManager = globalScene.phaseManager;
|
||||||
|
|
||||||
for (const pokemon of party) {
|
for (const pokemon of party) {
|
||||||
// Apply the CHARGED tag to party members with the WIND_POWER ability
|
// Apply the CHARGED tag to party members with the WIND_POWER ability
|
||||||
if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
|
if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
|
||||||
pokemon.addTag(BattlerTagType.CHARGED);
|
pokemon.addTag(BattlerTagType.CHARGED);
|
||||||
globalScene.queueMessage(
|
phaseManager.queueMessage(
|
||||||
i18next.t("abilityTriggers:windPowerCharged", {
|
i18next.t("abilityTriggers:windPowerCharged", {
|
||||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||||
moveName: this.getMoveName(),
|
moveName: this.getMoveName(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raise attack by one stage if party member has WIND_RIDER ability
|
// Raise attack by one stage if party member has WIND_RIDER ability
|
||||||
// TODO: Ability displays should be handled by the ability
|
// TODO: Ability displays should be handled by the ability
|
||||||
if (pokemon.hasAbility(AbilityId.WIND_RIDER)) {
|
if (pokemon.hasAbility(AbilityId.WIND_RIDER)) {
|
||||||
globalScene.queueAbilityDisplay(pokemon, false, true);
|
phaseManager.queueAbilityDisplay(pokemon, false, true);
|
||||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true));
|
phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true);
|
||||||
globalScene.queueAbilityDisplay(pokemon, false, false);
|
phaseManager.queueAbilityDisplay(pokemon, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(_arena: Arena, quiet = false): void {
|
onRemove(_arena: Arena, quiet = false): void {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -1179,11 +1170,11 @@ class HappyHourTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(_arena: Arena): void {
|
onRemove(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:happyHourOnRemove"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:happyHourOnRemove"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1193,7 +1184,7 @@ class SafeguardTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -1201,7 +1192,7 @@ class SafeguardTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRemove(_arena: Arena): void {
|
onRemove(_arena: Arena): void {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -1237,7 +1228,7 @@ class ImprisonTag extends ArenaTrapTag {
|
|||||||
p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId);
|
p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("battlerTags:imprisonOnAdd", {
|
i18next.t("battlerTags:imprisonOnAdd", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||||
}),
|
}),
|
||||||
@ -1294,7 +1285,7 @@ class FireGrassPledgeTag extends ArenaTag {
|
|||||||
|
|
||||||
override onAdd(_arena: Arena): void {
|
override onAdd(_arena: Arena): void {
|
||||||
// "A sea of fire enveloped your/the opposing team!"
|
// "A sea of fire enveloped your/the opposing team!"
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -1309,14 +1300,17 @@ class FireGrassPledgeTag extends ArenaTag {
|
|||||||
.filter(pokemon => !pokemon.isOfType(PokemonType.FIRE) && !pokemon.switchOutStatus)
|
.filter(pokemon => !pokemon.isOfType(PokemonType.FIRE) && !pokemon.switchOutStatus)
|
||||||
.forEach(pokemon => {
|
.forEach(pokemon => {
|
||||||
// "{pokemonNameWithAffix} was hurt by the sea of fire!"
|
// "{pokemonNameWithAffix} was hurt by the sea of fire!"
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:fireGrassPledgeLapse", {
|
i18next.t("arenaTag:fireGrassPledgeLapse", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// TODO: Replace this with a proper animation
|
// TODO: Replace this with a proper animation
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM),
|
"CommonAnimPhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
CommonAnim.MAGMA_STORM,
|
||||||
);
|
);
|
||||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||||
});
|
});
|
||||||
@ -1339,7 +1333,7 @@ class WaterFirePledgeTag extends ArenaTag {
|
|||||||
|
|
||||||
override onAdd(_arena: Arena): void {
|
override onAdd(_arena: Arena): void {
|
||||||
// "A rainbow appeared in the sky on your/the opposing team's side!"
|
// "A rainbow appeared in the sky on your/the opposing team's side!"
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -1373,7 +1367,7 @@ class GrassWaterPledgeTag extends ArenaTag {
|
|||||||
|
|
||||||
override onAdd(_arena: Arena): void {
|
override onAdd(_arena: Arena): void {
|
||||||
// "A swamp enveloped your/the opposing team!"
|
// "A swamp enveloped your/the opposing team!"
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||||
),
|
),
|
||||||
@ -1394,7 +1388,7 @@ export class FairyLockTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAdd(_arena: Arena): void {
|
onAdd(_arena: Arena): void {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1443,20 +1437,20 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
|||||||
// Could have a custom message that plays when a specific pokemon's NG ends? This entire thing exists due to passives after all
|
// Could have a custom message that plays when a specific pokemon's NG ends? This entire thing exists due to passives after all
|
||||||
const setter = globalScene
|
const setter = globalScene
|
||||||
.getField()
|
.getField()
|
||||||
.filter(p => p?.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false))[0];
|
.filter(p => p?.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false))[0];
|
||||||
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr));
|
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override onRemove(_arena: Arena, quiet = false) {
|
public override onRemove(_arena: Arena, quiet = false) {
|
||||||
this.beingRemoved = true;
|
this.beingRemoved = true;
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
|
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of globalScene.getField(true)) {
|
for (const pokemon of globalScene.getField(true)) {
|
||||||
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
|
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
|
||||||
if (pokemon && !pokemon.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false)) {
|
if (pokemon && !pokemon.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false)) {
|
||||||
[true, false].forEach(passive => applyOnGainAbAttrs(pokemon, passive));
|
[true, false].forEach(passive => applyOnGainAbAttrs(pokemon, passive));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1472,7 +1466,7 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
|||||||
|
|
||||||
private playActivationMessage(pokemon: Pokemon | null) {
|
private playActivationMessage(pokemon: Pokemon | null) {
|
||||||
if (pokemon) {
|
if (pokemon) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("arenaTag:neutralizingGasOnAdd", {
|
i18next.t("arenaTag:neutralizingGasOnAdd", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||||
}),
|
}),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
|
||||||
@ -5988,6 +5988,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.FEZANDIPITI,
|
SpeciesId.FEZANDIPITI,
|
||||||
SpeciesId.ARCHALUDON,
|
SpeciesId.ARCHALUDON,
|
||||||
SpeciesId.IRON_CROWN,
|
SpeciesId.IRON_CROWN,
|
||||||
|
SpeciesId.TERAPAGOS,
|
||||||
SpeciesId.ALOLA_RATICATE,
|
SpeciesId.ALOLA_RATICATE,
|
||||||
SpeciesId.ALOLA_RAICHU,
|
SpeciesId.ALOLA_RAICHU,
|
||||||
SpeciesId.ALOLA_SANDSLASH,
|
SpeciesId.ALOLA_SANDSLASH,
|
||||||
@ -16248,6 +16249,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.CALYREX,
|
SpeciesId.CALYREX,
|
||||||
SpeciesId.SANDY_SHOCKS,
|
SpeciesId.SANDY_SHOCKS,
|
||||||
SpeciesId.IRON_JUGULIS,
|
SpeciesId.IRON_JUGULIS,
|
||||||
|
SpeciesId.TERAPAGOS,
|
||||||
SpeciesId.ALOLA_DUGTRIO,
|
SpeciesId.ALOLA_DUGTRIO,
|
||||||
SpeciesId.GALAR_SLOWPOKE,
|
SpeciesId.GALAR_SLOWPOKE,
|
||||||
SpeciesId.GALAR_SLOWBRO,
|
SpeciesId.GALAR_SLOWBRO,
|
||||||
@ -39466,6 +39468,8 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.FARFETCHD,
|
SpeciesId.FARFETCHD,
|
||||||
SpeciesId.DODUO,
|
SpeciesId.DODUO,
|
||||||
SpeciesId.DODRIO,
|
SpeciesId.DODRIO,
|
||||||
|
SpeciesId.DEWGONG,
|
||||||
|
SpeciesId.GRIMER,
|
||||||
SpeciesId.MUK,
|
SpeciesId.MUK,
|
||||||
SpeciesId.GASTLY,
|
SpeciesId.GASTLY,
|
||||||
SpeciesId.HAUNTER,
|
SpeciesId.HAUNTER,
|
||||||
@ -39477,6 +39481,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.CUBONE,
|
SpeciesId.CUBONE,
|
||||||
SpeciesId.MAROWAK,
|
SpeciesId.MAROWAK,
|
||||||
SpeciesId.HITMONLEE,
|
SpeciesId.HITMONLEE,
|
||||||
|
SpeciesId.HITMONCHAN,
|
||||||
SpeciesId.LICKITUNG,
|
SpeciesId.LICKITUNG,
|
||||||
SpeciesId.TANGELA,
|
SpeciesId.TANGELA,
|
||||||
SpeciesId.GOLDEEN,
|
SpeciesId.GOLDEEN,
|
||||||
@ -48806,6 +48811,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.GARGANACL,
|
SpeciesId.GARGANACL,
|
||||||
SpeciesId.GLIMMET,
|
SpeciesId.GLIMMET,
|
||||||
SpeciesId.GLIMMORA,
|
SpeciesId.GLIMMORA,
|
||||||
|
SpeciesId.TERAPAGOS,
|
||||||
SpeciesId.ALOLA_GEODUDE,
|
SpeciesId.ALOLA_GEODUDE,
|
||||||
SpeciesId.ALOLA_GRAVELER,
|
SpeciesId.ALOLA_GRAVELER,
|
||||||
SpeciesId.ALOLA_GOLEM,
|
SpeciesId.ALOLA_GOLEM,
|
||||||
@ -53077,6 +53083,7 @@ export const tmSpecies: TmSpecies = {
|
|||||||
SpeciesId.MIRAIDON,
|
SpeciesId.MIRAIDON,
|
||||||
SpeciesId.ARCHALUDON,
|
SpeciesId.ARCHALUDON,
|
||||||
SpeciesId.IRON_CROWN,
|
SpeciesId.IRON_CROWN,
|
||||||
|
SpeciesId.TERAPAGOS,
|
||||||
[
|
[
|
||||||
SpeciesId.WORMADAM,
|
SpeciesId.WORMADAM,
|
||||||
"trash",
|
"trash",
|
||||||
|
@ -1,111 +1,22 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove } from "./moves/move";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { allMoves } from "./data-lists";
|
|
||||||
import { MoveFlags } from "#enums/MoveFlags";
|
import { MoveFlags } from "#enums/MoveFlags";
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils/common";
|
import {
|
||||||
import type { BattlerIndex } from "../battle";
|
type nil,
|
||||||
|
getFrameMs,
|
||||||
|
getEnumKeys,
|
||||||
|
getEnumValues,
|
||||||
|
animationFileName,
|
||||||
|
coerceArray,
|
||||||
|
isNullOrUndefined,
|
||||||
|
} from "#app/utils/common";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SubstituteTag } from "./battler-tags";
|
|
||||||
import { isNullOrUndefined } from "../utils/common";
|
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { EncounterAnim } from "#enums/encounter-anims";
|
import { EncounterAnim } from "#enums/encounter-anims";
|
||||||
|
import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
||||||
export enum AnimFrameTarget {
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
USER,
|
|
||||||
TARGET,
|
|
||||||
GRAPHIC,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AnimFocus {
|
|
||||||
TARGET = 1,
|
|
||||||
USER,
|
|
||||||
USER_TARGET,
|
|
||||||
SCREEN,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AnimBlendType {
|
|
||||||
NORMAL,
|
|
||||||
ADD,
|
|
||||||
SUBTRACT,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ChargeAnim {
|
|
||||||
FLY_CHARGING = 1000,
|
|
||||||
BOUNCE_CHARGING,
|
|
||||||
DIG_CHARGING,
|
|
||||||
FUTURE_SIGHT_CHARGING,
|
|
||||||
DIVE_CHARGING,
|
|
||||||
SOLAR_BEAM_CHARGING,
|
|
||||||
SHADOW_FORCE_CHARGING,
|
|
||||||
SKULL_BASH_CHARGING,
|
|
||||||
FREEZE_SHOCK_CHARGING,
|
|
||||||
SKY_DROP_CHARGING,
|
|
||||||
SKY_ATTACK_CHARGING,
|
|
||||||
ICE_BURN_CHARGING,
|
|
||||||
DOOM_DESIRE_CHARGING,
|
|
||||||
RAZOR_WIND_CHARGING,
|
|
||||||
PHANTOM_FORCE_CHARGING,
|
|
||||||
GEOMANCY_CHARGING,
|
|
||||||
SHADOW_BLADE_CHARGING,
|
|
||||||
SOLAR_BLADE_CHARGING,
|
|
||||||
BEAK_BLAST_CHARGING,
|
|
||||||
METEOR_BEAM_CHARGING,
|
|
||||||
ELECTRO_SHOT_CHARGING,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CommonAnim {
|
|
||||||
USE_ITEM = 2000,
|
|
||||||
HEALTH_UP,
|
|
||||||
TERASTALLIZE,
|
|
||||||
POISON = 2010,
|
|
||||||
TOXIC,
|
|
||||||
PARALYSIS,
|
|
||||||
SLEEP,
|
|
||||||
FROZEN,
|
|
||||||
BURN,
|
|
||||||
CONFUSION,
|
|
||||||
ATTRACT,
|
|
||||||
BIND,
|
|
||||||
WRAP,
|
|
||||||
CURSE_NO_GHOST,
|
|
||||||
LEECH_SEED,
|
|
||||||
FIRE_SPIN,
|
|
||||||
PROTECT,
|
|
||||||
COVET,
|
|
||||||
WHIRLPOOL,
|
|
||||||
BIDE,
|
|
||||||
SAND_TOMB,
|
|
||||||
QUICK_GUARD,
|
|
||||||
WIDE_GUARD,
|
|
||||||
CURSE,
|
|
||||||
MAGMA_STORM,
|
|
||||||
CLAMP,
|
|
||||||
SNAP_TRAP,
|
|
||||||
THUNDER_CAGE,
|
|
||||||
INFESTATION,
|
|
||||||
ORDER_UP_CURLY,
|
|
||||||
ORDER_UP_DROOPY,
|
|
||||||
ORDER_UP_STRETCHY,
|
|
||||||
RAGING_BULL_FIRE,
|
|
||||||
RAGING_BULL_WATER,
|
|
||||||
SALT_CURE,
|
|
||||||
POWDER,
|
|
||||||
SUNNY = 2100,
|
|
||||||
RAIN,
|
|
||||||
SANDSTORM,
|
|
||||||
HAIL,
|
|
||||||
SNOW,
|
|
||||||
WIND,
|
|
||||||
HEAVY_RAIN,
|
|
||||||
HARSH_SUN,
|
|
||||||
STRONG_WINDS,
|
|
||||||
MISTY_TERRAIN = 2110,
|
|
||||||
ELECTRIC_TERRAIN,
|
|
||||||
GRASSY_TERRAIN,
|
|
||||||
PSYCHIC_TERRAIN,
|
|
||||||
LOCK_ON = 2120,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AnimConfig {
|
export class AnimConfig {
|
||||||
public id: number;
|
public id: number;
|
||||||
@ -531,7 +442,7 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
|||||||
if (moveAnims.get(move) !== null) {
|
if (moveAnims.get(move) !== null) {
|
||||||
const chargeAnimSource = allMoves[move].isChargingMove()
|
const chargeAnimSource = allMoves[move].isChargingMove()
|
||||||
? allMoves[move]
|
? allMoves[move]
|
||||||
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
|
: (allMoves[move].getAttrs("DelayedAttackAttr")[0] ?? allMoves[move].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||||
if (chargeAnimSource && chargeAnims.get(chargeAnimSource.chargeAnim) === null) {
|
if (chargeAnimSource && chargeAnims.get(chargeAnimSource.chargeAnim) === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -542,10 +453,9 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
moveAnims.set(move, null);
|
moveAnims.set(move, null);
|
||||||
const defaultMoveAnim =
|
const defaultMoveAnim = allMoves[move].is("AttackMove")
|
||||||
allMoves[move] instanceof AttackMove
|
|
||||||
? MoveId.TACKLE
|
? MoveId.TACKLE
|
||||||
: allMoves[move] instanceof SelfStatusMove
|
: allMoves[move].is("SelfStatusMove")
|
||||||
? MoveId.FOCUS_ENERGY
|
? MoveId.FOCUS_ENERGY
|
||||||
: MoveId.TAIL_WHIP;
|
: MoveId.TAIL_WHIP;
|
||||||
|
|
||||||
@ -570,7 +480,7 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
|||||||
}
|
}
|
||||||
const chargeAnimSource = allMoves[move].isChargingMove()
|
const chargeAnimSource = allMoves[move].isChargingMove()
|
||||||
? allMoves[move]
|
? allMoves[move]
|
||||||
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
|
: (allMoves[move].getAttrs("DelayedAttackAttr")[0] ?? allMoves[move].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||||
if (chargeAnimSource) {
|
if (chargeAnimSource) {
|
||||||
initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
|
initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
|
||||||
} else {
|
} else {
|
||||||
@ -616,7 +526,7 @@ function logMissingMoveAnim(move: MoveId, ...optionalParams: any[]) {
|
|||||||
* @param encounterAnim one or more animations to fetch
|
* @param encounterAnim one or more animations to fetch
|
||||||
*/
|
*/
|
||||||
export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
|
export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
|
||||||
const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim];
|
const anims = coerceArray(encounterAnim);
|
||||||
const encounterAnimNames = getEnumKeys(EncounterAnim);
|
const encounterAnimNames = getEnumKeys(EncounterAnim);
|
||||||
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
|
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
|
||||||
for (const anim of anims) {
|
for (const anim of anims) {
|
||||||
@ -703,7 +613,7 @@ export function loadMoveAnimAssets(moveIds: MoveId[], startLoad?: boolean): Prom
|
|||||||
for (const moveId of moveIds) {
|
for (const moveId of moveIds) {
|
||||||
const chargeAnimSource = allMoves[moveId].isChargingMove()
|
const chargeAnimSource = allMoves[moveId].isChargingMove()
|
||||||
? allMoves[moveId]
|
? allMoves[moveId]
|
||||||
: (allMoves[moveId].getAttrs(DelayedAttackAttr)[0] ?? allMoves[moveId].getAttrs(BeakBlastHeaderAttr)[0]);
|
: (allMoves[moveId].getAttrs("DelayedAttackAttr")[0] ?? allMoves[moveId].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||||
if (chargeAnimSource) {
|
if (chargeAnimSource) {
|
||||||
const moveChargeAnims = chargeAnims.get(chargeAnimSource.chargeAnim);
|
const moveChargeAnims = chargeAnims.get(chargeAnimSource.chargeAnim);
|
||||||
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims![0]); // TODO: is the bang correct?
|
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims![0]); // TODO: is the bang correct?
|
||||||
@ -867,7 +777,7 @@ export abstract class BattleAnim {
|
|||||||
const user = !isOppAnim ? this.user : this.target;
|
const user = !isOppAnim ? this.user : this.target;
|
||||||
const target = !isOppAnim ? this.target : this.user;
|
const target = !isOppAnim ? this.target : this.user;
|
||||||
|
|
||||||
const targetSubstitute = onSubstitute && user !== target ? target!.getTag(SubstituteTag) : null;
|
const targetSubstitute = onSubstitute && user !== target ? target!.getTag(BattlerTagType.SUBSTITUTE) : null;
|
||||||
|
|
||||||
const userInitialX = user!.x; // TODO: is this bang correct?
|
const userInitialX = user!.x; // TODO: is this bang correct?
|
||||||
const userInitialY = user!.y; // TODO: is this bang correct?
|
const userInitialY = user!.y; // TODO: is this bang correct?
|
||||||
@ -941,7 +851,7 @@ export abstract class BattleAnim {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetSubstitute = !!onSubstitute && user !== target ? target.getTag(SubstituteTag) : null;
|
const targetSubstitute = !!onSubstitute && user !== target ? target.getTag(BattlerTagType.SUBSTITUTE) : null;
|
||||||
|
|
||||||
const userSprite = user.getSprite();
|
const userSprite = user.getSprite();
|
||||||
const targetSprite = targetSubstitute?.sprite ?? target.getSprite();
|
const targetSprite = targetSubstitute?.sprite ?? target.getSprite();
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
import { getPokemonNameWithAffix } from "../messages";
|
import { getPokemonNameWithAffix } from "../messages";
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import { HitResult } from "../field/pokemon";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { getStatusEffectHealText } from "./status-effect";
|
import { getStatusEffectHealText } from "./status-effect";
|
||||||
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common";
|
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common";
|
||||||
import {
|
import { applyAbAttrs } from "./abilities/apply-ab-attrs";
|
||||||
DoubleBerryEffectAbAttr,
|
|
||||||
ReduceBerryUseThresholdAbAttr,
|
|
||||||
applyAbAttrs,
|
|
||||||
} from "./abilities/ability";
|
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { Stat, type BattleStat } from "#app/enums/stat";
|
import { Stat, type BattleStat } from "#app/enums/stat";
|
||||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
|
||||||
export function getBerryName(berryType: BerryType): string {
|
export function getBerryName(berryType: BerryType): string {
|
||||||
@ -44,25 +38,25 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
|||||||
const threshold = new NumberHolder(0.25);
|
const threshold = new NumberHolder(0.25);
|
||||||
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
|
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
|
||||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6;
|
return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6;
|
||||||
};
|
};
|
||||||
case BerryType.LANSAT:
|
case BerryType.LANSAT:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new NumberHolder(0.25);
|
const threshold = new NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
|
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
|
||||||
};
|
};
|
||||||
case BerryType.STARF:
|
case BerryType.STARF:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new NumberHolder(0.25);
|
const threshold = new NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||||
return pokemon.getHpRatio() < 0.25;
|
return pokemon.getHpRatio() < 0.25;
|
||||||
};
|
};
|
||||||
case BerryType.LEPPA:
|
case BerryType.LEPPA:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
const threshold = new NumberHolder(0.25);
|
const threshold = new NumberHolder(0.25);
|
||||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||||
return !!pokemon.getMoveset().find(m => !m.getPpRatio());
|
return !!pokemon.getMoveset().find(m => !m.getPpRatio());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -78,9 +72,9 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
case BerryType.ENIGMA:
|
case BerryType.ENIGMA:
|
||||||
{
|
{
|
||||||
const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4));
|
const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4));
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, hpHealed);
|
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, hpHealed);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new PokemonHealPhase(
|
"PokemonHealPhase",
|
||||||
consumer.getBattlerIndex(),
|
consumer.getBattlerIndex(),
|
||||||
hpHealed.value,
|
hpHealed.value,
|
||||||
i18next.t("battle:hpHealBerry", {
|
i18next.t("battle:hpHealBerry", {
|
||||||
@ -88,14 +82,13 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
berryName: getBerryName(berryType),
|
berryName: getBerryName(berryType),
|
||||||
}),
|
}),
|
||||||
true,
|
true,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BerryType.LUM:
|
case BerryType.LUM:
|
||||||
{
|
{
|
||||||
if (consumer.status) {
|
if (consumer.status) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
getStatusEffectHealText(consumer.status.effect, getPokemonNameWithAffix(consumer)),
|
getStatusEffectHealText(consumer.status.effect, getPokemonNameWithAffix(consumer)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -112,9 +105,13 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
// Offset BerryType such that LIECHI --> Stat.ATK = 1, GANLON --> Stat.DEF = 2, etc etc.
|
// Offset BerryType such that LIECHI --> Stat.ATK = 1, GANLON --> Stat.DEF = 2, etc etc.
|
||||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||||
const statStages = new NumberHolder(1);
|
const statStages = new NumberHolder(1);
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, statStages);
|
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, statStages);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(consumer.getBattlerIndex(), true, [stat], statStages.value),
|
"StatStageChangePhase",
|
||||||
|
consumer.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
[stat],
|
||||||
|
statStages.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -129,9 +126,13 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
{
|
{
|
||||||
const randStat = randSeedInt(Stat.SPD, Stat.ATK);
|
const randStat = randSeedInt(Stat.SPD, Stat.ATK);
|
||||||
const stages = new NumberHolder(2);
|
const stages = new NumberHolder(2);
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, stages);
|
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, stages);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(consumer.getBattlerIndex(), true, [randStat], stages.value),
|
"StatStageChangePhase",
|
||||||
|
consumer.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
[randStat],
|
||||||
|
stages.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -144,7 +145,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
consumer.getMoveset().find(m => m.ppUsed < m.getMovePp());
|
consumer.getMoveset().find(m => m.ppUsed < m.getMovePp());
|
||||||
if (ppRestoreMove) {
|
if (ppRestoreMove) {
|
||||||
ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0);
|
ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0);
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("battle:ppHealBerry", {
|
i18next.t("battle:ppHealBerry", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
|
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
|
||||||
moveName: ppRestoreMove.getName(),
|
moveName: ppRestoreMove.getName(),
|
||||||
|
@ -2,17 +2,19 @@ import { BooleanHolder, type NumberHolder, randSeedItem } from "#app/utils/commo
|
|||||||
import { deepCopy } from "#app/utils/data";
|
import { deepCopy } from "#app/utils/data";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import type { DexAttrProps, GameData } from "#app/system/game-data";
|
import type { DexAttrProps, GameData } from "#app/system/game-data";
|
||||||
import { defaultStarterSpecies } from "#app/system/game-data";
|
import { defaultStarterSpecies } from "#app/constants";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
import { getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||||
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "./moves/pokemon-move";
|
||||||
import type { FixedBattleConfig } from "#app/battle";
|
import type { FixedBattleConfig } from "#app/battle";
|
||||||
import { getRandomTrainerFunc } from "#app/battle";
|
import { getRandomTrainerFunc } from "#app/battle";
|
||||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { Challenges } from "#enums/challenges";
|
import { Challenges } from "#enums/challenges";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
@ -20,97 +22,16 @@ import { TrainerType } from "#enums/trainer-type";
|
|||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import { TypeColor, TypeShadow } from "#enums/color";
|
import { TypeColor, TypeShadow } from "#enums/color";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { pokemonFormChanges } from "./pokemon-forms";
|
import { pokemonFormChanges } from "./pokemon-forms";
|
||||||
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
||||||
|
import { ChallengeType } from "#enums/challenge-type";
|
||||||
|
import type { MoveSourceType } from "#enums/move-source-type";
|
||||||
|
|
||||||
/** A constant for the default max cost of the starting party before a run */
|
/** A constant for the default max cost of the starting party before a run */
|
||||||
const DEFAULT_PARTY_MAX_COST = 10;
|
const DEFAULT_PARTY_MAX_COST = 10;
|
||||||
|
|
||||||
/**
|
|
||||||
* An enum for all the challenge types. The parameter entries on these describe the
|
|
||||||
* parameters to use when calling the applyChallenges function.
|
|
||||||
*/
|
|
||||||
export enum ChallengeType {
|
|
||||||
/**
|
|
||||||
* Challenges which modify what starters you can choose
|
|
||||||
* @see {@link Challenge.applyStarterChoice}
|
|
||||||
*/
|
|
||||||
STARTER_CHOICE,
|
|
||||||
/**
|
|
||||||
* Challenges which modify how many starter points you have
|
|
||||||
* @see {@link Challenge.applyStarterPoints}
|
|
||||||
*/
|
|
||||||
STARTER_POINTS,
|
|
||||||
/**
|
|
||||||
* Challenges which modify how many starter points you have
|
|
||||||
* @see {@link Challenge.applyStarterPointCost}
|
|
||||||
*/
|
|
||||||
STARTER_COST,
|
|
||||||
/**
|
|
||||||
* Challenges which modify your starters in some way
|
|
||||||
* @see {@link Challenge.applyStarterModify}
|
|
||||||
*/
|
|
||||||
STARTER_MODIFY,
|
|
||||||
/**
|
|
||||||
* Challenges which limit which pokemon you can have in battle.
|
|
||||||
* @see {@link Challenge.applyPokemonInBattle}
|
|
||||||
*/
|
|
||||||
POKEMON_IN_BATTLE,
|
|
||||||
/**
|
|
||||||
* Adds or modifies the fixed battles in a run
|
|
||||||
* @see {@link Challenge.applyFixedBattle}
|
|
||||||
*/
|
|
||||||
FIXED_BATTLES,
|
|
||||||
/**
|
|
||||||
* Modifies the effectiveness of Type matchups in battle
|
|
||||||
* @see {@linkcode Challenge.applyTypeEffectiveness}
|
|
||||||
*/
|
|
||||||
TYPE_EFFECTIVENESS,
|
|
||||||
/**
|
|
||||||
* Modifies what level the AI pokemon are. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
AI_LEVEL,
|
|
||||||
/**
|
|
||||||
* Modifies how many move slots the AI has. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
AI_MOVE_SLOTS,
|
|
||||||
/**
|
|
||||||
* Modifies if a pokemon has its passive. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
PASSIVE_ACCESS,
|
|
||||||
/**
|
|
||||||
* Modifies the game mode settings in some way. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
GAME_MODE_MODIFY,
|
|
||||||
/**
|
|
||||||
* Modifies what level AI pokemon can access a move. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
MOVE_ACCESS,
|
|
||||||
/**
|
|
||||||
* Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
MOVE_WEIGHT,
|
|
||||||
/**
|
|
||||||
* Modifies what the pokemon stats for Flip Stat Mode.
|
|
||||||
*/
|
|
||||||
FLIP_STAT,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for challenge types that modify movesets, these denote the various sources of moves for pokemon.
|
|
||||||
*/
|
|
||||||
export enum MoveSourceType {
|
|
||||||
LEVEL_UP, // Currently unimplemented for move access
|
|
||||||
RELEARNER, // Relearner moves currently unimplemented
|
|
||||||
COMMON_TM,
|
|
||||||
GREAT_TM,
|
|
||||||
ULTRA_TM,
|
|
||||||
COMMON_EGG,
|
|
||||||
RARE_EGG,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A challenge object. Exists only to serve as a base class.
|
* A challenge object. Exists only to serve as a base class.
|
||||||
*/
|
*/
|
||||||
|
@ -5,7 +5,8 @@ import { PlayerPokemon } from "#app/field/pokemon";
|
|||||||
import type { Starter } from "#app/ui/starter-select-ui-handler";
|
import type { Starter } from "#app/ui/starter-select-ui-handler";
|
||||||
import { randSeedGauss, randSeedInt, randSeedItem, getEnumValues } from "#app/utils/common";
|
import { randSeedGauss, randSeedInt, randSeedItem, getEnumValues } from "#app/utils/common";
|
||||||
import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
|
import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||||
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
import PokemonSpecies, { getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||||
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import type { Ability } from "./abilities/ability-class";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
|
import type { ModifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import type { Ability } from "./abilities/ability";
|
||||||
import type Move from "./moves/move";
|
import type Move from "./moves/move";
|
||||||
|
|
||||||
export const allAbilities: Ability[] = [];
|
export const allAbilities: Ability[] = [];
|
||||||
export const allMoves: Move[] = [];
|
export const allMoves: Move[] = [];
|
||||||
|
export const allSpecies: PokemonSpecies[] = [];
|
||||||
|
|
||||||
|
// TODO: Figure out what this is used for and provide an appropriate tsdoc comment
|
||||||
|
export const modifierTypes = {} as ModifierTypes;
|
||||||
|
@ -1723,49 +1723,6 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const doubleBattleDialogue = {
|
|
||||||
blue_red_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:blue_red_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:blue_red_double.victory.1"],
|
|
||||||
},
|
|
||||||
red_blue_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:red_blue_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:red_blue_double.victory.1"],
|
|
||||||
},
|
|
||||||
tate_liza_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:tate_liza_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:tate_liza_double.victory.1"],
|
|
||||||
},
|
|
||||||
liza_tate_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:liza_tate_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:liza_tate_double.victory.1"],
|
|
||||||
},
|
|
||||||
wallace_steven_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:wallace_steven_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:wallace_steven_double.victory.1"],
|
|
||||||
},
|
|
||||||
steven_wallace_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:steven_wallace_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:steven_wallace_double.victory.1"],
|
|
||||||
},
|
|
||||||
alder_iris_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:alder_iris_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:alder_iris_double.victory.1"],
|
|
||||||
},
|
|
||||||
iris_alder_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:iris_alder_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:iris_alder_double.victory.1"],
|
|
||||||
},
|
|
||||||
marnie_piers_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:marnie_piers_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:marnie_piers_double.victory.1"],
|
|
||||||
},
|
|
||||||
piers_marnie_double: {
|
|
||||||
encounter: ["doubleBattleDialogue:piers_marnie_double.encounter.1"],
|
|
||||||
victory: ["doubleBattleDialogue:piers_marnie_double.victory.1"],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const battleSpecDialogue = {
|
export const battleSpecDialogue = {
|
||||||
[BattleSpec.FINAL_BOSS]: {
|
[BattleSpec.FINAL_BOSS]: {
|
||||||
encounter: "battleSpecDialogue:encounter",
|
encounter: "battleSpecDialogue:encounter",
|
||||||
|
44
src/data/double-battle-dialogue.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// TODO: Move this back into `dialogue.ts` after finding a suitable way to remove the circular dependencies
|
||||||
|
// that caused this to be moved out in the first place
|
||||||
|
export const doubleBattleDialogue = {
|
||||||
|
blue_red_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:blue_red_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:blue_red_double.victory.1"],
|
||||||
|
},
|
||||||
|
red_blue_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:red_blue_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:red_blue_double.victory.1"],
|
||||||
|
},
|
||||||
|
tate_liza_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:tate_liza_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:tate_liza_double.victory.1"],
|
||||||
|
},
|
||||||
|
liza_tate_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:liza_tate_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:liza_tate_double.victory.1"],
|
||||||
|
},
|
||||||
|
wallace_steven_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:wallace_steven_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:wallace_steven_double.victory.1"],
|
||||||
|
},
|
||||||
|
steven_wallace_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:steven_wallace_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:steven_wallace_double.victory.1"],
|
||||||
|
},
|
||||||
|
alder_iris_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:alder_iris_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:alder_iris_double.victory.1"],
|
||||||
|
},
|
||||||
|
iris_alder_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:iris_alder_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:iris_alder_double.victory.1"],
|
||||||
|
},
|
||||||
|
marnie_piers_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:marnie_piers_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:marnie_piers_double.victory.1"],
|
||||||
|
},
|
||||||
|
piers_marnie_double: {
|
||||||
|
encounter: ["doubleBattleDialogue:piers_marnie_double.encounter.1"],
|
||||||
|
victory: ["doubleBattleDialogue:piers_marnie_double.victory.1"],
|
||||||
|
},
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type { DexEntry, StarterDataEntry } from "#app/system/game-data";
|
import type { StarterDataEntry } from "#app/system/game-data";
|
||||||
|
import type { DexEntry } from "#app/@types/dex-data";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores data associated with a specific egg and the hatched pokemon
|
* Stores data associated with a specific egg and the hatched pokemon
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type BattleScene from "#app/battle-scene";
|
import type BattleScene from "#app/battle-scene";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import { VariantTier } from "#enums/variant-tier";
|
import { VariantTier } from "#enums/variant-tier";
|
||||||
import { randInt, randomString, randSeedInt, getIvsFromId } from "#app/utils/common";
|
import { randInt, randomString, randSeedInt, getIvsFromId } from "#app/utils/common";
|
||||||
|
58
src/data/moves/apply-attrs.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Module holding functions to apply move attributes.
|
||||||
|
* Must not import anything that is not a type.
|
||||||
|
*/
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import type { default as Move, MoveAttr } from "./move";
|
||||||
|
import type { ChargingMove } from "#app/@types/move-types";
|
||||||
|
import type { MoveAttrFilter, MoveAttrString } from "#app/@types/move-types";
|
||||||
|
|
||||||
|
function applyMoveAttrsInternal(
|
||||||
|
attrFilter: MoveAttrFilter,
|
||||||
|
user: Pokemon | null,
|
||||||
|
target: Pokemon | null,
|
||||||
|
move: Move,
|
||||||
|
args: any[],
|
||||||
|
): void {
|
||||||
|
move.attrs.filter(attr => attrFilter(attr)).forEach(attr => attr.apply(user, target, move, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyMoveChargeAttrsInternal(
|
||||||
|
attrFilter: MoveAttrFilter,
|
||||||
|
user: Pokemon | null,
|
||||||
|
target: Pokemon | null,
|
||||||
|
move: ChargingMove,
|
||||||
|
args: any[],
|
||||||
|
): void {
|
||||||
|
move.chargeAttrs.filter(attr => attrFilter(attr)).forEach(attr => attr.apply(user, target, move, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyMoveAttrs(
|
||||||
|
attrType: MoveAttrString,
|
||||||
|
user: Pokemon | null,
|
||||||
|
target: Pokemon | null,
|
||||||
|
move: Move,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyMoveAttrsInternal((attr: MoveAttr) => attr.is(attrType), user, target, move, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyFilteredMoveAttrs(
|
||||||
|
attrFilter: MoveAttrFilter,
|
||||||
|
user: Pokemon,
|
||||||
|
target: Pokemon | null,
|
||||||
|
move: Move,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyMoveAttrsInternal(attrFilter, user, target, move, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyMoveChargeAttrs(
|
||||||
|
attrType: MoveAttrString,
|
||||||
|
user: Pokemon | null,
|
||||||
|
target: Pokemon | null,
|
||||||
|
move: ChargingMove,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
applyMoveChargeAttrsInternal((attr: MoveAttr) => attr.is(attrType), user, target, move, args);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
|
|
||||||
/** Set of moves that cannot be called by {@linkcode MoveId.METRONOME Metronome} */
|
/** Set of moves that cannot be called by {@linkcode MoveId.METRONOME | Metronome}. */
|
||||||
export const invalidMetronomeMoves: ReadonlySet<MoveId> = new Set([
|
export const invalidMetronomeMoves: ReadonlySet<MoveId> = new Set([
|
||||||
MoveId.AFTER_YOU,
|
MoveId.AFTER_YOU,
|
||||||
MoveId.ASSIST,
|
MoveId.ASSIST,
|
||||||
@ -255,3 +255,28 @@ export const noAbilityTypeOverrideMoves: ReadonlySet<MoveId> = new Set([
|
|||||||
MoveId.TECHNO_BLAST,
|
MoveId.TECHNO_BLAST,
|
||||||
MoveId.HIDDEN_POWER,
|
MoveId.HIDDEN_POWER,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
/** Set of all moves that cannot be copied by {@linkcode Moves.SKETCH}. */
|
||||||
|
export const invalidSketchMoves: ReadonlySet<MoveId> = new Set([
|
||||||
|
MoveId.NONE,
|
||||||
|
MoveId.CHATTER,
|
||||||
|
MoveId.MIRROR_MOVE,
|
||||||
|
MoveId.SLEEP_TALK,
|
||||||
|
MoveId.STRUGGLE,
|
||||||
|
MoveId.SKETCH,
|
||||||
|
MoveId.REVIVAL_BLESSING,
|
||||||
|
MoveId.TERA_STARSTORM,
|
||||||
|
MoveId.BREAKNECK_BLITZ__PHYSICAL,
|
||||||
|
MoveId.BREAKNECK_BLITZ__SPECIAL,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** Set of all moves that cannot be locked into by {@linkcode Moves.ENCORE}. */
|
||||||
|
export const invalidEncoreMoves: ReadonlySet<MoveId> = new Set([
|
||||||
|
MoveId.MIMIC,
|
||||||
|
MoveId.MIRROR_MOVE,
|
||||||
|
MoveId.TRANSFORM,
|
||||||
|
MoveId.STRUGGLE,
|
||||||
|
MoveId.SKETCH,
|
||||||
|
MoveId.SLEEP_TALK,
|
||||||
|
MoveId.ENCORE,
|
||||||
|
]);
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
import { MoveTarget } from "#enums/MoveTarget";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import type { MoveId } from "#enums/move-id";
|
||||||
|
import type { MoveTargetSet, UserMoveConditionFunc } from "./move";
|
||||||
import type Move from "./move";
|
import type Move from "./move";
|
||||||
|
import { NumberHolder, isNullOrUndefined } from "#app/utils/common";
|
||||||
|
import { MoveTarget } from "#enums/MoveTarget";
|
||||||
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
|
import { allMoves } from "#app/data/data-lists";
|
||||||
|
import { applyMoveAttrs } from "./apply-attrs";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the move targets the field
|
* Return whether the move targets the field
|
||||||
@ -18,3 +27,88 @@ export function isFieldTargeted(move: Move): boolean {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMoveTargets(user: Pokemon, move: MoveId, replaceTarget?: MoveTarget): MoveTargetSet {
|
||||||
|
const variableTarget = new NumberHolder(0);
|
||||||
|
user.getOpponents(false).forEach(p => applyMoveAttrs("VariableTargetAttr", user, p, allMoves[move], variableTarget));
|
||||||
|
|
||||||
|
let moveTarget: MoveTarget | undefined;
|
||||||
|
if (allMoves[move].hasAttr("VariableTargetAttr")) {
|
||||||
|
moveTarget = variableTarget.value;
|
||||||
|
} else if (replaceTarget !== undefined) {
|
||||||
|
moveTarget = replaceTarget;
|
||||||
|
} else if (move) {
|
||||||
|
moveTarget = allMoves[move].moveTarget;
|
||||||
|
} else if (move === undefined) {
|
||||||
|
moveTarget = MoveTarget.NEAR_ENEMY;
|
||||||
|
}
|
||||||
|
const opponents = user.getOpponents(false);
|
||||||
|
|
||||||
|
let set: Pokemon[] = [];
|
||||||
|
let multiple = false;
|
||||||
|
const ally: Pokemon | undefined = user.getAlly();
|
||||||
|
|
||||||
|
switch (moveTarget) {
|
||||||
|
case MoveTarget.USER:
|
||||||
|
case MoveTarget.PARTY:
|
||||||
|
set = [user];
|
||||||
|
break;
|
||||||
|
case MoveTarget.NEAR_OTHER:
|
||||||
|
case MoveTarget.OTHER:
|
||||||
|
case MoveTarget.ALL_NEAR_OTHERS:
|
||||||
|
case MoveTarget.ALL_OTHERS:
|
||||||
|
set = !isNullOrUndefined(ally) ? opponents.concat([ally]) : opponents;
|
||||||
|
multiple = moveTarget === MoveTarget.ALL_NEAR_OTHERS || moveTarget === MoveTarget.ALL_OTHERS;
|
||||||
|
break;
|
||||||
|
case MoveTarget.NEAR_ENEMY:
|
||||||
|
case MoveTarget.ALL_NEAR_ENEMIES:
|
||||||
|
case MoveTarget.ALL_ENEMIES:
|
||||||
|
case MoveTarget.ENEMY_SIDE:
|
||||||
|
set = opponents;
|
||||||
|
multiple = moveTarget !== MoveTarget.NEAR_ENEMY;
|
||||||
|
break;
|
||||||
|
case MoveTarget.RANDOM_NEAR_ENEMY:
|
||||||
|
set = [opponents[user.randBattleSeedInt(opponents.length)]];
|
||||||
|
break;
|
||||||
|
case MoveTarget.ATTACKER:
|
||||||
|
return { targets: [-1 as BattlerIndex], multiple: false };
|
||||||
|
case MoveTarget.NEAR_ALLY:
|
||||||
|
case MoveTarget.ALLY:
|
||||||
|
set = !isNullOrUndefined(ally) ? [ally] : [];
|
||||||
|
break;
|
||||||
|
case MoveTarget.USER_OR_NEAR_ALLY:
|
||||||
|
case MoveTarget.USER_AND_ALLIES:
|
||||||
|
case MoveTarget.USER_SIDE:
|
||||||
|
set = !isNullOrUndefined(ally) ? [user, ally] : [user];
|
||||||
|
multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY;
|
||||||
|
break;
|
||||||
|
case MoveTarget.ALL:
|
||||||
|
case MoveTarget.BOTH_SIDES:
|
||||||
|
set = (!isNullOrUndefined(ally) ? [user, ally] : [user]).concat(opponents);
|
||||||
|
multiple = true;
|
||||||
|
break;
|
||||||
|
case MoveTarget.CURSE:
|
||||||
|
{
|
||||||
|
const extraTargets = !isNullOrUndefined(ally) ? [ally] : [];
|
||||||
|
set = user.getTypes(true).includes(PokemonType.GHOST) ? opponents.concat(extraTargets) : [user];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
targets: set
|
||||||
|
.filter(p => p?.isActive(true))
|
||||||
|
.map(p => p.getBattlerIndex())
|
||||||
|
.filter(t => t !== undefined),
|
||||||
|
multiple,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const frenzyMissFunc: UserMoveConditionFunc = (user: Pokemon, move: Move) => {
|
||||||
|
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id) {
|
||||||
|
user.getMoveQueue().shift();
|
||||||
|
}
|
||||||
|
user.removeTag(BattlerTagType.FRENZY); // FRENZY tag should be disrupted on miss/no effect
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
92
src/data/moves/pokemon-move.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import { toDmgValue } from "#app/utils/common";
|
||||||
|
import type { MoveId } from "#enums/move-id";
|
||||||
|
import { allMoves } from "../data-lists";
|
||||||
|
import type Move from "./move";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class for the {@linkcode Move} class for Pokemon to interact with.
|
||||||
|
* These are the moves assigned to a {@linkcode Pokemon} object.
|
||||||
|
* It links to {@linkcode Move} class via the move ID.
|
||||||
|
* Compared to {@linkcode Move}, this class also tracks things like
|
||||||
|
* PP Ups recieved, PP used, etc.
|
||||||
|
* @see {@linkcode isUsable} - checks if move is restricted, out of PP, or not implemented.
|
||||||
|
* @see {@linkcode getMove} - returns {@linkcode Move} object by looking it up via ID.
|
||||||
|
* @see {@linkcode usePp} - removes a point of PP from the move.
|
||||||
|
* @see {@linkcode getMovePp} - returns amount of PP a move currently has.
|
||||||
|
* @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount.
|
||||||
|
* @see {@linkcode getName} - returns name of {@linkcode Move}.
|
||||||
|
**/
|
||||||
|
export class PokemonMove {
|
||||||
|
public moveId: MoveId;
|
||||||
|
public ppUsed: number;
|
||||||
|
public ppUp: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If defined and nonzero, overrides the maximum PP of the move (e.g., due to move being copied by Transform).
|
||||||
|
* This also nullifies all effects of `ppUp`.
|
||||||
|
*/
|
||||||
|
public maxPpOverride?: number;
|
||||||
|
|
||||||
|
constructor(moveId: MoveId, ppUsed = 0, ppUp = 0, maxPpOverride?: number) {
|
||||||
|
this.moveId = moveId;
|
||||||
|
this.ppUsed = ppUsed;
|
||||||
|
this.ppUp = ppUp;
|
||||||
|
this.maxPpOverride = maxPpOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets.
|
||||||
|
* The move is unusable if it is out of PP, restricted by an effect, or unimplemented.
|
||||||
|
*
|
||||||
|
* @param pokemon - {@linkcode Pokemon} that would be using this move
|
||||||
|
* @param ignorePp - If `true`, skips the PP check
|
||||||
|
* @param ignoreRestrictionTags - If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag})
|
||||||
|
* @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`.
|
||||||
|
*/
|
||||||
|
isUsable(pokemon: Pokemon, ignorePp = false, ignoreRestrictionTags = false): boolean {
|
||||||
|
// TODO: Add Sky Drop's 1 turn stall
|
||||||
|
if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getMove().name.endsWith(" (N)")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMove(): Move {
|
||||||
|
return allMoves[this.moveId];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets {@link ppUsed} for this move and ensures the value does not exceed {@link getMovePp}
|
||||||
|
* @param count Amount of PP to use
|
||||||
|
*/
|
||||||
|
usePp(count = 1) {
|
||||||
|
this.ppUsed = Math.min(this.ppUsed + count, this.getMovePp());
|
||||||
|
}
|
||||||
|
|
||||||
|
getMovePp(): number {
|
||||||
|
return this.maxPpOverride || this.getMove().pp + this.ppUp * toDmgValue(this.getMove().pp / 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPpRatio(): number {
|
||||||
|
return 1 - this.ppUsed / this.getMovePp();
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return this.getMove().name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies an existing move or creates a valid {@linkcode PokemonMove} object from json representing one
|
||||||
|
* @param source The data for the move to copy; can be a {@linkcode PokemonMove} or JSON object representing one
|
||||||
|
* @returns A valid {@linkcode PokemonMove} object
|
||||||
|
*/
|
||||||
|
static loadMove(source: PokemonMove | any): PokemonMove {
|
||||||
|
return new PokemonMove(source.moveId, source.ppUsed, source.ppUp, source.maxPpOverride);
|
||||||
|
}
|
||||||
|
}
|
@ -19,9 +19,8 @@ import i18next from "i18next";
|
|||||||
import type { IEggOptions } from "#app/data/egg";
|
import type { IEggOptions } from "#app/data/egg";
|
||||||
import { EggSourceType } from "#enums/egg-source-types";
|
import { EggSourceType } from "#enums/egg-source-types";
|
||||||
import { EggTier } from "#enums/egg-type";
|
import { EggTier } from "#enums/egg-type";
|
||||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
@ -182,7 +181,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
|||||||
async () => {
|
async () => {
|
||||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||||
// Full heal party
|
// Full heal party
|
||||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
|
||||||
|
|
||||||
const eggOptions: IEggOptions = {
|
const eggOptions: IEggOptions = {
|
||||||
pulled: false,
|
pulled: false,
|
||||||
|
@ -7,9 +7,10 @@ import {
|
|||||||
transitionMysteryEncounterIntroVisuals,
|
transitionMysteryEncounterIntroVisuals,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -21,11 +22,11 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun
|
|||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { BerryModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
import { BerryModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { randInt } from "#app/utils/common";
|
import { randInt } from "#app/utils/common";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import {
|
import {
|
||||||
applyModifierTypeToPlayerPokemon,
|
applyModifierTypeToPlayerPokemon,
|
||||||
catchPokemon,
|
catchPokemon,
|
||||||
@ -33,11 +34,11 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||||
import type { BerryType } from "#enums/berry-type";
|
import type { BerryType } from "#enums/berry-type";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounters/absoluteAvarice";
|
const namespace = "mysteryEncounters/absoluteAvarice";
|
||||||
@ -237,8 +238,12 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
"StatStageChangePhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
statChangesForBattle,
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -303,7 +308,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.ENEMY],
|
targets: [BattlerIndex.ENEMY],
|
||||||
move: new PokemonMove(MoveId.STUFF_CHEEKS),
|
move: new PokemonMove(MoveId.STUFF_CHEEKS),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
});
|
});
|
||||||
|
|
||||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
setEncounterExp,
|
setEncounterExp,
|
||||||
updatePlayerMoney,
|
updatePlayerMoney,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -18,11 +18,10 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getHighestStatTotalPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { EXTORTION_ABILITIES, EXTORTION_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
@ -137,7 +136,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
|
|||||||
})
|
})
|
||||||
.withOptionPhase(async () => {
|
.withOptionPhase(async () => {
|
||||||
// Give the player a Shiny Charm
|
// Give the player a Shiny Charm
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM));
|
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.SHINY_CHARM);
|
||||||
leaveEncounterWithoutBattle(true);
|
leaveEncounterWithoutBattle(true);
|
||||||
})
|
})
|
||||||
.build(),
|
.build(),
|
||||||
|
@ -12,7 +12,9 @@ import {
|
|||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { BerryModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
|
import type { BerryModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
import { ModifierPoolType, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
@ -35,7 +37,6 @@ import { BerryModifier } from "#app/modifier/modifier";
|
|||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { PERMANENT_STATS, Stat } from "#enums/stat";
|
import { PERMANENT_STATS, Stat } from "#enums/stat";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
@ -237,8 +238,12 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
|||||||
config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
||||||
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(`${namespace}:option.2.boss_enraged`);
|
queueEncounterMessage(`${namespace}:option.2.boss_enraged`);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
"StatStageChangePhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
statChangesForBattle,
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
setEncounterRewards(
|
setEncounterRewards(
|
||||||
|
@ -24,9 +24,8 @@ import { TrainerType } from "#enums/trainer-type";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
@ -39,7 +38,7 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import type { AttackTypeBoosterModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
|
import type { AttackTypeBoosterModifierType, ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import {
|
import {
|
||||||
AttackTypeBoosterModifier,
|
AttackTypeBoosterModifier,
|
||||||
@ -51,7 +50,7 @@ import {
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import MoveInfoOverlay from "#app/ui/move-info-overlay";
|
import MoveInfoOverlay from "#app/ui/move-info-overlay";
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
|
||||||
@ -766,8 +765,10 @@ function doBugTypeMoveTutor(): Promise<void> {
|
|||||||
|
|
||||||
// Option select complete, handle if they are learning a move
|
// Option select complete, handle if they are learning a move
|
||||||
if (result && result.selectedOptionIndex < moveOptions.length) {
|
if (result && result.selectedOptionIndex < moveOptions.length) {
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new LearnMovePhase(result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId),
|
"LearnMovePhase",
|
||||||
|
result.selectedPokemonIndex,
|
||||||
|
moveOptions[result.selectedOptionIndex].moveId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,9 +11,9 @@ import {
|
|||||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||||
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { ModifierPoolType, modifierTypes } from "#app/modifier/modifier-type";
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -22,7 +22,7 @@ import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-en
|
|||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import {
|
import {
|
||||||
applyAbilityOverrideToPokemon,
|
applyAbilityOverrideToPokemon,
|
||||||
@ -37,11 +37,10 @@ import { UiMode } from "#enums/ui-mode";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { Ability } from "#app/data/abilities/ability-class";
|
|
||||||
import { BerryModifier } from "#app/modifier/modifier";
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
@ -49,6 +48,8 @@ import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
|||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { EncounterAnim } from "#enums/encounter-anims";
|
import { EncounterAnim } from "#enums/encounter-anims";
|
||||||
import { Challenges } from "#enums/challenges";
|
import { Challenges } from "#enums/challenges";
|
||||||
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
import { allAbilities, modifierTypes } from "#app/data/data-lists";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/clowningAround";
|
const namespace = "mysteryEncounters/clowningAround";
|
||||||
@ -139,7 +140,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
|
|
||||||
// Generate random ability for Blacephalon from pool
|
// Generate random ability for Blacephalon from pool
|
||||||
const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)];
|
const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)];
|
||||||
encounter.setDialogueToken("ability", new Ability(ability, 3).name);
|
encounter.setDialogueToken("ability", allAbilities[ability].name);
|
||||||
encounter.misc = { ability };
|
encounter.misc = { ability };
|
||||||
|
|
||||||
// Decide the random types for Blacephalon. They should not be the same.
|
// Decide the random types for Blacephalon. They should not be the same.
|
||||||
@ -209,19 +210,19 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.ENEMY_2],
|
targets: [BattlerIndex.ENEMY_2],
|
||||||
move: new PokemonMove(MoveId.ROLE_PLAY),
|
move: new PokemonMove(MoveId.ROLE_PLAY),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||||
targets: [BattlerIndex.PLAYER],
|
targets: [BattlerIndex.PLAYER],
|
||||||
move: new PokemonMove(MoveId.TAUNT),
|
move: new PokemonMove(MoveId.TAUNT),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||||
targets: [BattlerIndex.PLAYER_2],
|
targets: [BattlerIndex.PLAYER_2],
|
||||||
move: new PokemonMove(MoveId.TAUNT),
|
move: new PokemonMove(MoveId.TAUNT),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -19,15 +19,14 @@ import {
|
|||||||
getEncounterPokemonLevelForWave,
|
getEncounterPokemonLevelForWave,
|
||||||
STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER,
|
STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
@ -41,6 +40,7 @@ import { PokeballType } from "#enums/pokeball";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounters/dancingLessons";
|
const namespace = "mysteryEncounters/dancingLessons";
|
||||||
@ -176,13 +176,12 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(
|
"StatStageChangePhase",
|
||||||
pokemon.getBattlerIndex(),
|
pokemon.getBattlerIndex(),
|
||||||
true,
|
true,
|
||||||
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF],
|
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF],
|
||||||
1,
|
1,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -216,7 +215,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.PLAYER],
|
targets: [BattlerIndex.PLAYER],
|
||||||
move: new PokemonMove(MoveId.REVELATION_DANCE),
|
move: new PokemonMove(MoveId.REVELATION_DANCE),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
});
|
});
|
||||||
|
|
||||||
await hideOricorioPokemon();
|
await hideOricorioPokemon();
|
||||||
@ -245,8 +244,10 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
|
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new LearnMovePhase(globalScene.getPlayerParty().indexOf(pokemon), MoveId.REVELATION_DANCE),
|
"LearnMovePhase",
|
||||||
|
globalScene.getPlayerParty().indexOf(pokemon),
|
||||||
|
MoveId.REVELATION_DANCE,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Play animation again to "learn" the dance
|
// Play animation again to "learn" the dance
|
||||||
|
@ -3,8 +3,8 @@ import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
|||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
@ -16,7 +16,6 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
|
||||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
import { PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
@ -165,7 +164,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE
|
|||||||
.withOptionPhase(async () => {
|
.withOptionPhase(async () => {
|
||||||
// Give the player 5 Rogue Balls
|
// Give the player 5 Rogue Balls
|
||||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.ROGUE_BALL));
|
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.ROGUE_BALL);
|
||||||
|
|
||||||
// Start encounter with random legendary (7-10 starter strength) that has level additive
|
// Start encounter with random legendary (7-10 starter strength) that has level additive
|
||||||
// If this is a mono-type challenge, always ensure the required type is filtered for
|
// If this is a mono-type challenge, always ensure the required type is filtered for
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
updatePlayerMoney,
|
updatePlayerMoney,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
@ -28,8 +28,7 @@ import {
|
|||||||
PreserveBerryModifier,
|
PreserveBerryModifier,
|
||||||
} from "#app/modifier/modifier";
|
} from "#app/modifier/modifier";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { randSeedItem } from "#app/utils/common";
|
import { randSeedItem } from "#app/utils/common";
|
||||||
@ -65,10 +64,10 @@ const doEventReward = () => {
|
|||||||
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount());
|
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount());
|
||||||
});
|
});
|
||||||
if (candidates.length > 0) {
|
if (candidates.length > 0) {
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)]));
|
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes[randSeedItem(candidates)]);
|
||||||
} else {
|
} else {
|
||||||
// At max stacks, give a Voucher instead
|
// At max stacks, give a Voucher instead
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.VOUCHER));
|
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.VOUCHER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -181,7 +180,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
);
|
);
|
||||||
doEventReward();
|
doEventReward();
|
||||||
} else {
|
} else {
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN));
|
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.AMULET_COIN);
|
||||||
doEventReward();
|
doEventReward();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +265,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
);
|
);
|
||||||
doEventReward();
|
doEventReward();
|
||||||
} else {
|
} else {
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR));
|
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.CANDY_JAR);
|
||||||
doEventReward();
|
doEventReward();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -288,7 +287,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
);
|
);
|
||||||
doEventReward();
|
doEventReward();
|
||||||
} else {
|
} else {
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH));
|
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.BERRY_POUCH);
|
||||||
doEventReward();
|
doEventReward();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -372,7 +371,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
);
|
);
|
||||||
doEventReward();
|
doEventReward();
|
||||||
} else {
|
} else {
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM));
|
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.HEALING_CHARM);
|
||||||
doEventReward();
|
doEventReward();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import {
|
|||||||
leaveEncounterWithoutBattle,
|
leaveEncounterWithoutBattle,
|
||||||
setEncounterRewards,
|
setEncounterRewards,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { ModifierTypeFunc } from "#app/modifier/modifier-type";
|
import type { ModifierTypeFunc } from "#app/@types/modifier-types";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
@ -7,8 +7,9 @@ import {
|
|||||||
setEncounterExp,
|
setEncounterExp,
|
||||||
setEncounterRewards,
|
setEncounterRewards,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
generateModifierType,
|
generateModifierType,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -21,12 +20,12 @@ import {
|
|||||||
TypeRequirement,
|
TypeRequirement,
|
||||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
@ -44,10 +43,10 @@ import { EncounterAnim } from "#enums/encounter-anims";
|
|||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { Ability } from "#app/data/abilities/ability-class";
|
|
||||||
import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
import { allAbilities, modifierTypes } from "#app/data/data-lists";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/fieryFallout";
|
const namespace = "mysteryEncounters/fieryFallout";
|
||||||
@ -92,8 +91,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
|||||||
gender: Gender.MALE,
|
gender: Gender.MALE,
|
||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1),
|
"StatStageChangePhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
[Stat.SPDEF, Stat.SPD],
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -103,8 +106,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
|||||||
gender: Gender.FEMALE,
|
gender: Gender.FEMALE,
|
||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1),
|
"StatStageChangePhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
[Stat.SPDEF, Stat.SPD],
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -194,13 +201,13 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
|||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.PLAYER],
|
targets: [BattlerIndex.PLAYER],
|
||||||
move: new PokemonMove(MoveId.FIRE_SPIN),
|
move: new PokemonMove(MoveId.FIRE_SPIN),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||||
targets: [BattlerIndex.PLAYER_2],
|
targets: [BattlerIndex.PLAYER_2],
|
||||||
move: new PokemonMove(MoveId.FIRE_SPIN),
|
move: new PokemonMove(MoveId.FIRE_SPIN),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
||||||
@ -239,7 +246,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
|||||||
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
|
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
|
||||||
// Burn applied
|
// Burn applied
|
||||||
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
|
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
|
||||||
encounter.setDialogueToken("abilityName", new Ability(AbilityId.HEATPROOF, 3).name);
|
encounter.setDialogueToken("abilityName", allAbilities[AbilityId.HEATPROOF].name);
|
||||||
queueEncounterMessage(`${namespace}:option.2.target_burned`);
|
queueEncounterMessage(`${namespace}:option.2.target_burned`);
|
||||||
|
|
||||||
// Also permanently change the burned Pokemon's ability to Heatproof
|
// Also permanently change the burned Pokemon's ability to Heatproof
|
||||||
|
@ -9,13 +9,10 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
import {
|
import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
getPlayerModifierTypeOptions,
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
ModifierPoolType,
|
|
||||||
regenerateModifierPoolThresholds,
|
|
||||||
} from "#app/modifier/modifier-type";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -32,7 +29,6 @@ import PokemonData from "#app/system/pokemon-data";
|
|||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
@ -76,7 +72,13 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
|||||||
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
||||||
// Randomly boost 1 stat 2 stages
|
// Randomly boost 1 stat 2 stages
|
||||||
// Cannot boost Spd, Acc, or Evasion
|
// Cannot boost Spd, Acc, or Evasion
|
||||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2));
|
globalScene.phaseManager.unshiftNew(
|
||||||
|
"StatStageChangePhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
[randSeedInt(4, 1)],
|
||||||
|
2,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -13,8 +13,8 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
|
|||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { FieldPosition } from "#app/field/pokemon";
|
import { FieldPosition } from "#enums/field-position";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
@ -25,10 +25,8 @@ import { getPokemonNameWithAffix } from "#app/messages";
|
|||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
||||||
import { addPokeballOpenParticles } from "#app/field/anims";
|
import { addPokeballOpenParticles } from "#app/field/anims";
|
||||||
import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase";
|
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
@ -411,13 +409,13 @@ function summonPlayerPokemonAnimation(pokemon: PlayerPokemon): Promise<void> {
|
|||||||
pokemon.resetSummonData();
|
pokemon.resetSummonData();
|
||||||
globalScene.time.delayedCall(1000, () => {
|
globalScene.time.delayedCall(1000, () => {
|
||||||
if (pokemon.isShiny()) {
|
if (pokemon.isShiny()) {
|
||||||
globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
|
globalScene.phaseManager.unshiftNew("ShinySparklePhase", pokemon.getBattlerIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
pokemon.resetTurnData();
|
pokemon.resetTurnData();
|
||||||
|
|
||||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
||||||
globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex()));
|
globalScene.phaseManager.pushNew("PostSummonPhase", pokemon.getBattlerIndex());
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -4,14 +4,11 @@ import {
|
|||||||
setEncounterRewards,
|
setEncounterRewards,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { MusicPreference } from "#app/system/settings/settings";
|
import { MusicPreference } from "#app/system/settings/settings";
|
||||||
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
import {
|
import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
getPlayerModifierTypeOptions,
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
ModifierPoolType,
|
|
||||||
regenerateModifierPoolThresholds,
|
|
||||||
} from "#app/modifier/modifier-type";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -19,7 +16,8 @@ import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-en
|
|||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
|
import { allSpecies } from "#app/data/data-lists";
|
||||||
import { getTypeRgb } from "#app/data/type";
|
import { getTypeRgb } from "#app/data/type";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
@ -33,7 +31,8 @@ import {
|
|||||||
} from "#app/utils/common";
|
} from "#app/utils/common";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import {
|
import {
|
||||||
HiddenAbilityRateBoosterModifier,
|
HiddenAbilityRateBoosterModifier,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
@ -11,7 +11,7 @@ import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encount
|
|||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
|
||||||
const OPTION_1_REQUIRED_MOVE = MoveId.SURF;
|
const OPTION_1_REQUIRED_MOVE = MoveId.SURF;
|
||||||
const OPTION_2_REQUIRED_MOVE = MoveId.FLY;
|
const OPTION_2_REQUIRED_MOVE = MoveId.FLY;
|
||||||
|
@ -7,8 +7,8 @@ import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
|||||||
import { trainerPartyTemplates } from "#app/data/trainers/TrainerPartyTemplate";
|
import { trainerPartyTemplates } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -14,10 +14,9 @@ import {
|
|||||||
getHighestLevelPlayerPokemon,
|
getHighestLevelPlayerPokemon,
|
||||||
koPlayerPokemon,
|
koPlayerPokemon,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
@ -189,8 +188,8 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
const allowedPokemon = globalScene.getPokemonAllowedInBattle();
|
const allowedPokemon = globalScene.getPokemonAllowedInBattle();
|
||||||
if (allowedPokemon.length === 0) {
|
if (allowedPokemon.length === 0) {
|
||||||
// If there are no longer any legal pokemon in the party, game over.
|
// If there are no longer any legal pokemon in the party, game over.
|
||||||
globalScene.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue();
|
||||||
globalScene.unshiftPhase(new GameOverPhase());
|
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||||
} else {
|
} else {
|
||||||
// Show which Pokemon was KOed, then start battle against Gimmighoul
|
// Show which Pokemon was KOed, then start battle against Gimmighoul
|
||||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||||
|
@ -17,7 +17,7 @@ import { PokeballType } from "#enums/pokeball";
|
|||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { NumberHolder, randSeedInt } from "#app/utils/common";
|
import { NumberHolder, randSeedInt } from "#app/utils/common";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import {
|
import {
|
||||||
doPlayerFlee,
|
doPlayerFlee,
|
||||||
@ -29,8 +29,6 @@ import { getEncounterText, showEncounterText } from "#app/data/mystery-encounter
|
|||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
|
||||||
import { SummonPhase } from "#app/phases/summon-phase";
|
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups";
|
import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups";
|
||||||
|
|
||||||
@ -276,7 +274,7 @@ async function summonSafariPokemon() {
|
|||||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||||
// Message pokemon remaining
|
// Message pokemon remaining
|
||||||
encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining);
|
encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining);
|
||||||
globalScene.queueMessage(getEncounterText(`${namespace}:safari.remaining_count`) ?? "", null, true);
|
globalScene.phaseManager.queueMessage(getEncounterText(`${namespace}:safari.remaining_count`) ?? "", null, true);
|
||||||
|
|
||||||
// Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken
|
// Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken
|
||||||
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
|
// Safari pokemon roll twice on shiny and HA chances, but are otherwise normal
|
||||||
@ -325,7 +323,7 @@ async function summonSafariPokemon() {
|
|||||||
encounter.misc.pokemon = pokemon;
|
encounter.misc.pokemon = pokemon;
|
||||||
encounter.misc.safariPokemonRemaining -= 1;
|
encounter.misc.safariPokemonRemaining -= 1;
|
||||||
|
|
||||||
globalScene.unshiftPhase(new SummonPhase(0, false));
|
globalScene.phaseManager.unshiftNew("SummonPhase", 0, false);
|
||||||
|
|
||||||
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
|
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
|
||||||
|
|
||||||
@ -336,7 +334,7 @@ async function summonSafariPokemon() {
|
|||||||
|
|
||||||
const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier);
|
const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier);
|
||||||
if (ivScannerModifier) {
|
if (ivScannerModifier) {
|
||||||
globalScene.pushPhase(new ScanIvsPhase(pokemon.getBattlerIndex()));
|
globalScene.phaseManager.pushNew("ScanIvsPhase", pokemon.getBattlerIndex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,7 +557,7 @@ async function doEndTurn(cursorIndex: number) {
|
|||||||
leaveEncounterWithoutBattle(true);
|
leaveEncounterWithoutBattle(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
globalScene.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000);
|
globalScene.phaseManager.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000);
|
||||||
initSubsequentOptionSelect({
|
initSubsequentOptionSelect({
|
||||||
overrideOptions: safariZoneGameOptions,
|
overrideOptions: safariZoneGameOptions,
|
||||||
startingCursorIndex: cursorIndex,
|
startingCursorIndex: cursorIndex,
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -21,16 +21,17 @@ import {
|
|||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { AiType, PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { AiType } from "#enums/ai-type";
|
||||||
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
|
||||||
/** i18n namespace for the encounter */
|
/** i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/slumberingSnorlax";
|
const namespace = "mysteryEncounters/slumberingSnorlax";
|
||||||
@ -137,7 +138,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
|||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.PLAYER],
|
targets: [BattlerIndex.PLAYER],
|
||||||
move: new PokemonMove(MoveId.SNORE),
|
move: new PokemonMove(MoveId.SNORE),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
});
|
});
|
||||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||||
},
|
},
|
||||||
@ -155,7 +156,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
|||||||
async () => {
|
async () => {
|
||||||
// Fall asleep waiting for Snorlax
|
// Fall asleep waiting for Snorlax
|
||||||
// Full heal party
|
// Full heal party
|
||||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
|
||||||
queueEncounterMessage(`${namespace}:option.2.rest_result`);
|
queueEncounterMessage(`${namespace}:option.2.rest_result`);
|
||||||
leaveEncounterWithoutBattle();
|
leaveEncounterWithoutBattle();
|
||||||
},
|
},
|
||||||
|
@ -23,11 +23,11 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
|
|||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
import { getBiomeKey } from "#app/field/arena";
|
import { getBiomeKey } from "#app/field/arena";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { getPartyLuckValue, modifierTypes } from "#app/modifier/modifier-type";
|
import { getPartyLuckValue } from "#app/modifier/modifier-type";
|
||||||
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import {
|
import {
|
||||||
@ -227,7 +227,13 @@ async function doBiomeTransitionDialogueAndBattleInit() {
|
|||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(`${namespace}:boss_enraged`);
|
queueEncounterMessage(`${namespace}:boss_enraged`);
|
||||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
globalScene.phaseManager.unshiftNew(
|
||||||
|
"StatStageChangePhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
statChangesForBattle,
|
||||||
|
1,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -15,7 +15,7 @@ import { BiomeId } from "#enums/biome-id";
|
|||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
@ -26,7 +26,7 @@ import { EggSourceType } from "#enums/egg-source-types";
|
|||||||
import { EggTier } from "#enums/egg-type";
|
import { EggTier } from "#enums/egg-type";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { getPokeballTintColor } from "#app/data/pokeball";
|
import { getPokeballTintColor } from "#app/data/pokeball";
|
||||||
|
|
||||||
@ -658,8 +658,8 @@ function onGameOver() {
|
|||||||
globalScene.playBgm(globalScene.arena.bgm);
|
globalScene.playBgm(globalScene.arena.bgm);
|
||||||
|
|
||||||
// Clear any leftover battle phases
|
// Clear any leftover battle phases
|
||||||
globalScene.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue();
|
||||||
globalScene.clearPhaseQueueSplice();
|
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||||
|
|
||||||
// Return enemy Pokemon
|
// Return enemy Pokemon
|
||||||
const pokemon = globalScene.getEnemyPokemon();
|
const pokemon = globalScene.getEnemyPokemon();
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
getSpriteKeysFromPokemon,
|
getSpriteKeysFromPokemon,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
|
@ -8,27 +8,27 @@ import {
|
|||||||
generateModifierType,
|
generateModifierType,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/theStrongStuff";
|
const namespace = "mysteryEncounters/theStrongStuff";
|
||||||
@ -116,8 +116,12 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(`${namespace}:option.2.stat_boost`);
|
queueEncounterMessage(`${namespace}:option.2.stat_boost`);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 1),
|
"StatStageChangePhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
[Stat.DEF, Stat.SPDEF],
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -211,13 +215,13 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.PLAYER],
|
targets: [BattlerIndex.PLAYER],
|
||||||
move: new PokemonMove(MoveId.GASTRO_ACID),
|
move: new PokemonMove(MoveId.GASTRO_ACID),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.PLAYER],
|
targets: [BattlerIndex.PLAYER],
|
||||||
move: new PokemonMove(MoveId.STEALTH_ROCK),
|
move: new PokemonMove(MoveId.STEALTH_ROCK),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
transitionMysteryEncounterIntroVisuals,
|
transitionMysteryEncounterIntroVisuals,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -17,21 +17,18 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/abilities/ability";
|
import { applyPostBattleInitAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
|
||||||
import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
|
|
||||||
import { ReturnPhase } from "#app/phases/return-phase";
|
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
|
||||||
@ -143,7 +140,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounter
|
|||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
// Refuse the challenge, they full heal the party and give the player a Rarer Candy
|
// Refuse the challenge, they full heal the party and give the player a Rarer Candy
|
||||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
|
||||||
setEncounterRewards({
|
setEncounterRewards({
|
||||||
guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY],
|
guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY],
|
||||||
fillRemaining: false,
|
fillRemaining: false,
|
||||||
@ -209,7 +206,7 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
|
|||||||
for (const pokemon of playerField) {
|
for (const pokemon of playerField) {
|
||||||
pokemon.lapseTag(BattlerTagType.COMMANDED);
|
pokemon.lapseTag(BattlerTagType.COMMANDED);
|
||||||
}
|
}
|
||||||
playerField.forEach((_, p) => globalScene.unshiftPhase(new ReturnPhase(p)));
|
playerField.forEach((_, p) => globalScene.phaseManager.unshiftNew("ReturnPhase", p));
|
||||||
|
|
||||||
for (const pokemon of globalScene.getPlayerParty()) {
|
for (const pokemon of globalScene.getPlayerParty()) {
|
||||||
// Only trigger form change when Eiscue is in Noice form
|
// Only trigger form change when Eiscue is in Noice form
|
||||||
@ -224,10 +221,10 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
|
|||||||
|
|
||||||
// Each trainer battle is supposed to be a new fight, so reset all per-battle activation effects
|
// Each trainer battle is supposed to be a new fight, so reset all per-battle activation effects
|
||||||
pokemon.resetBattleAndWaveData();
|
pokemon.resetBattleAndWaveData();
|
||||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
globalScene.unshiftPhase(new ShowTrainerPhase());
|
globalScene.phaseManager.unshiftNew("ShowTrainerPhase");
|
||||||
// Hide the trainer and init next battle
|
// Hide the trainer and init next battle
|
||||||
const trainer = globalScene.currentBattle.trainer;
|
const trainer = globalScene.currentBattle.trainer;
|
||||||
// Unassign previous trainer from battle so it isn't destroyed before animation completes
|
// Unassign previous trainer from battle so it isn't destroyed before animation completes
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Ability } from "#app/data/abilities/ability-class";
|
import type { Ability } from "#app/data/abilities/ability";
|
||||||
import { allAbilities } from "#app/data/data-lists";
|
import { allAbilities } from "#app/data/data-lists";
|
||||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import {
|
import {
|
||||||
@ -12,7 +12,7 @@ import { speciesStarterCosts } from "#app/data/balance/starters";
|
|||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { AbilityAttr } from "#app/system/game-data";
|
import { AbilityAttr } from "#enums/ability-attr";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { isNullOrUndefined, randSeedShuffle } from "#app/utils/common";
|
import { isNullOrUndefined, randSeedShuffle } from "#app/utils/common";
|
||||||
@ -25,7 +25,7 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
|
|||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { getStatKey } from "#enums/stat";
|
import { getStatKey } from "#enums/stat";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
transitionMysteryEncounterIntroVisuals,
|
transitionMysteryEncounterIntroVisuals,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -21,13 +21,14 @@ import { HitHealModifier, PokemonHeldItemModifier, TurnHealModifier } from "#app
|
|||||||
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounters/trashToTreasure";
|
const namespace = "mysteryEncounters/trashToTreasure";
|
||||||
@ -207,13 +208,13 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.PLAYER],
|
targets: [BattlerIndex.PLAYER],
|
||||||
move: new PokemonMove(MoveId.TOXIC),
|
move: new PokemonMove(MoveId.TOXIC),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.ENEMY],
|
targets: [BattlerIndex.ENEMY],
|
||||||
move: new PokemonMove(MoveId.STOCKPILE),
|
move: new PokemonMove(MoveId.STOCKPILE),
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -29,15 +29,14 @@ import {
|
|||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { SelfStatusMove } from "#app/data/moves/move";
|
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { BerryModifier } from "#app/modifier/modifier";
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
import { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/uncommonBreed";
|
const namespace = "mysteryEncounters/uncommonBreed";
|
||||||
@ -103,8 +102,12 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
|||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
||||||
globalScene.unshiftPhase(
|
globalScene.phaseManager.unshiftNew(
|
||||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
"StatStageChangePhase",
|
||||||
|
pokemon.getBattlerIndex(),
|
||||||
|
true,
|
||||||
|
statChangesForBattle,
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -172,13 +175,13 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
|||||||
// Check what type of move the egg move is to determine target
|
// Check what type of move the egg move is to determine target
|
||||||
const pokemonMove = new PokemonMove(eggMove);
|
const pokemonMove = new PokemonMove(eggMove);
|
||||||
const move = pokemonMove.getMove();
|
const move = pokemonMove.getMove();
|
||||||
const target = move instanceof SelfStatusMove ? BattlerIndex.ENEMY : BattlerIndex.PLAYER;
|
const target = move.is("SelfStatusMove") ? BattlerIndex.ENEMY : BattlerIndex.PLAYER;
|
||||||
|
|
||||||
encounter.startOfBattleEffects.push({
|
encounter.startOfBattleEffects.push({
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [target],
|
targets: [target],
|
||||||
move: pokemonMove,
|
move: pokemonMove,
|
||||||
ignorePp: true,
|
useMode: MoveUseMode.IGNORE_PP,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,16 +16,17 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common";
|
import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
|
import { allSpecies } from "#app/data/data-lists";
|
||||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import {
|
import {
|
||||||
doPokemonTransformationSequence,
|
doPokemonTransformationSequence,
|
||||||
@ -34,12 +35,12 @@ import {
|
|||||||
import { getLevelTotalExp } from "#app/data/exp";
|
import { getLevelTotalExp } from "#app/data/exp";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { Challenges } from "#enums/challenges";
|
import { Challenges } from "#enums/challenges";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||||
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { allAbilities } from "../data-lists";
|
import { allAbilities } from "../data-lists";
|
||||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||||
|
import { SpeciesFormChangeItemTrigger } from "../pokemon-forms/form-change-triggers";
|
||||||
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
||||||
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { coerceArray, isNullOrUndefined } from "#app/utils/common";
|
||||||
import type { AbilityId } from "#enums/ability-id";
|
import type { AbilityId } from "#enums/ability-id";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
|
||||||
import { TimeOfDay } from "#enums/time-of-day";
|
import { TimeOfDay } from "#enums/time-of-day";
|
||||||
|
|
||||||
export interface EncounterRequirement {
|
export interface EncounterRequirement {
|
||||||
@ -270,20 +270,16 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
|||||||
|
|
||||||
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
||||||
super();
|
super();
|
||||||
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [timeOfDay];
|
this.requiredTimeOfDay = coerceArray(timeOfDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
const timeOfDay = globalScene.arena?.getTimeOfDay();
|
const timeOfDay = globalScene.arena?.getTimeOfDay();
|
||||||
if (
|
return !(
|
||||||
!isNullOrUndefined(timeOfDay) &&
|
!isNullOrUndefined(timeOfDay) &&
|
||||||
this.requiredTimeOfDay?.length > 0 &&
|
this.requiredTimeOfDay?.length > 0 &&
|
||||||
!this.requiredTimeOfDay.includes(timeOfDay)
|
!this.requiredTimeOfDay.includes(timeOfDay)
|
||||||
) {
|
);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override getDialogueToken(_pokemon?: PlayerPokemon): [string, string] {
|
override getDialogueToken(_pokemon?: PlayerPokemon): [string, string] {
|
||||||
@ -296,20 +292,16 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
|||||||
|
|
||||||
constructor(weather: WeatherType | WeatherType[]) {
|
constructor(weather: WeatherType | WeatherType[]) {
|
||||||
super();
|
super();
|
||||||
this.requiredWeather = Array.isArray(weather) ? weather : [weather];
|
this.requiredWeather = coerceArray(weather);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
const currentWeather = globalScene.arena.weather?.weatherType;
|
const currentWeather = globalScene.arena.weather?.weatherType;
|
||||||
if (
|
return !(
|
||||||
!isNullOrUndefined(currentWeather) &&
|
!isNullOrUndefined(currentWeather) &&
|
||||||
this.requiredWeather?.length > 0 &&
|
this.requiredWeather?.length > 0 &&
|
||||||
!this.requiredWeather.includes(currentWeather!)
|
!this.requiredWeather.includes(currentWeather!)
|
||||||
) {
|
);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override getDialogueToken(_pokemon?: PlayerPokemon): [string, string] {
|
override getDialogueToken(_pokemon?: PlayerPokemon): [string, string] {
|
||||||
@ -366,7 +358,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
|
|||||||
constructor(heldItem: string | string[], minNumberOfItems = 1) {
|
constructor(heldItem: string | string[], minNumberOfItems = 1) {
|
||||||
super();
|
super();
|
||||||
this.minNumberOfItems = minNumberOfItems;
|
this.minNumberOfItems = minNumberOfItems;
|
||||||
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
this.requiredHeldItemModifiers = coerceArray(heldItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -432,7 +424,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredSpecies = Array.isArray(species) ? species : [species];
|
this.requiredSpecies = coerceArray(species);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -472,7 +464,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredNature = Array.isArray(nature) ? nature : [nature];
|
this.requiredNature = coerceArray(nature);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -510,7 +502,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
|||||||
this.excludeFainted = excludeFainted;
|
this.excludeFainted = excludeFainted;
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredType = Array.isArray(type) ? type : [type];
|
this.requiredType = coerceArray(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -564,7 +556,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
|||||||
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredMoves = Array.isArray(moves) ? moves : [moves];
|
this.requiredMoves = coerceArray(moves);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -615,7 +607,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [learnableMove];
|
this.requiredMoves = coerceArray(learnableMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -671,7 +663,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
|||||||
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredAbilities = Array.isArray(abilities) ? abilities : [abilities];
|
this.requiredAbilities = coerceArray(abilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -716,7 +708,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [statusEffect];
|
this.requiredStatusEffect = coerceArray(statusEffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -791,7 +783,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [formChangeItem];
|
this.requiredFormChangeItem = coerceArray(formChangeItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
override meetsRequirement(): boolean {
|
||||||
@ -803,7 +795,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
|||||||
}
|
}
|
||||||
|
|
||||||
filterByForm(pokemon, formChangeItem) {
|
filterByForm(pokemon, formChangeItem) {
|
||||||
if (
|
return (
|
||||||
pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) &&
|
pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) &&
|
||||||
// Get all form changes for this species with an item trigger, including any compound triggers
|
// Get all form changes for this species with an item trigger, including any compound triggers
|
||||||
pokemonFormChanges[pokemon.species.speciesId]
|
pokemonFormChanges[pokemon.species.speciesId]
|
||||||
@ -812,10 +804,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
|||||||
.flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
|
.flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
|
||||||
.flatMap(fc => fc.item)
|
.flatMap(fc => fc.item)
|
||||||
.includes(formChangeItem)
|
.includes(formChangeItem)
|
||||||
) {
|
);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
||||||
@ -843,72 +832,6 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
|
||||||
requiredEvolutionItem: EvolutionItem[];
|
|
||||||
minNumberOfPokemon: number;
|
|
||||||
invertQuery: boolean;
|
|
||||||
|
|
||||||
constructor(evolutionItems: EvolutionItem | EvolutionItem[], minNumberOfPokemon = 1, invertQuery = false) {
|
|
||||||
super();
|
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
|
||||||
this.invertQuery = invertQuery;
|
|
||||||
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [evolutionItems];
|
|
||||||
}
|
|
||||||
|
|
||||||
override meetsRequirement(): boolean {
|
|
||||||
const partyPokemon = globalScene.getPlayerParty();
|
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionItem?.length < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
|
|
||||||
}
|
|
||||||
|
|
||||||
filterByEvo(pokemon, evolutionItem) {
|
|
||||||
if (
|
|
||||||
pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) &&
|
|
||||||
pokemonEvolutions[pokemon.species.speciesId].filter(
|
|
||||||
e => e.item === evolutionItem && (!e.condition || e.condition.predicate(pokemon)),
|
|
||||||
).length &&
|
|
||||||
pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
pokemon.isFusion() &&
|
|
||||||
pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) &&
|
|
||||||
pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(
|
|
||||||
e => e.item === evolutionItem && (!e.condition || e.condition.predicate(pokemon)),
|
|
||||||
).length &&
|
|
||||||
pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
|
||||||
if (!this.invertQuery) {
|
|
||||||
return partyPokemon.filter(
|
|
||||||
pokemon =>
|
|
||||||
this.requiredEvolutionItem.filter(evolutionItem => this.filterByEvo(pokemon, evolutionItem)).length > 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed evolutionItemss
|
|
||||||
return partyPokemon.filter(
|
|
||||||
pokemon =>
|
|
||||||
this.requiredEvolutionItem.filter(evolutionItems => this.filterByEvo(pokemon, evolutionItems)).length === 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
|
||||||
const requiredItems = this.requiredEvolutionItem.filter(evoItem => this.filterByEvo(pokemon, evoItem));
|
|
||||||
if (requiredItems.length > 0) {
|
|
||||||
return ["evolutionItem", EvolutionItem[requiredItems[0]]];
|
|
||||||
}
|
|
||||||
return ["evolutionItem", ""];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HeldItemRequirement extends EncounterPokemonRequirement {
|
export class HeldItemRequirement extends EncounterPokemonRequirement {
|
||||||
requiredHeldItemModifiers: string[];
|
requiredHeldItemModifiers: string[];
|
||||||
minNumberOfPokemon: number;
|
minNumberOfPokemon: number;
|
||||||
@ -919,7 +842,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
this.requiredHeldItemModifiers = coerceArray(heldItem);
|
||||||
this.requireTransferable = requireTransferable;
|
this.requireTransferable = requireTransferable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -983,7 +906,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
|||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [heldItemTypes];
|
this.requiredHeldItemTypes = coerceArray(heldItemTypes);
|
||||||
this.requireTransferable = requireTransferable;
|
this.requireTransferable = requireTransferable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/data/mystery-encounters/mystery-encounters";
|
import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/constants";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import type { PokemonMove } from "../moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils/common";
|
import { capitalizeFirstLetter, coerceArray, isNullOrUndefined } from "#app/utils/common";
|
||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
||||||
import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
|
import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
|
||||||
@ -20,22 +21,22 @@ import {
|
|||||||
StatusEffectRequirement,
|
StatusEffectRequirement,
|
||||||
WaveRangeRequirement,
|
WaveRangeRequirement,
|
||||||
} from "./mystery-encounter-requirements";
|
} from "./mystery-encounter-requirements";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import type { GameModes } from "#app/game-mode";
|
import type { GameModes } from "#enums/game-modes";
|
||||||
import type { EncounterAnim } from "#enums/encounter-anims";
|
import type { EncounterAnim } from "#enums/encounter-anims";
|
||||||
import type { Challenges } from "#enums/challenges";
|
import type { Challenges } from "#enums/challenges";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
|
||||||
export interface EncounterStartOfBattleEffect {
|
export interface EncounterStartOfBattleEffect {
|
||||||
sourcePokemon?: Pokemon;
|
sourcePokemon?: Pokemon;
|
||||||
sourceBattlerIndex?: BattlerIndex;
|
sourceBattlerIndex?: BattlerIndex;
|
||||||
targets: BattlerIndex[];
|
targets: BattlerIndex[];
|
||||||
move: PokemonMove;
|
move: PokemonMove;
|
||||||
ignorePp: boolean;
|
useMode: MoveUseMode; // TODO: This should always be ignore PP...
|
||||||
followUp?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_MAX_ALLOWED_ENCOUNTERS = 2;
|
const DEFAULT_MAX_ALLOWED_ENCOUNTERS = 2;
|
||||||
@ -253,7 +254,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
|||||||
*/
|
*/
|
||||||
selectedOption?: MysteryEncounterOption;
|
selectedOption?: MysteryEncounterOption;
|
||||||
/**
|
/**
|
||||||
* Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases
|
* Array containing data pertaining to free moves used at the start of a battle mystery envounter.
|
||||||
*/
|
*/
|
||||||
startOfBattleEffects: EncounterStartOfBattleEffect[] = [];
|
startOfBattleEffects: EncounterStartOfBattleEffect[] = [];
|
||||||
/**
|
/**
|
||||||
@ -716,7 +717,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
withAnimations(
|
withAnimations(
|
||||||
...encounterAnimations: EncounterAnim[]
|
...encounterAnimations: EncounterAnim[]
|
||||||
): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
|
): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
|
||||||
const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [encounterAnimations];
|
const animations = coerceArray(encounterAnimations);
|
||||||
return Object.assign(this, { encounterAnimations: animations });
|
return Object.assign(this, { encounterAnimations: animations });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,7 +729,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
withDisallowedGameModes(
|
withDisallowedGameModes(
|
||||||
...disallowedGameModes: GameModes[]
|
...disallowedGameModes: GameModes[]
|
||||||
): this & Required<Pick<IMysteryEncounter, "disallowedGameModes">> {
|
): this & Required<Pick<IMysteryEncounter, "disallowedGameModes">> {
|
||||||
const gameModes = Array.isArray(disallowedGameModes) ? disallowedGameModes : [disallowedGameModes];
|
const gameModes = coerceArray(disallowedGameModes);
|
||||||
return Object.assign(this, { disallowedGameModes: gameModes });
|
return Object.assign(this, { disallowedGameModes: gameModes });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,7 +741,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
withDisallowedChallenges(
|
withDisallowedChallenges(
|
||||||
...disallowedChallenges: Challenges[]
|
...disallowedChallenges: Challenges[]
|
||||||
): this & Required<Pick<IMysteryEncounter, "disallowedChallenges">> {
|
): this & Required<Pick<IMysteryEncounter, "disallowedChallenges">> {
|
||||||
const challenges = Array.isArray(disallowedChallenges) ? disallowedChallenges : [disallowedChallenges];
|
const challenges = coerceArray(disallowedChallenges);
|
||||||
return Object.assign(this, { disallowedChallenges: challenges });
|
return Object.assign(this, { disallowedChallenges: challenges });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,42 +34,6 @@ import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encount
|
|||||||
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
|
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
|
||||||
import { getBiomeName } from "#app/data/balance/biomes";
|
import { getBiomeName } from "#app/data/balance/biomes";
|
||||||
|
|
||||||
/**
|
|
||||||
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
|
|
||||||
*/
|
|
||||||
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 3;
|
|
||||||
/**
|
|
||||||
* The divisor for determining ME spawns, defines the "maximum" weight required for a spawn
|
|
||||||
* If spawn_weight === MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, 100% chance to spawn a ME
|
|
||||||
*/
|
|
||||||
export const MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT = 256;
|
|
||||||
/**
|
|
||||||
* When an ME spawn roll fails, WEIGHT_INCREMENT_ON_SPAWN_MISS is added to future rolls for ME spawn checks.
|
|
||||||
* These values are cleared whenever the next ME spawns, and spawn weight returns to BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT
|
|
||||||
*/
|
|
||||||
export const WEIGHT_INCREMENT_ON_SPAWN_MISS = 3;
|
|
||||||
/**
|
|
||||||
* Specifies the target average for total ME spawns in a single Classic run.
|
|
||||||
* Used by anti-variance mechanic to check whether a run is above or below the target on a given wave.
|
|
||||||
*/
|
|
||||||
export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 12;
|
|
||||||
/**
|
|
||||||
* Will increase/decrease the chance of spawning a ME based on the current run's total MEs encountered vs AVERAGE_ENCOUNTERS_PER_RUN_TARGET
|
|
||||||
* Example:
|
|
||||||
* AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 17 (expects avg 1 ME every 10 floors)
|
|
||||||
* ANTI_VARIANCE_WEIGHT_MODIFIER = 15
|
|
||||||
*
|
|
||||||
* On wave 20, if 1 ME has been encountered, the difference from expected average is 0 MEs.
|
|
||||||
* So anti-variance adds 0/256 to the spawn weight check for ME spawn.
|
|
||||||
*
|
|
||||||
* On wave 20, if 0 MEs have been encountered, the difference from expected average is 1 ME.
|
|
||||||
* So anti-variance adds 15/256 to the spawn weight check for ME spawn.
|
|
||||||
*
|
|
||||||
* On wave 20, if 2 MEs have been encountered, the difference from expected average is -1 ME.
|
|
||||||
* So anti-variance adds -15/256 to the spawn weight check for ME spawn.
|
|
||||||
*/
|
|
||||||
export const ANTI_VARIANCE_WEIGHT_MODIFIER = 15;
|
|
||||||
|
|
||||||
export const EXTREME_ENCOUNTER_BIOMES = [
|
export const EXTREME_ENCOUNTER_BIOMES = [
|
||||||
BiomeId.SEA,
|
BiomeId.SEA,
|
||||||
BiomeId.SEABED,
|
BiomeId.SEABED,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { coerceArray, isNullOrUndefined } from "#app/utils/common";
|
||||||
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
|
|||||||
|
|
||||||
constructor(requiredMoves: MoveId | MoveId[], options: CanLearnMoveRequirementOptions = {}) {
|
constructor(requiredMoves: MoveId | MoveId[], options: CanLearnMoveRequirementOptions = {}) {
|
||||||
super();
|
super();
|
||||||
this.requiredMoves = Array.isArray(requiredMoves) ? requiredMoves : [requiredMoves];
|
this.requiredMoves = coerceArray(requiredMoves);
|
||||||
|
|
||||||
this.excludeLevelMoves = options.excludeLevelMoves ?? false;
|
this.excludeLevelMoves = options.excludeLevelMoves ?? false;
|
||||||
this.excludeTmMoves = options.excludeTmMoves ?? false;
|
this.excludeTmMoves = options.excludeTmMoves ?? false;
|
||||||
|
@ -51,7 +51,7 @@ function getTextWithDialogueTokens(keyOrString: string): string | null {
|
|||||||
*/
|
*/
|
||||||
export function queueEncounterMessage(contentKey: string): void {
|
export function queueEncounterMessage(contentKey: string): void {
|
||||||
const text: string | null = getEncounterText(contentKey);
|
const text: string | null = getEncounterText(contentKey);
|
||||||
globalScene.queueMessage(text ?? "", null, true);
|
globalScene.phaseManager.queueMessage(text ?? "", null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,42 +1,36 @@
|
|||||||
import type Battle from "#app/battle";
|
import type Battle from "#app/battle";
|
||||||
import { BattlerIndex } from "#app/battle";
|
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
|
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
|
||||||
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import {
|
import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/constants";
|
||||||
AVERAGE_ENCOUNTERS_PER_RUN_TARGET,
|
|
||||||
WEIGHT_INCREMENT_ON_SPAWN_MISS,
|
|
||||||
} from "#app/data/mystery-encounters/mystery-encounters";
|
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import type { AiType, PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import type { AiType } from "#enums/ai-type";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { EnemyPokemon, FieldPosition, PokemonMove } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
import { FieldPosition } from "#enums/field-position";
|
||||||
import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type";
|
import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type";
|
||||||
import {
|
import {
|
||||||
getPartyLuckValue,
|
getPartyLuckValue,
|
||||||
ModifierPoolType,
|
|
||||||
ModifierTypeGenerator,
|
ModifierTypeGenerator,
|
||||||
ModifierTypeOption,
|
ModifierTypeOption,
|
||||||
modifierTypes,
|
|
||||||
regenerateModifierPoolThresholds,
|
regenerateModifierPoolThresholds,
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
import {
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
MysteryEncounterBattlePhase,
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
MysteryEncounterBattleStartCleanupPhase,
|
|
||||||
MysteryEncounterPhase,
|
|
||||||
MysteryEncounterRewardsPhase,
|
|
||||||
} from "#app/phases/mystery-encounter-phases";
|
|
||||||
import type PokemonData from "#app/system/pokemon-data";
|
import type PokemonData from "#app/system/pokemon-data";
|
||||||
import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler";
|
||||||
import { PartyUiMode } from "#app/ui/party-ui-handler";
|
import { PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { isNullOrUndefined, randSeedInt, randomString, randSeedItem } from "#app/utils/common";
|
import { isNullOrUndefined, randSeedInt, randomString, randSeedItem, coerceArray } from "#app/utils/common";
|
||||||
import type { BattlerTagType } from "#enums/battler-tag-type";
|
import type { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
import type { TrainerType } from "#enums/trainer-type";
|
import type { TrainerType } from "#enums/trainer-type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import type { Gender } from "#app/data/gender";
|
import type { Gender } from "#app/data/gender";
|
||||||
import type { Nature } from "#enums/nature";
|
import type { Nature } from "#enums/nature";
|
||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
@ -50,18 +44,11 @@ import type PokemonSpecies from "#app/data/pokemon-species";
|
|||||||
import type { IEggOptions } from "#app/data/egg";
|
import type { IEggOptions } from "#app/data/egg";
|
||||||
import { Egg } from "#app/data/egg";
|
import { Egg } from "#app/data/egg";
|
||||||
import type { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
import type { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||||
import { MovePhase } from "#app/phases/move-phase";
|
|
||||||
import { EggLapsePhase } from "#app/phases/egg-lapse-phase";
|
|
||||||
import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase";
|
|
||||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
|
||||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
|
||||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
|
||||||
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
|
||||||
import type { Variant } from "#app/sprites/variant";
|
import type { Variant } from "#app/sprites/variant";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { getNatureName } from "#app/data/nature";
|
import { getNatureName } from "#app/data/nature";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
@ -428,7 +415,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
|
|||||||
console.log("Moveset:", moveset);
|
console.log("Moveset:", moveset);
|
||||||
});
|
});
|
||||||
|
|
||||||
globalScene.pushPhase(new MysteryEncounterBattlePhase(partyConfig.disableSwitch));
|
globalScene.phaseManager.pushNew("MysteryEncounterBattlePhase", partyConfig.disableSwitch);
|
||||||
|
|
||||||
await Promise.all(loadEnemyAssets);
|
await Promise.all(loadEnemyAssets);
|
||||||
battle.enemyParty.forEach((enemyPokemon_2, e_1) => {
|
battle.enemyParty.forEach((enemyPokemon_2, e_1) => {
|
||||||
@ -461,7 +448,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig):
|
|||||||
* @param moves
|
* @param moves
|
||||||
*/
|
*/
|
||||||
export function loadCustomMovesForEncounter(moves: MoveId | MoveId[]) {
|
export function loadCustomMovesForEncounter(moves: MoveId | MoveId[]) {
|
||||||
moves = Array.isArray(moves) ? moves : [moves];
|
moves = coerceArray(moves);
|
||||||
return Promise.all(moves.map(move => initMoveAnim(move))).then(() => loadMoveAnimAssets(moves));
|
return Promise.all(moves.map(move => initMoveAnim(move))).then(() => loadMoveAnimAssets(moves));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,7 +467,7 @@ export function updatePlayerMoney(changeValue: number, playSound = true, showMes
|
|||||||
}
|
}
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
if (changeValue < 0) {
|
if (changeValue < 0) {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("mysteryEncounterMessages:paid_money", {
|
i18next.t("mysteryEncounterMessages:paid_money", {
|
||||||
amount: -changeValue,
|
amount: -changeValue,
|
||||||
}),
|
}),
|
||||||
@ -488,7 +475,7 @@ export function updatePlayerMoney(changeValue: number, playSound = true, showMes
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
globalScene.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("mysteryEncounterMessages:receive_money", {
|
i18next.t("mysteryEncounterMessages:receive_money", {
|
||||||
amount: changeValue,
|
amount: changeValue,
|
||||||
}),
|
}),
|
||||||
@ -767,9 +754,9 @@ export function setEncounterRewards(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (customShopRewards) {
|
if (customShopRewards) {
|
||||||
globalScene.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards));
|
globalScene.phaseManager.unshiftNew("SelectModifierPhase", 0, undefined, customShopRewards);
|
||||||
} else {
|
} else {
|
||||||
globalScene.tryRemovePhase(p => p instanceof SelectModifierPhase);
|
globalScene.phaseManager.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eggRewards) {
|
if (eggRewards) {
|
||||||
@ -804,10 +791,10 @@ export function setEncounterRewards(
|
|||||||
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
|
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
|
||||||
*/
|
*/
|
||||||
export function setEncounterExp(participantId: number | number[], baseExpValue: number, useWaveIndex = true) {
|
export function setEncounterExp(participantId: number | number[], baseExpValue: number, useWaveIndex = true) {
|
||||||
const participantIds = Array.isArray(participantId) ? participantId : [participantId];
|
const participantIds = coerceArray(participantId);
|
||||||
|
|
||||||
globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => {
|
globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => {
|
||||||
globalScene.unshiftPhase(new PartyExpPhase(baseExpValue, useWaveIndex, new Set(participantIds)));
|
globalScene.phaseManager.unshiftNew("PartyExpPhase", baseExpValue, useWaveIndex, new Set(participantIds));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@ -829,7 +816,7 @@ export class OptionSelectSettings {
|
|||||||
* @param optionSelectSettings
|
* @param optionSelectSettings
|
||||||
*/
|
*/
|
||||||
export function initSubsequentOptionSelect(optionSelectSettings: OptionSelectSettings) {
|
export function initSubsequentOptionSelect(optionSelectSettings: OptionSelectSettings) {
|
||||||
globalScene.pushPhase(new MysteryEncounterPhase(optionSelectSettings));
|
globalScene.phaseManager.pushNew("MysteryEncounterPhase", optionSelectSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -843,8 +830,8 @@ export function leaveEncounterWithoutBattle(
|
|||||||
encounterMode: MysteryEncounterMode = MysteryEncounterMode.NO_BATTLE,
|
encounterMode: MysteryEncounterMode = MysteryEncounterMode.NO_BATTLE,
|
||||||
) {
|
) {
|
||||||
globalScene.currentBattle.mysteryEncounter!.encounterMode = encounterMode;
|
globalScene.currentBattle.mysteryEncounter!.encounterMode = encounterMode;
|
||||||
globalScene.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue();
|
||||||
globalScene.clearPhaseQueueSplice();
|
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||||
handleMysteryEncounterVictory(addHealPhase);
|
handleMysteryEncounterVictory(addHealPhase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -857,8 +844,8 @@ 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.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue();
|
||||||
globalScene.unshiftPhase(new GameOverPhase());
|
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -869,8 +856,8 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) {
|
if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) {
|
||||||
globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
|
globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase);
|
||||||
globalScene.pushPhase(new EggLapsePhase());
|
globalScene.phaseManager.pushNew("EggLapsePhase");
|
||||||
} else if (
|
} else if (
|
||||||
!globalScene
|
!globalScene
|
||||||
.getEnemyParty()
|
.getEnemyParty()
|
||||||
@ -878,15 +865,15 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu
|
|||||||
encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true),
|
encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
globalScene.pushPhase(new BattleEndPhase(true));
|
globalScene.phaseManager.pushNew("BattleEndPhase", true);
|
||||||
if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) {
|
||||||
globalScene.pushPhase(new TrainerVictoryPhase());
|
globalScene.phaseManager.pushNew("TrainerVictoryPhase");
|
||||||
}
|
}
|
||||||
if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) {
|
if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) {
|
||||||
globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
|
globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase);
|
||||||
if (!encounter.doContinueEncounter) {
|
if (!encounter.doContinueEncounter) {
|
||||||
// Only lapse eggs once for multi-battle encounters
|
// Only lapse eggs once for multi-battle encounters
|
||||||
globalScene.pushPhase(new EggLapsePhase());
|
globalScene.phaseManager.pushNew("EggLapsePhase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -900,8 +887,8 @@ 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.clearPhaseQueue();
|
globalScene.phaseManager.clearPhaseQueue();
|
||||||
globalScene.unshiftPhase(new GameOverPhase());
|
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,14 +899,14 @@ export function handleMysteryEncounterBattleFailed(addHealPhase = false, doNotCo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE) {
|
if (encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE) {
|
||||||
globalScene.pushPhase(new BattleEndPhase(false));
|
globalScene.phaseManager.pushNew("BattleEndPhase", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase));
|
globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase);
|
||||||
|
|
||||||
if (!encounter.doContinueEncounter) {
|
if (!encounter.doContinueEncounter) {
|
||||||
// Only lapse eggs once for multi-battle encounters
|
// Only lapse eggs once for multi-battle encounters
|
||||||
globalScene.pushPhase(new EggLapsePhase());
|
globalScene.phaseManager.pushNew("EggLapsePhase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -986,30 +973,12 @@ export function handleMysteryEncounterBattleStartEffects() {
|
|||||||
) {
|
) {
|
||||||
const effects = encounter.startOfBattleEffects;
|
const effects = encounter.startOfBattleEffects;
|
||||||
effects.forEach(effect => {
|
effects.forEach(effect => {
|
||||||
let source: EnemyPokemon | Pokemon;
|
const source = effect.sourcePokemon ?? globalScene.getField()[effect.sourceBattlerIndex ?? 0];
|
||||||
if (effect.sourcePokemon) {
|
globalScene.phaseManager.pushNew("MovePhase", source, effect.targets, effect.move, effect.useMode);
|
||||||
source = effect.sourcePokemon;
|
|
||||||
} else if (!isNullOrUndefined(effect.sourceBattlerIndex)) {
|
|
||||||
if (effect.sourceBattlerIndex === BattlerIndex.ATTACKER) {
|
|
||||||
source = globalScene.getEnemyField()[0];
|
|
||||||
} else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY) {
|
|
||||||
source = globalScene.getEnemyField()[0];
|
|
||||||
} else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY_2) {
|
|
||||||
source = globalScene.getEnemyField()[1];
|
|
||||||
} else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER) {
|
|
||||||
source = globalScene.getPlayerField()[0];
|
|
||||||
} else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER_2) {
|
|
||||||
source = globalScene.getPlayerField()[1];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
source = globalScene.getEnemyField()[0];
|
|
||||||
}
|
|
||||||
// @ts-ignore: source cannot be undefined
|
|
||||||
globalScene.pushPhase(new MovePhase(source, effect.targets, effect.move, effect.followUp, effect.ignorePp));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pseudo turn end phase to reset flinch states, Endure, etc.
|
// Pseudo turn end phase to reset flinch states, Endure, etc.
|
||||||
globalScene.pushPhase(new MysteryEncounterBattleStartCleanupPhase());
|
globalScene.phaseManager.pushNew("MysteryEncounterBattleStartCleanupPhase");
|
||||||
|
|
||||||
encounter.startOfBattleEffectsComplete = true;
|
encounter.startOfBattleEffectsComplete = true;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import { PartyUiMode } from "#app/ui/party-ui-handler";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import type { PokemonType } from "#enums/pokemon-type";
|
import type { PokemonType } from "#enums/pokemon-type";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import {
|
import {
|
||||||
getEncounterText,
|
getEncounterText,
|
||||||
@ -29,10 +29,9 @@ import {
|
|||||||
} from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import type { PermanentStat } from "#enums/stat";
|
import type { PermanentStat } from "#enums/stat";
|
||||||
import { VictoryPhase } from "#app/phases/victory-phase";
|
|
||||||
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
|
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
|
||||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||||
import type { AbilityId } from "#enums/ability-id";
|
import type { AbilityId } from "#enums/ability-id";
|
||||||
@ -675,7 +674,7 @@ export async function catchPokemon(
|
|||||||
if (!globalScene.getEnemyParty().some(p => p.id === pokemon.id)) {
|
if (!globalScene.getEnemyParty().some(p => p.id === pokemon.id)) {
|
||||||
globalScene.getEnemyParty().push(pokemon);
|
globalScene.getEnemyParty().push(pokemon);
|
||||||
}
|
}
|
||||||
globalScene.unshiftPhase(new VictoryPhase(pokemon.id, true));
|
globalScene.phaseManager.unshiftNew("VictoryPhase", pokemon.id, true);
|
||||||
globalScene.pokemonInfoContainer.hide();
|
globalScene.pokemonInfoContainer.hide();
|
||||||
if (pokeball) {
|
if (pokeball) {
|
||||||
removePb(pokeball);
|
removePb(pokeball);
|
||||||
|
97
src/data/phase-priority-queue.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { Phase } from "#app/phase";
|
||||||
|
import { ActivatePriorityQueuePhase } from "#app/phases/activate-priority-queue-phase";
|
||||||
|
import type { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||||
|
import { PostSummonActivateAbilityPhase } from "#app/phases/post-summon-activate-ability-phase";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { BooleanHolder } from "#app/utils/common";
|
||||||
|
import { TrickRoomTag } from "#app/data/arena-tag";
|
||||||
|
import { DynamicPhaseType } from "#enums/dynamic-phase-type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier";
|
|
||||||
import { NumberHolder } from "#app/utils/common";
|
import { NumberHolder } from "#app/utils/common";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
@ -94,7 +93,7 @@ export function getCriticalCaptureChance(modifiedCatchRate: number): number {
|
|||||||
}
|
}
|
||||||
const dexCount = globalScene.gameData.getSpeciesCount(d => !!d.caughtAttr);
|
const dexCount = globalScene.gameData.getSpeciesCount(d => !!d.caughtAttr);
|
||||||
const catchingCharmMultiplier = new NumberHolder(1);
|
const catchingCharmMultiplier = new NumberHolder(1);
|
||||||
globalScene.findModifier(m => m instanceof CriticalCatchChanceBoosterModifier)?.apply(catchingCharmMultiplier);
|
globalScene.findModifier(m => m.is("CriticalCatchChanceBoosterModifier"))?.apply(catchingCharmMultiplier);
|
||||||
const dexMultiplier =
|
const dexMultiplier =
|
||||||
globalScene.gameMode.isDaily || dexCount > 800
|
globalScene.gameMode.isDaily || dexCount > 800
|
||||||
? 2.5
|
? 2.5
|
||||||
|
@ -1,139 +1,30 @@
|
|||||||
import { PokemonFormChangeItemModifier } from "../modifier/modifier";
|
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
|
||||||
import { allMoves } from "./data-lists";
|
import { allMoves } from "./data-lists";
|
||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import type { Constructor, nil } from "#app/utils/common";
|
import type { Constructor, nil } from "#app/utils/common";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import type { TimeOfDay } from "#enums/time-of-day";
|
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
|
||||||
import i18next from "i18next";
|
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { Challenges } from "#app/enums/challenges";
|
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
export enum FormChangeItem {
|
import {
|
||||||
NONE,
|
MeloettaFormChangePostMoveTrigger,
|
||||||
|
SpeciesDefaultFormMatchTrigger,
|
||||||
ABOMASITE,
|
SpeciesFormChangeAbilityTrigger,
|
||||||
ABSOLITE,
|
SpeciesFormChangeActiveTrigger,
|
||||||
AERODACTYLITE,
|
SpeciesFormChangeCompoundTrigger,
|
||||||
AGGRONITE,
|
SpeciesFormChangeItemTrigger,
|
||||||
ALAKAZITE,
|
SpeciesFormChangeLapseTeraTrigger,
|
||||||
ALTARIANITE,
|
SpeciesFormChangeManualTrigger,
|
||||||
AMPHAROSITE,
|
SpeciesFormChangeMoveLearnedTrigger,
|
||||||
AUDINITE,
|
SpeciesFormChangePreMoveTrigger,
|
||||||
BANETTITE,
|
SpeciesFormChangeRevertWeatherFormTrigger,
|
||||||
BEEDRILLITE,
|
SpeciesFormChangeTeraTrigger,
|
||||||
BLASTOISINITE,
|
type SpeciesFormChangeTrigger,
|
||||||
BLAZIKENITE,
|
SpeciesFormChangeWeatherTrigger,
|
||||||
CAMERUPTITE,
|
} from "./pokemon-forms/form-change-triggers";
|
||||||
CHARIZARDITE_X,
|
|
||||||
CHARIZARDITE_Y,
|
|
||||||
DIANCITE,
|
|
||||||
GALLADITE,
|
|
||||||
GARCHOMPITE,
|
|
||||||
GARDEVOIRITE,
|
|
||||||
GENGARITE,
|
|
||||||
GLALITITE,
|
|
||||||
GYARADOSITE,
|
|
||||||
HERACRONITE,
|
|
||||||
HOUNDOOMINITE,
|
|
||||||
KANGASKHANITE,
|
|
||||||
LATIASITE,
|
|
||||||
LATIOSITE,
|
|
||||||
LOPUNNITE,
|
|
||||||
LUCARIONITE,
|
|
||||||
MANECTITE,
|
|
||||||
MAWILITE,
|
|
||||||
MEDICHAMITE,
|
|
||||||
METAGROSSITE,
|
|
||||||
MEWTWONITE_X,
|
|
||||||
MEWTWONITE_Y,
|
|
||||||
PIDGEOTITE,
|
|
||||||
PINSIRITE,
|
|
||||||
RAYQUAZITE,
|
|
||||||
SABLENITE,
|
|
||||||
SALAMENCITE,
|
|
||||||
SCEPTILITE,
|
|
||||||
SCIZORITE,
|
|
||||||
SHARPEDONITE,
|
|
||||||
SLOWBRONITE,
|
|
||||||
STEELIXITE,
|
|
||||||
SWAMPERTITE,
|
|
||||||
TYRANITARITE,
|
|
||||||
VENUSAURITE,
|
|
||||||
|
|
||||||
BLUE_ORB = 50,
|
|
||||||
RED_ORB,
|
|
||||||
ADAMANT_CRYSTAL,
|
|
||||||
LUSTROUS_GLOBE,
|
|
||||||
GRISEOUS_CORE,
|
|
||||||
REVEAL_GLASS,
|
|
||||||
MAX_MUSHROOMS,
|
|
||||||
DARK_STONE,
|
|
||||||
LIGHT_STONE,
|
|
||||||
PRISON_BOTTLE,
|
|
||||||
RUSTED_SWORD,
|
|
||||||
RUSTED_SHIELD,
|
|
||||||
ICY_REINS_OF_UNITY,
|
|
||||||
SHADOW_REINS_OF_UNITY,
|
|
||||||
ULTRANECROZIUM_Z,
|
|
||||||
|
|
||||||
SHARP_METEORITE = 100,
|
|
||||||
HARD_METEORITE,
|
|
||||||
SMOOTH_METEORITE,
|
|
||||||
GRACIDEA,
|
|
||||||
SHOCK_DRIVE,
|
|
||||||
BURN_DRIVE,
|
|
||||||
CHILL_DRIVE,
|
|
||||||
DOUSE_DRIVE,
|
|
||||||
N_SOLARIZER,
|
|
||||||
N_LUNARIZER,
|
|
||||||
WELLSPRING_MASK,
|
|
||||||
HEARTHFLAME_MASK,
|
|
||||||
CORNERSTONE_MASK,
|
|
||||||
FIST_PLATE,
|
|
||||||
SKY_PLATE,
|
|
||||||
TOXIC_PLATE,
|
|
||||||
EARTH_PLATE,
|
|
||||||
STONE_PLATE,
|
|
||||||
INSECT_PLATE,
|
|
||||||
SPOOKY_PLATE,
|
|
||||||
IRON_PLATE,
|
|
||||||
FLAME_PLATE,
|
|
||||||
SPLASH_PLATE,
|
|
||||||
MEADOW_PLATE,
|
|
||||||
ZAP_PLATE,
|
|
||||||
MIND_PLATE,
|
|
||||||
ICICLE_PLATE,
|
|
||||||
DRACO_PLATE,
|
|
||||||
DREAD_PLATE,
|
|
||||||
PIXIE_PLATE,
|
|
||||||
BLANK_PLATE, // TODO: Find a potential use for this
|
|
||||||
LEGEND_PLATE, // TODO: Find a potential use for this
|
|
||||||
FIGHTING_MEMORY,
|
|
||||||
FLYING_MEMORY,
|
|
||||||
POISON_MEMORY,
|
|
||||||
GROUND_MEMORY,
|
|
||||||
ROCK_MEMORY,
|
|
||||||
BUG_MEMORY,
|
|
||||||
GHOST_MEMORY,
|
|
||||||
STEEL_MEMORY,
|
|
||||||
FIRE_MEMORY,
|
|
||||||
WATER_MEMORY,
|
|
||||||
GRASS_MEMORY,
|
|
||||||
ELECTRIC_MEMORY,
|
|
||||||
PSYCHIC_MEMORY,
|
|
||||||
ICE_MEMORY,
|
|
||||||
DRAGON_MEMORY,
|
|
||||||
DARK_MEMORY,
|
|
||||||
FAIRY_MEMORY,
|
|
||||||
NORMAL_MEMORY, // TODO: Find a potential use for this
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
|
export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
|
||||||
export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void;
|
export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void;
|
||||||
@ -186,11 +77,7 @@ export class SpeciesFormChange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.trigger.canChange(pokemon)) {
|
return this.trigger.canChange(pokemon);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findTrigger(triggerType: Constructor<SpeciesFormChangeTrigger>): SpeciesFormChangeTrigger | nil {
|
findTrigger(triggerType: Constructor<SpeciesFormChangeTrigger>): SpeciesFormChangeTrigger | nil {
|
||||||
@ -218,347 +105,6 @@ export class SpeciesFormChangeCondition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class SpeciesFormChangeTrigger {
|
|
||||||
public description = "";
|
|
||||||
|
|
||||||
canChange(_pokemon: Pokemon): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasTriggerType(triggerType: Constructor<SpeciesFormChangeTrigger>): boolean {
|
|
||||||
return this instanceof triggerType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeManualTrigger extends SpeciesFormChangeTrigger {}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeAbilityTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public description: string = i18next.t("pokemonEvolutions:Forms.ability");
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeCompoundTrigger {
|
|
||||||
public description = "";
|
|
||||||
public triggers: SpeciesFormChangeTrigger[];
|
|
||||||
|
|
||||||
constructor(...triggers: SpeciesFormChangeTrigger[]) {
|
|
||||||
this.triggers = triggers;
|
|
||||||
this.description = this.triggers
|
|
||||||
.filter(trigger => trigger?.description?.length > 0)
|
|
||||||
.map(trigger => trigger.description)
|
|
||||||
.join(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
for (const trigger of this.triggers) {
|
|
||||||
if (!trigger.canChange(pokemon)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasTriggerType(triggerType: Constructor<SpeciesFormChangeTrigger>): boolean {
|
|
||||||
return !!this.triggers.find(t => t.hasTriggerType(triggerType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeItemTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public item: FormChangeItem;
|
|
||||||
public active: boolean;
|
|
||||||
|
|
||||||
constructor(item: FormChangeItem, active = true) {
|
|
||||||
super();
|
|
||||||
this.item = item;
|
|
||||||
this.active = active;
|
|
||||||
this.description = this.active
|
|
||||||
? i18next.t("pokemonEvolutions:Forms.item", {
|
|
||||||
item: i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.item]}`),
|
|
||||||
})
|
|
||||||
: i18next.t("pokemonEvolutions:Forms.deactivateItem", {
|
|
||||||
item: i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.item]}`),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return !!globalScene.findModifier(
|
|
||||||
m =>
|
|
||||||
m instanceof PokemonFormChangeItemModifier &&
|
|
||||||
m.pokemonId === pokemon.id &&
|
|
||||||
m.formChangeItem === this.item &&
|
|
||||||
m.active === this.active,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeTimeOfDayTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public timesOfDay: TimeOfDay[];
|
|
||||||
|
|
||||||
constructor(...timesOfDay: TimeOfDay[]) {
|
|
||||||
super();
|
|
||||||
this.timesOfDay = timesOfDay;
|
|
||||||
this.description = i18next.t("pokemonEvolutions:Forms.timeOfDay");
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(_pokemon: Pokemon): boolean {
|
|
||||||
return this.timesOfDay.indexOf(globalScene.arena.getTimeOfDay()) > -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeActiveTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public active: boolean;
|
|
||||||
|
|
||||||
constructor(active = false) {
|
|
||||||
super();
|
|
||||||
this.active = active;
|
|
||||||
this.description = this.active
|
|
||||||
? i18next.t("pokemonEvolutions:Forms.enter")
|
|
||||||
: i18next.t("pokemonEvolutions:Forms.leave");
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return pokemon.isActive(true) === this.active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public statusEffects: StatusEffect[];
|
|
||||||
public invert: boolean;
|
|
||||||
|
|
||||||
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
|
|
||||||
super();
|
|
||||||
if (!Array.isArray(statusEffects)) {
|
|
||||||
statusEffects = [statusEffects];
|
|
||||||
}
|
|
||||||
this.statusEffects = statusEffects;
|
|
||||||
this.invert = invert;
|
|
||||||
this.description = i18next.t("pokemonEvolutions:Forms.statusEffect");
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return this.statusEffects.indexOf(pokemon.status?.effect || StatusEffect.NONE) > -1 !== this.invert;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public move: MoveId;
|
|
||||||
public known: boolean;
|
|
||||||
|
|
||||||
constructor(move: MoveId, known = true) {
|
|
||||||
super();
|
|
||||||
this.move = move;
|
|
||||||
this.known = known;
|
|
||||||
const moveKey = MoveId[this.move]
|
|
||||||
.split("_")
|
|
||||||
.filter(f => f)
|
|
||||||
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
|
||||||
.join("") as unknown as string;
|
|
||||||
this.description = known
|
|
||||||
? i18next.t("pokemonEvolutions:Forms.moveLearned", {
|
|
||||||
move: i18next.t(`move:${moveKey}.name`),
|
|
||||||
})
|
|
||||||
: i18next.t("pokemonEvolutions:Forms.moveForgotten", {
|
|
||||||
move: i18next.t(`move:${moveKey}.name`),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return !!pokemon.moveset.filter(m => m.moveId === this.move).length === this.known;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class SpeciesFormChangeMoveTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public movePredicate: (m: MoveId) => boolean;
|
|
||||||
public used: boolean;
|
|
||||||
|
|
||||||
constructor(move: MoveId | ((m: MoveId) => boolean), used = true) {
|
|
||||||
super();
|
|
||||||
this.movePredicate = typeof move === "function" ? move : (m: MoveId) => m === move;
|
|
||||||
this.used = used;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger {
|
|
||||||
description = i18next.t("pokemonEvolutions:Forms.preMove");
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
const command = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
|
|
||||||
return !!command?.move && this.movePredicate(command.move.move) === this.used;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangePostMoveTrigger extends SpeciesFormChangeMoveTrigger {
|
|
||||||
description = i18next.t("pokemonEvolutions:Forms.postMove");
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return (
|
|
||||||
pokemon.summonData && !!pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length === this.used
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MeloettaFormChangePostMoveTrigger extends SpeciesFormChangePostMoveTrigger {
|
|
||||||
override canChange(pokemon: Pokemon): boolean {
|
|
||||||
if (globalScene.gameMode.hasChallenge(Challenges.SINGLE_TYPE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Meloetta will not transform if it has the ability Sheer Force when using Relic Song
|
|
||||||
if (pokemon.hasAbility(AbilityId.SHEER_FORCE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return super.canChange(pokemon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
private formKey: string;
|
|
||||||
|
|
||||||
constructor(formKey: string) {
|
|
||||||
super();
|
|
||||||
this.formKey = formKey;
|
|
||||||
this.description = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return (
|
|
||||||
this.formKey ===
|
|
||||||
pokemon.species.forms[globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)]
|
|
||||||
.formKey
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used for triggering form changes based on the user's Tera type.
|
|
||||||
* Used by Ogerpon and Terapagos.
|
|
||||||
* @extends SpeciesFormChangeTrigger
|
|
||||||
*/
|
|
||||||
export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
description = i18next.t("pokemonEvolutions:Forms.tera");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used for triggering form changes based on the user's lapsed Tera type.
|
|
||||||
* Used by Ogerpon and Terapagos.
|
|
||||||
* @extends SpeciesFormChangeTrigger
|
|
||||||
*/
|
|
||||||
export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
description = i18next.t("pokemonEvolutions:Forms.teraLapse");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used for triggering form changes based on weather.
|
|
||||||
* Used by Castform and Cherrim.
|
|
||||||
* @extends SpeciesFormChangeTrigger
|
|
||||||
*/
|
|
||||||
export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
/** The ability that triggers the form change */
|
|
||||||
public ability: AbilityId;
|
|
||||||
/** The list of weathers that trigger the form change */
|
|
||||||
public weathers: WeatherType[];
|
|
||||||
|
|
||||||
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
|
||||||
super();
|
|
||||||
this.ability = ability;
|
|
||||||
this.weathers = weathers;
|
|
||||||
this.description = i18next.t("pokemonEvolutions:Forms.weather");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the Pokemon has the required ability and is in the correct weather while
|
|
||||||
* the weather or ability is also not suppressed.
|
|
||||||
* @param {Pokemon} pokemon the pokemon that is trying to do the form change
|
|
||||||
* @returns `true` if the Pokemon can change forms, `false` otherwise
|
|
||||||
*/
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
|
|
||||||
const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
|
|
||||||
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
|
||||||
|
|
||||||
return (
|
|
||||||
!isAbilitySuppressed &&
|
|
||||||
!isWeatherSuppressed &&
|
|
||||||
pokemon.hasAbility(this.ability) &&
|
|
||||||
this.weathers.includes(currentWeather)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used for reverting to the original form when the weather runs out
|
|
||||||
* or when the user loses the ability/is suppressed.
|
|
||||||
* Used by Castform and Cherrim.
|
|
||||||
* @extends SpeciesFormChangeTrigger
|
|
||||||
*/
|
|
||||||
export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
/** The ability that triggers the form change*/
|
|
||||||
public ability: AbilityId;
|
|
||||||
/** The list of weathers that will also trigger a form change to original form */
|
|
||||||
public weathers: WeatherType[];
|
|
||||||
|
|
||||||
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
|
||||||
super();
|
|
||||||
this.ability = ability;
|
|
||||||
this.weathers = weathers;
|
|
||||||
this.description = i18next.t("pokemonEvolutions:Forms.weatherRevert");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the Pokemon has the required ability and the weather is one that will revert
|
|
||||||
* the Pokemon to its original form or the weather or ability is suppressed
|
|
||||||
* @param {Pokemon} pokemon the pokemon that is trying to do the form change
|
|
||||||
* @returns `true` if the Pokemon will revert to its original form, `false` otherwise
|
|
||||||
*/
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
if (pokemon.hasAbility(this.ability, false, true)) {
|
|
||||||
const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
|
|
||||||
const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
|
|
||||||
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
|
||||||
const summonDataAbility = pokemon.summonData.ability;
|
|
||||||
const isAbilityChanged = summonDataAbility !== this.ability && summonDataAbility !== AbilityId.NONE;
|
|
||||||
|
|
||||||
if (this.weathers.includes(currentWeather) || isWeatherSuppressed || isAbilitySuppressed || isAbilityChanged) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: SpeciesFormChange, preName: string): string {
|
|
||||||
const isMega = formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1;
|
|
||||||
const isGmax = formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1;
|
|
||||||
const isEmax = formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1;
|
|
||||||
const isRevert = !isMega && formChange.formKey === pokemon.species.forms[0].formKey;
|
|
||||||
if (isMega) {
|
|
||||||
return i18next.t("battlePokemonForm:megaChange", {
|
|
||||||
preName,
|
|
||||||
pokemonName: pokemon.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isGmax) {
|
|
||||||
return i18next.t("battlePokemonForm:gigantamaxChange", {
|
|
||||||
preName,
|
|
||||||
pokemonName: pokemon.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isEmax) {
|
|
||||||
return i18next.t("battlePokemonForm:eternamaxChange", {
|
|
||||||
preName,
|
|
||||||
pokemonName: pokemon.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isRevert) {
|
|
||||||
return i18next.t("battlePokemonForm:revertChange", {
|
|
||||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (pokemon.getAbility().id === AbilityId.DISGUISE) {
|
|
||||||
return i18next.t("battlePokemonForm:disguiseChange");
|
|
||||||
}
|
|
||||||
return i18next.t("battlePokemonForm:formChange", { preName });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives a condition for form changing checking if a species is registered as caught in the player's dex data.
|
* Gives a condition for form changing checking if a species is registered as caught in the player's dex data.
|
||||||
* Used for fusion forms such as Kyurem and Necrozma.
|
* Used for fusion forms such as Kyurem and Necrozma.
|
||||||
|
345
src/data/pokemon-forms/form-change-triggers.ts
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
import i18next from "i18next";
|
||||||
|
import { coerceArray, type Constructor } from "#app/utils/common";
|
||||||
|
import type { TimeOfDay } from "#enums/time-of-day";
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||||
|
import type { PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
|
import { WeatherType } from "#enums/weather-type";
|
||||||
|
|
||||||
|
export abstract class SpeciesFormChangeTrigger {
|
||||||
|
public description = "";
|
||||||
|
|
||||||
|
canChange(_pokemon: Pokemon): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTriggerType(triggerType: Constructor<SpeciesFormChangeTrigger>): boolean {
|
||||||
|
return this instanceof triggerType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeManualTrigger extends SpeciesFormChangeTrigger {}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeAbilityTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public description: string = i18next.t("pokemonEvolutions:Forms.ability");
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeCompoundTrigger {
|
||||||
|
public description = "";
|
||||||
|
public triggers: SpeciesFormChangeTrigger[];
|
||||||
|
|
||||||
|
constructor(...triggers: SpeciesFormChangeTrigger[]) {
|
||||||
|
this.triggers = triggers;
|
||||||
|
this.description = this.triggers
|
||||||
|
.filter(trigger => trigger?.description?.length > 0)
|
||||||
|
.map(trigger => trigger.description)
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
for (const trigger of this.triggers) {
|
||||||
|
if (!trigger.canChange(pokemon)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTriggerType(triggerType: Constructor<SpeciesFormChangeTrigger>): boolean {
|
||||||
|
return !!this.triggers.find(t => t.hasTriggerType(triggerType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeItemTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public item: FormChangeItem;
|
||||||
|
public active: boolean;
|
||||||
|
|
||||||
|
constructor(item: FormChangeItem, active = true) {
|
||||||
|
super();
|
||||||
|
this.item = item;
|
||||||
|
this.active = active;
|
||||||
|
this.description = this.active
|
||||||
|
? i18next.t("pokemonEvolutions:Forms.item", {
|
||||||
|
item: i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.item]}`),
|
||||||
|
})
|
||||||
|
: i18next.t("pokemonEvolutions:Forms.deactivateItem", {
|
||||||
|
item: i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.item]}`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return !!globalScene.findModifier(r => {
|
||||||
|
// Assume that if m has the `formChangeItem` property, then it is a PokemonFormChangeItemModifier
|
||||||
|
const m = r as PokemonFormChangeItemModifier;
|
||||||
|
return (
|
||||||
|
"formChangeItem" in m &&
|
||||||
|
m.pokemonId === pokemon.id &&
|
||||||
|
m.formChangeItem === this.item &&
|
||||||
|
m.active === this.active
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeTimeOfDayTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public timesOfDay: TimeOfDay[];
|
||||||
|
|
||||||
|
constructor(...timesOfDay: TimeOfDay[]) {
|
||||||
|
super();
|
||||||
|
this.timesOfDay = timesOfDay;
|
||||||
|
this.description = i18next.t("pokemonEvolutions:Forms.timeOfDay");
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(_pokemon: Pokemon): boolean {
|
||||||
|
return this.timesOfDay.indexOf(globalScene.arena.getTimeOfDay()) > -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class SpeciesFormChangeActiveTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public active: boolean;
|
||||||
|
|
||||||
|
constructor(active = false) {
|
||||||
|
super();
|
||||||
|
this.active = active;
|
||||||
|
this.description = this.active
|
||||||
|
? i18next.t("pokemonEvolutions:Forms.enter")
|
||||||
|
: i18next.t("pokemonEvolutions:Forms.leave");
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return pokemon.isActive(true) === this.active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public statusEffects: StatusEffect[];
|
||||||
|
public invert: boolean;
|
||||||
|
|
||||||
|
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
|
||||||
|
super();
|
||||||
|
this.statusEffects = coerceArray(statusEffects);
|
||||||
|
this.invert = invert;
|
||||||
|
// this.description = i18next.t("pokemonEvolutions:Forms.statusEffect");
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return this.statusEffects.indexOf(pokemon.status?.effect || StatusEffect.NONE) > -1 !== this.invert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public move: MoveId;
|
||||||
|
public known: boolean;
|
||||||
|
|
||||||
|
constructor(move: MoveId, known = true) {
|
||||||
|
super();
|
||||||
|
this.move = move;
|
||||||
|
this.known = known;
|
||||||
|
const moveKey = MoveId[this.move]
|
||||||
|
.split("_")
|
||||||
|
.filter(f => f)
|
||||||
|
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
||||||
|
.join("") as unknown as string;
|
||||||
|
this.description = known
|
||||||
|
? i18next.t("pokemonEvolutions:Forms.moveLearned", {
|
||||||
|
move: i18next.t(`move:${moveKey}.name`),
|
||||||
|
})
|
||||||
|
: i18next.t("pokemonEvolutions:Forms.moveForgotten", {
|
||||||
|
move: i18next.t(`move:${moveKey}.name`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return !!pokemon.moveset.filter(m => m.moveId === this.move).length === this.known;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class SpeciesFormChangeMoveTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public movePredicate: (m: MoveId) => boolean;
|
||||||
|
public used: boolean;
|
||||||
|
|
||||||
|
constructor(move: MoveId | ((m: MoveId) => boolean), used = true) {
|
||||||
|
super();
|
||||||
|
this.movePredicate = typeof move === "function" ? move : (m: MoveId) => m === move;
|
||||||
|
this.used = used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger {
|
||||||
|
description = i18next.t("pokemonEvolutions:Forms.preMove");
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
const command = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
|
||||||
|
return !!command?.move && this.movePredicate(command.move.move) === this.used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangePostMoveTrigger extends SpeciesFormChangeMoveTrigger {
|
||||||
|
description = i18next.t("pokemonEvolutions:Forms.postMove");
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return (
|
||||||
|
pokemon.summonData && !!pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length === this.used
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MeloettaFormChangePostMoveTrigger extends SpeciesFormChangePostMoveTrigger {
|
||||||
|
override canChange(pokemon: Pokemon): boolean {
|
||||||
|
if (globalScene.gameMode.hasChallenge(Challenges.SINGLE_TYPE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Meloetta will not transform if it has the ability Sheer Force when using Relic Song
|
||||||
|
if (pokemon.hasAbility(AbilityId.SHEER_FORCE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.canChange(pokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
private formKey: string;
|
||||||
|
|
||||||
|
constructor(formKey: string) {
|
||||||
|
super();
|
||||||
|
this.formKey = formKey;
|
||||||
|
this.description = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return (
|
||||||
|
this.formKey ===
|
||||||
|
pokemon.species.forms[globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)]
|
||||||
|
.formKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for triggering form changes based on the user's Tera type.
|
||||||
|
* Used by Ogerpon and Terapagos.
|
||||||
|
*/
|
||||||
|
export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for triggering form changes based on the user's lapsed Tera type.
|
||||||
|
* Used by Ogerpon and Terapagos.
|
||||||
|
*/
|
||||||
|
export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for triggering form changes based on weather.
|
||||||
|
* Used by Castform and Cherrim.
|
||||||
|
*/
|
||||||
|
export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
/** The ability that triggers the form change */
|
||||||
|
public ability: AbilityId;
|
||||||
|
/** The list of weathers that trigger the form change */
|
||||||
|
public weathers: WeatherType[];
|
||||||
|
|
||||||
|
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
||||||
|
super();
|
||||||
|
this.ability = ability;
|
||||||
|
this.weathers = weathers;
|
||||||
|
this.description = i18next.t("pokemonEvolutions:Forms.weather");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the Pokemon has the required ability and is in the correct weather while
|
||||||
|
* the weather or ability is also not suppressed.
|
||||||
|
* @param pokemon - The pokemon that is trying to do the form change
|
||||||
|
* @returns `true` if the Pokemon can change forms, `false` otherwise
|
||||||
|
*/
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
|
||||||
|
const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
|
||||||
|
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
||||||
|
|
||||||
|
return (
|
||||||
|
!isAbilitySuppressed &&
|
||||||
|
!isWeatherSuppressed &&
|
||||||
|
pokemon.hasAbility(this.ability) &&
|
||||||
|
this.weathers.includes(currentWeather)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for reverting to the original form when the weather runs out
|
||||||
|
* or when the user loses the ability/is suppressed.
|
||||||
|
* Used by Castform and Cherrim.
|
||||||
|
*/
|
||||||
|
export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
/** The ability that triggers the form change*/
|
||||||
|
public ability: AbilityId;
|
||||||
|
/** The list of weathers that will also trigger a form change to original form */
|
||||||
|
public weathers: WeatherType[];
|
||||||
|
|
||||||
|
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
||||||
|
super();
|
||||||
|
this.ability = ability;
|
||||||
|
this.weathers = weathers;
|
||||||
|
this.description = i18next.t("pokemonEvolutions:Forms.weatherRevert");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the Pokemon has the required ability and the weather is one that will revert
|
||||||
|
* the Pokemon to its original form or the weather or ability is suppressed
|
||||||
|
* @param {Pokemon} pokemon the pokemon that is trying to do the form change
|
||||||
|
* @returns `true` if the Pokemon will revert to its original form, `false` otherwise
|
||||||
|
*/
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
if (pokemon.hasAbility(this.ability, false, true)) {
|
||||||
|
const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
|
||||||
|
const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
|
||||||
|
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
||||||
|
const summonDataAbility = pokemon.summonData.ability;
|
||||||
|
const isAbilityChanged = summonDataAbility !== this.ability && summonDataAbility !== AbilityId.NONE;
|
||||||
|
|
||||||
|
if (this.weathers.includes(currentWeather) || isWeatherSuppressed || isAbilitySuppressed || isAbilityChanged) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: SpeciesFormChange, preName: string): string {
|
||||||
|
const isMega = formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1;
|
||||||
|
const isGmax = formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1;
|
||||||
|
const isEmax = formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1;
|
||||||
|
const isRevert = !isMega && formChange.formKey === pokemon.species.forms[0].formKey;
|
||||||
|
if (isMega) {
|
||||||
|
return i18next.t("battlePokemonForm:megaChange", {
|
||||||
|
preName,
|
||||||
|
pokemonName: pokemon.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isGmax) {
|
||||||
|
return i18next.t("battlePokemonForm:gigantamaxChange", {
|
||||||
|
preName,
|
||||||
|
pokemonName: pokemon.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isEmax) {
|
||||||
|
return i18next.t("battlePokemonForm:eternamaxChange", {
|
||||||
|
preName,
|
||||||
|
pokemonName: pokemon.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isRevert) {
|
||||||
|
return i18next.t("battlePokemonForm:revertChange", {
|
||||||
|
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (pokemon.getAbility().id === AbilityId.DISGUISE) {
|
||||||
|
return i18next.t("battlePokemonForm:disguiseChange");
|
||||||
|
}
|
||||||
|
return i18next.t("battlePokemonForm:formChange", { preName });
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import type { Localizable } from "#app/interfaces/locales";
|
import type { Localizable } from "#app/@types/locales";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-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";
|
||||||
@ -7,8 +7,16 @@ import i18next from "i18next";
|
|||||||
import type { AnySound } from "#app/battle-scene";
|
import type { AnySound } from "#app/battle-scene";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { GameMode } from "#app/game-mode";
|
import type { GameMode } from "#app/game-mode";
|
||||||
import { DexAttr, type StarterMoveset } from "#app/system/game-data";
|
import type { StarterMoveset } from "#app/system/game-data";
|
||||||
import { isNullOrUndefined, capitalizeString, randSeedInt, randSeedGauss, randSeedItem } from "#app/utils/common";
|
import { DexAttr } from "#enums/dex-attr";
|
||||||
|
import {
|
||||||
|
isNullOrUndefined,
|
||||||
|
capitalizeString,
|
||||||
|
randSeedInt,
|
||||||
|
randSeedGauss,
|
||||||
|
randSeedItem,
|
||||||
|
randSeedFloat,
|
||||||
|
} from "#app/utils/common";
|
||||||
import { uncatchableSpecies } from "#app/data/balance/biomes";
|
import { uncatchableSpecies } from "#app/data/balance/biomes";
|
||||||
import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
||||||
import { GrowthRate } from "#app/data/exp";
|
import { GrowthRate } from "#app/data/exp";
|
||||||
@ -34,6 +42,8 @@ import { starterPassiveAbilities } from "#app/data/balance/passives";
|
|||||||
import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite";
|
import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite";
|
||||||
import { hasExpSprite } from "#app/sprites/sprite-utils";
|
import { hasExpSprite } from "#app/sprites/sprite-utils";
|
||||||
import { Gender } from "./gender";
|
import { Gender } from "./gender";
|
||||||
|
import { allSpecies } from "#app/data/data-lists";
|
||||||
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
||||||
|
|
||||||
export enum Region {
|
export enum Region {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
@ -74,23 +84,6 @@ export const normalForm: SpeciesId[] = [
|
|||||||
SpeciesId.CALYREX,
|
SpeciesId.CALYREX,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the {@linkcode PokemonSpecies} object associated with the {@linkcode SpeciesId} enum given
|
|
||||||
* @param species The species to fetch
|
|
||||||
* @returns The associated {@linkcode PokemonSpecies} object
|
|
||||||
*/
|
|
||||||
export function getPokemonSpecies(species: SpeciesId | SpeciesId[]): PokemonSpecies {
|
|
||||||
// If a special pool (named trainers) is used here it CAN happen that they have a array as species (which means choose one of those two). So we catch that with this code block
|
|
||||||
if (Array.isArray(species)) {
|
|
||||||
// Pick a random species from the list
|
|
||||||
species = species[Math.floor(Math.random() * species.length)];
|
|
||||||
}
|
|
||||||
if (species >= 2000) {
|
|
||||||
return allSpecies.find(s => s.speciesId === species)!; // TODO: is this bang correct?
|
|
||||||
}
|
|
||||||
return allSpecies[species - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPokemonSpeciesForm(species: SpeciesId, formIndex: number): PokemonSpeciesForm {
|
export function getPokemonSpeciesForm(species: SpeciesId, formIndex: number): PokemonSpeciesForm {
|
||||||
const retSpecies: PokemonSpecies =
|
const retSpecies: PokemonSpecies =
|
||||||
species >= 2000
|
species >= 2000
|
||||||
@ -750,7 +743,7 @@ export abstract class PokemonSpeciesForm {
|
|||||||
let paletteColors: Map<number, number> = new Map();
|
let paletteColors: Map<number, number> = new Map();
|
||||||
|
|
||||||
const originalRandom = Math.random;
|
const originalRandom = Math.random;
|
||||||
Math.random = Phaser.Math.RND.frac;
|
Math.random = randSeedFloat;
|
||||||
|
|
||||||
globalScene.executeWithSeedOffset(
|
globalScene.executeWithSeedOffset(
|
||||||
() => {
|
() => {
|
||||||
@ -773,6 +766,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
readonly mythical: boolean;
|
readonly mythical: boolean;
|
||||||
readonly species: string;
|
readonly species: string;
|
||||||
readonly growthRate: GrowthRate;
|
readonly growthRate: GrowthRate;
|
||||||
|
/** The chance (as a decimal) for this Species to be male, or `null` for genderless species */
|
||||||
readonly malePercent: number | null;
|
readonly malePercent: number | null;
|
||||||
readonly genderDiffs: boolean;
|
readonly genderDiffs: boolean;
|
||||||
readonly canChangeForm: boolean;
|
readonly canChangeForm: boolean;
|
||||||
@ -889,7 +883,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
return Gender.GENDERLESS;
|
return Gender.GENDERLESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Phaser.Math.RND.realInRange(0, 1) <= this.malePercent) {
|
if (randSeedFloat() <= this.malePercent) {
|
||||||
return Gender.MALE;
|
return Gender.MALE;
|
||||||
}
|
}
|
||||||
return Gender.FEMALE;
|
return Gender.FEMALE;
|
||||||
@ -1138,7 +1132,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noEvolutionChance === 1 || Phaser.Math.RND.realInRange(0, 1) < noEvolutionChance) {
|
if (noEvolutionChance === 1 || randSeedFloat() <= noEvolutionChance) {
|
||||||
return this.speciesId;
|
return this.speciesId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1281,10 +1275,6 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
isObtainable() {
|
|
||||||
return super.isObtainable();
|
|
||||||
}
|
|
||||||
|
|
||||||
hasVariants() {
|
hasVariants() {
|
||||||
let variantDataIndex: string | number = this.speciesId;
|
let variantDataIndex: string | number = this.speciesId;
|
||||||
if (this.forms.length > 0) {
|
if (this.forms.length > 0) {
|
||||||
@ -1443,8 +1433,6 @@ export function getPokerusStarters(): PokemonSpecies[] {
|
|||||||
return pokerusStarters;
|
return pokerusStarters;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const allSpecies: PokemonSpecies[] = [];
|
|
||||||
|
|
||||||
// biome-ignore format: manually formatted
|
// biome-ignore format: manually formatted
|
||||||
export function initSpecies() {
|
export function initSpecies() {
|
||||||
allSpecies.push(
|
allSpecies.push(
|
||||||
@ -2026,9 +2014,9 @@ export function initSpecies() {
|
|||||||
new PokemonForm("Normal", "", PokemonType.GROUND, null, 3.5, 950, AbilityId.DROUGHT, AbilityId.NONE, AbilityId.NONE, 670, 100, 150, 140, 100, 90, 90, 3, 0, 335, false, null, true),
|
new PokemonForm("Normal", "", PokemonType.GROUND, null, 3.5, 950, AbilityId.DROUGHT, AbilityId.NONE, AbilityId.NONE, 670, 100, 150, 140, 100, 90, 90, 3, 0, 335, false, null, true),
|
||||||
new PokemonForm("Primal", "primal", PokemonType.GROUND, PokemonType.FIRE, 5, 999.7, AbilityId.DESOLATE_LAND, AbilityId.NONE, AbilityId.NONE, 770, 100, 180, 160, 150, 90, 90, 3, 0, 335),
|
new PokemonForm("Primal", "primal", PokemonType.GROUND, PokemonType.FIRE, 5, 999.7, AbilityId.DESOLATE_LAND, AbilityId.NONE, AbilityId.NONE, 770, 100, 180, 160, 150, 90, 90, 3, 0, 335),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(SpeciesId.RAYQUAZA, 3, false, true, false, "Sky High Pokémon", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 45, 0, 340, GrowthRate.SLOW, null, false, true,
|
new PokemonSpecies(SpeciesId.RAYQUAZA, 3, false, true, false, "Sky High Pokémon", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 3, 0, 340, GrowthRate.SLOW, null, false, true,
|
||||||
new PokemonForm("Normal", "", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 45, 0, 340, false, null, true),
|
new PokemonForm("Normal", "", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 3, 0, 340, false, null, true),
|
||||||
new PokemonForm("Mega", SpeciesFormKey.MEGA, PokemonType.DRAGON, PokemonType.FLYING, 10.8, 392, AbilityId.DELTA_STREAM, AbilityId.NONE, AbilityId.NONE, 780, 105, 180, 100, 180, 100, 115, 45, 0, 340),
|
new PokemonForm("Mega", SpeciesFormKey.MEGA, PokemonType.DRAGON, PokemonType.FLYING, 10.8, 392, AbilityId.DELTA_STREAM, AbilityId.NONE, AbilityId.NONE, 780, 105, 180, 100, 180, 100, 115, 3, 0, 340),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(SpeciesId.JIRACHI, 3, false, false, true, "Wish Pokémon", PokemonType.STEEL, PokemonType.PSYCHIC, 0.3, 1.1, AbilityId.SERENE_GRACE, AbilityId.NONE, AbilityId.NONE, 600, 100, 100, 100, 100, 100, 100, 3, 100, 300, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.JIRACHI, 3, false, false, true, "Wish Pokémon", PokemonType.STEEL, PokemonType.PSYCHIC, 0.3, 1.1, AbilityId.SERENE_GRACE, AbilityId.NONE, AbilityId.NONE, 600, 100, 100, 100, 100, 100, 100, 3, 100, 300, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.DEOXYS, 3, false, false, true, "DNA Pokémon", PokemonType.PSYCHIC, null, 1.7, 60.8, AbilityId.PRESSURE, AbilityId.NONE, AbilityId.NONE, 600, 50, 150, 50, 150, 50, 150, 3, 0, 300, GrowthRate.SLOW, null, false, true,
|
new PokemonSpecies(SpeciesId.DEOXYS, 3, false, false, true, "DNA Pokémon", PokemonType.PSYCHIC, null, 1.7, 60.8, AbilityId.PRESSURE, AbilityId.NONE, AbilityId.NONE, 600, 50, 150, 50, 150, 50, 150, 3, 0, 300, GrowthRate.SLOW, null, false, true,
|
||||||
@ -2274,10 +2262,10 @@ export function initSpecies() {
|
|||||||
new PokemonSpecies(SpeciesId.WHIMSICOTT, 5, false, false, false, "Windveiled Pokémon", PokemonType.GRASS, PokemonType.FAIRY, 0.7, 6.6, AbilityId.PRANKSTER, AbilityId.INFILTRATOR, AbilityId.CHLOROPHYLL, 480, 60, 67, 85, 77, 75, 116, 75, 50, 168, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(SpeciesId.WHIMSICOTT, 5, false, false, false, "Windveiled Pokémon", PokemonType.GRASS, PokemonType.FAIRY, 0.7, 6.6, AbilityId.PRANKSTER, AbilityId.INFILTRATOR, AbilityId.CHLOROPHYLL, 480, 60, 67, 85, 77, 75, 116, 75, 50, 168, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(SpeciesId.PETILIL, 5, false, false, false, "Bulb Pokémon", PokemonType.GRASS, null, 0.5, 6.6, AbilityId.CHLOROPHYLL, AbilityId.OWN_TEMPO, AbilityId.LEAF_GUARD, 280, 45, 35, 50, 70, 50, 30, 190, 50, 56, GrowthRate.MEDIUM_FAST, 0, false),
|
new PokemonSpecies(SpeciesId.PETILIL, 5, false, false, false, "Bulb Pokémon", PokemonType.GRASS, null, 0.5, 6.6, AbilityId.CHLOROPHYLL, AbilityId.OWN_TEMPO, AbilityId.LEAF_GUARD, 280, 45, 35, 50, 70, 50, 30, 190, 50, 56, GrowthRate.MEDIUM_FAST, 0, false),
|
||||||
new PokemonSpecies(SpeciesId.LILLIGANT, 5, false, false, false, "Flowering Pokémon", PokemonType.GRASS, null, 1.1, 16.3, AbilityId.CHLOROPHYLL, AbilityId.OWN_TEMPO, AbilityId.LEAF_GUARD, 480, 70, 60, 75, 110, 75, 90, 75, 50, 168, GrowthRate.MEDIUM_FAST, 0, false),
|
new PokemonSpecies(SpeciesId.LILLIGANT, 5, false, false, false, "Flowering Pokémon", PokemonType.GRASS, null, 1.1, 16.3, AbilityId.CHLOROPHYLL, AbilityId.OWN_TEMPO, AbilityId.LEAF_GUARD, 480, 70, 60, 75, 110, 75, 90, 75, 50, 168, GrowthRate.MEDIUM_FAST, 0, false),
|
||||||
new PokemonSpecies(SpeciesId.BASCULIN, 5, false, false, false, "Hostile Pokémon", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, GrowthRate.MEDIUM_FAST, 50, false, false,
|
new PokemonSpecies(SpeciesId.BASCULIN, 5, false, false, false, "Hostile Pokémon", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, GrowthRate.MEDIUM_FAST, 50, false, false,
|
||||||
new PokemonForm("Red-Striped Form", "red-striped", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true),
|
new PokemonForm("Red-Striped Form", "red-striped", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true),
|
||||||
new PokemonForm("Blue-Striped Form", "blue-striped", PokemonType.WATER, null, 1, 18, AbilityId.ROCK_HEAD, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true),
|
new PokemonForm("Blue-Striped Form", "blue-striped", PokemonType.WATER, null, 1, 18, AbilityId.ROCK_HEAD, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true),
|
||||||
new PokemonForm("White-Striped Form", "white-striped", PokemonType.WATER, null, 1, 18, AbilityId.RATTLED, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true),
|
new PokemonForm("White-Striped Form", "white-striped", PokemonType.WATER, null, 1, 18, AbilityId.RATTLED, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(SpeciesId.SANDILE, 5, false, false, false, "Desert Croc Pokémon", PokemonType.GROUND, PokemonType.DARK, 0.7, 15.2, AbilityId.INTIMIDATE, AbilityId.MOXIE, AbilityId.ANGER_POINT, 292, 50, 72, 35, 35, 35, 65, 180, 50, 58, GrowthRate.MEDIUM_SLOW, 50, false),
|
new PokemonSpecies(SpeciesId.SANDILE, 5, false, false, false, "Desert Croc Pokémon", PokemonType.GROUND, PokemonType.DARK, 0.7, 15.2, AbilityId.INTIMIDATE, AbilityId.MOXIE, AbilityId.ANGER_POINT, 292, 50, 72, 35, 35, 35, 65, 180, 50, 58, GrowthRate.MEDIUM_SLOW, 50, false),
|
||||||
new PokemonSpecies(SpeciesId.KROKOROK, 5, false, false, false, "Desert Croc Pokémon", PokemonType.GROUND, PokemonType.DARK, 1, 33.4, AbilityId.INTIMIDATE, AbilityId.MOXIE, AbilityId.ANGER_POINT, 351, 60, 82, 45, 45, 45, 74, 90, 50, 123, GrowthRate.MEDIUM_SLOW, 50, false),
|
new PokemonSpecies(SpeciesId.KROKOROK, 5, false, false, false, "Desert Croc Pokémon", PokemonType.GROUND, PokemonType.DARK, 1, 33.4, AbilityId.INTIMIDATE, AbilityId.MOXIE, AbilityId.ANGER_POINT, 351, 60, 82, 45, 45, 45, 74, 90, 50, 123, GrowthRate.MEDIUM_SLOW, 50, false),
|
||||||
@ -2744,10 +2732,10 @@ export function initSpecies() {
|
|||||||
new PokemonSpecies(SpeciesId.TAPU_LELE, 7, true, false, false, "Land Spirit Pokémon", PokemonType.PSYCHIC, PokemonType.FAIRY, 1.2, 18.6, AbilityId.PSYCHIC_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 85, 75, 130, 115, 95, 3, 50, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.TAPU_LELE, 7, true, false, false, "Land Spirit Pokémon", PokemonType.PSYCHIC, PokemonType.FAIRY, 1.2, 18.6, AbilityId.PSYCHIC_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 85, 75, 130, 115, 95, 3, 50, 285, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.TAPU_BULU, 7, true, false, false, "Land Spirit Pokémon", PokemonType.GRASS, PokemonType.FAIRY, 1.9, 45.5, AbilityId.GRASSY_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 130, 115, 85, 95, 75, 3, 50, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.TAPU_BULU, 7, true, false, false, "Land Spirit Pokémon", PokemonType.GRASS, PokemonType.FAIRY, 1.9, 45.5, AbilityId.GRASSY_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 130, 115, 85, 95, 75, 3, 50, 285, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.TAPU_FINI, 7, true, false, false, "Land Spirit Pokémon", PokemonType.WATER, PokemonType.FAIRY, 1.3, 21.2, AbilityId.MISTY_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 75, 115, 95, 130, 85, 3, 50, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.TAPU_FINI, 7, true, false, false, "Land Spirit Pokémon", PokemonType.WATER, PokemonType.FAIRY, 1.3, 21.2, AbilityId.MISTY_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 75, 115, 95, 130, 85, 3, 50, 285, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.COSMOG, 7, true, false, false, "Nebula Pokémon", PokemonType.PSYCHIC, null, 0.2, 0.1, AbilityId.UNAWARE, AbilityId.NONE, AbilityId.NONE, 200, 43, 29, 31, 29, 31, 37, 45, 0, 40, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.COSMOG, 7, true, false, false, "Nebula Pokémon", PokemonType.PSYCHIC, null, 0.2, 0.1, AbilityId.UNAWARE, AbilityId.NONE, AbilityId.NONE, 200, 43, 29, 31, 29, 31, 37, 3, 0, 40, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.COSMOEM, 7, true, false, false, "Protostar Pokémon", PokemonType.PSYCHIC, null, 0.1, 999.9, AbilityId.STURDY, AbilityId.NONE, AbilityId.NONE, 400, 43, 29, 131, 29, 131, 37, 45, 0, 140, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.COSMOEM, 7, true, false, false, "Protostar Pokémon", PokemonType.PSYCHIC, null, 0.1, 999.9, AbilityId.STURDY, AbilityId.NONE, AbilityId.NONE, 400, 43, 29, 131, 29, 131, 37, 3, 0, 140, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.SOLGALEO, 7, false, true, false, "Sunne Pokémon", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, AbilityId.FULL_METAL_BODY, AbilityId.NONE, AbilityId.NONE, 680, 137, 137, 107, 113, 89, 97, 45, 0, 340, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.SOLGALEO, 7, false, true, false, "Sunne Pokémon", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, AbilityId.FULL_METAL_BODY, AbilityId.NONE, AbilityId.NONE, 680, 137, 137, 107, 113, 89, 97, 3, 0, 340, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.LUNALA, 7, false, true, false, "Moone Pokémon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, AbilityId.SHADOW_SHIELD, AbilityId.NONE, AbilityId.NONE, 680, 137, 113, 89, 137, 107, 97, 45, 0, 340, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.LUNALA, 7, false, true, false, "Moone Pokémon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, AbilityId.SHADOW_SHIELD, AbilityId.NONE, AbilityId.NONE, 680, 137, 113, 89, 137, 107, 97, 3, 0, 340, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.NIHILEGO, 7, true, false, false, "Parasite Pokémon", PokemonType.ROCK, PokemonType.POISON, 1.2, 55.5, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 109, 53, 47, 127, 131, 103, 45, 0, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.NIHILEGO, 7, true, false, false, "Parasite Pokémon", PokemonType.ROCK, PokemonType.POISON, 1.2, 55.5, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 109, 53, 47, 127, 131, 103, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.BUZZWOLE, 7, true, false, false, "Swollen Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 2.4, 333.6, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 107, 139, 139, 53, 53, 79, 45, 0, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.BUZZWOLE, 7, true, false, false, "Swollen Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 2.4, 333.6, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 107, 139, 139, 53, 53, 79, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.PHEROMOSA, 7, true, false, false, "Lissome Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 1.8, 25, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 71, 137, 37, 137, 37, 151, 45, 0, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.PHEROMOSA, 7, true, false, false, "Lissome Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 1.8, 25, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 71, 137, 37, 137, 37, 151, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||||
@ -2755,11 +2743,11 @@ export function initSpecies() {
|
|||||||
new PokemonSpecies(SpeciesId.CELESTEELA, 7, true, false, false, "Launch Pokémon", PokemonType.STEEL, PokemonType.FLYING, 9.2, 999.9, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 97, 101, 103, 107, 101, 61, 45, 0, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.CELESTEELA, 7, true, false, false, "Launch Pokémon", PokemonType.STEEL, PokemonType.FLYING, 9.2, 999.9, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 97, 101, 103, 107, 101, 61, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.KARTANA, 7, true, false, false, "Drawn Sword Pokémon", PokemonType.GRASS, PokemonType.STEEL, 0.3, 0.1, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 59, 181, 131, 59, 31, 109, 45, 0, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.KARTANA, 7, true, false, false, "Drawn Sword Pokémon", PokemonType.GRASS, PokemonType.STEEL, 0.3, 0.1, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 59, 181, 131, 59, 31, 109, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.GUZZLORD, 7, true, false, false, "Junkivore Pokémon", PokemonType.DARK, PokemonType.DRAGON, 5.5, 888, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 223, 101, 53, 97, 53, 43, 45, 0, 285, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(SpeciesId.GUZZLORD, 7, true, false, false, "Junkivore Pokémon", PokemonType.DARK, PokemonType.DRAGON, 5.5, 888, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 223, 101, 53, 97, 53, 43, 45, 0, 285, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(SpeciesId.NECROZMA, 7, false, true, false, "Prism Pokémon", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 255, 0, 300, GrowthRate.SLOW, null, false, false,
|
new PokemonSpecies(SpeciesId.NECROZMA, 7, false, true, false, "Prism Pokémon", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 3, 0, 300, GrowthRate.SLOW, null, false, false,
|
||||||
new PokemonForm("Normal", "", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 255, 0, 300, false, null, true),
|
new PokemonForm("Normal", "", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 3, 0, 300, false, null, true),
|
||||||
new PokemonForm("Dusk Mane", "dusk-mane", PokemonType.PSYCHIC, PokemonType.STEEL, 3.8, 460, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 157, 127, 113, 109, 77, 255, 0, 340),
|
new PokemonForm("Dusk Mane", "dusk-mane", PokemonType.PSYCHIC, PokemonType.STEEL, 3.8, 460, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 157, 127, 113, 109, 77, 3, 0, 340),
|
||||||
new PokemonForm("Dawn Wings", "dawn-wings", PokemonType.PSYCHIC, PokemonType.GHOST, 4.2, 350, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 113, 109, 157, 127, 77, 255, 0, 340),
|
new PokemonForm("Dawn Wings", "dawn-wings", PokemonType.PSYCHIC, PokemonType.GHOST, 4.2, 350, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 113, 109, 157, 127, 77, 3, 0, 340),
|
||||||
new PokemonForm("Ultra", "ultra", PokemonType.PSYCHIC, PokemonType.DRAGON, 7.5, 230, AbilityId.NEUROFORCE, AbilityId.NONE, AbilityId.NONE, 754, 97, 167, 97, 167, 97, 129, 255, 0, 377),
|
new PokemonForm("Ultra", "ultra", PokemonType.PSYCHIC, PokemonType.DRAGON, 7.5, 230, AbilityId.NEUROFORCE, AbilityId.NONE, AbilityId.NONE, 754, 97, 167, 97, 167, 97, 129, 3, 0, 377),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(SpeciesId.MAGEARNA, 7, false, false, true, "Artificial Pokémon", PokemonType.STEEL, PokemonType.FAIRY, 1, 80.5, AbilityId.SOUL_HEART, AbilityId.NONE, AbilityId.NONE, 600, 80, 95, 115, 130, 115, 65, 3, 0, 300, GrowthRate.SLOW, null, false, false,
|
new PokemonSpecies(SpeciesId.MAGEARNA, 7, false, false, true, "Artificial Pokémon", PokemonType.STEEL, PokemonType.FAIRY, 1, 80.5, AbilityId.SOUL_HEART, AbilityId.NONE, AbilityId.NONE, 600, 80, 95, 115, 130, 115, 65, 3, 0, 300, GrowthRate.SLOW, null, false, false,
|
||||||
new PokemonForm("Normal", "", PokemonType.STEEL, PokemonType.FAIRY, 1, 80.5, AbilityId.SOUL_HEART, AbilityId.NONE, AbilityId.NONE, 600, 80, 95, 115, 130, 115, 65, 3, 0, 300, false, null, true),
|
new PokemonForm("Normal", "", PokemonType.STEEL, PokemonType.FAIRY, 1, 80.5, AbilityId.SOUL_HEART, AbilityId.NONE, AbilityId.NONE, 600, 80, 95, 115, 130, 115, 65, 3, 0, 300, false, null, true),
|
||||||
@ -2968,15 +2956,15 @@ export function initSpecies() {
|
|||||||
new PokemonForm("Ice", "ice", PokemonType.PSYCHIC, PokemonType.ICE, 2.4, 809.1, AbilityId.AS_ONE_GLASTRIER, AbilityId.NONE, AbilityId.NONE, 680, 100, 165, 150, 85, 130, 50, 3, 100, 340),
|
new PokemonForm("Ice", "ice", PokemonType.PSYCHIC, PokemonType.ICE, 2.4, 809.1, AbilityId.AS_ONE_GLASTRIER, AbilityId.NONE, AbilityId.NONE, 680, 100, 165, 150, 85, 130, 50, 3, 100, 340),
|
||||||
new PokemonForm("Shadow", "shadow", PokemonType.PSYCHIC, PokemonType.GHOST, 2.4, 53.6, AbilityId.AS_ONE_SPECTRIER, AbilityId.NONE, AbilityId.NONE, 680, 100, 85, 80, 165, 100, 150, 3, 100, 340),
|
new PokemonForm("Shadow", "shadow", PokemonType.PSYCHIC, PokemonType.GHOST, 2.4, 53.6, AbilityId.AS_ONE_SPECTRIER, AbilityId.NONE, AbilityId.NONE, 680, 100, 85, 80, 165, 100, 150, 3, 100, 340),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(SpeciesId.WYRDEER, 8, false, false, false, "Big Horn Pokémon", PokemonType.NORMAL, PokemonType.PSYCHIC, 1.8, 95.1, AbilityId.INTIMIDATE, AbilityId.FRISK, AbilityId.SAP_SIPPER, 525, 103, 105, 72, 105, 75, 65, 135, 50, 263, GrowthRate.SLOW, 50, false),
|
new PokemonSpecies(SpeciesId.WYRDEER, 8, false, false, false, "Big Horn Pokémon", PokemonType.NORMAL, PokemonType.PSYCHIC, 1.8, 95.1, AbilityId.INTIMIDATE, AbilityId.FRISK, AbilityId.SAP_SIPPER, 525, 103, 105, 72, 105, 75, 65, 45, 50, 263, GrowthRate.SLOW, 50, false),
|
||||||
new PokemonSpecies(SpeciesId.KLEAVOR, 8, false, false, false, "Axe Pokémon", PokemonType.BUG, PokemonType.ROCK, 1.8, 89, AbilityId.SWARM, AbilityId.SHEER_FORCE, AbilityId.SHARPNESS, 500, 70, 135, 95, 45, 70, 85, 115, 50, 175, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(SpeciesId.KLEAVOR, 8, false, false, false, "Axe Pokémon", PokemonType.BUG, PokemonType.ROCK, 1.8, 89, AbilityId.SWARM, AbilityId.SHEER_FORCE, AbilityId.SHARPNESS, 500, 70, 135, 95, 45, 70, 85, 15, 50, 175, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(SpeciesId.URSALUNA, 8, false, false, false, "Peat Pokémon", PokemonType.GROUND, PokemonType.NORMAL, 2.4, 290, AbilityId.GUTS, AbilityId.BULLETPROOF, AbilityId.UNNERVE, 550, 130, 140, 105, 45, 80, 50, 75, 50, 275, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(SpeciesId.URSALUNA, 8, false, false, false, "Peat Pokémon", PokemonType.GROUND, PokemonType.NORMAL, 2.4, 290, AbilityId.GUTS, AbilityId.BULLETPROOF, AbilityId.UNNERVE, 550, 130, 140, 105, 45, 80, 50, 20, 50, 275, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(SpeciesId.BASCULEGION, 8, false, false, false, "Big Fish Pokémon", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 135, 50, 265, GrowthRate.MEDIUM_FAST, 50, false, false,
|
new PokemonSpecies(SpeciesId.BASCULEGION, 8, false, false, false, "Big Fish Pokémon", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 45, 50, 265, GrowthRate.MEDIUM_FAST, 50, false, false,
|
||||||
new PokemonForm("Male", "male", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 135, 50, 265, false, "", true),
|
new PokemonForm("Male", "male", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 45, 50, 265, false, "", true),
|
||||||
new PokemonForm("Female", "female", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 92, 65, 100, 75, 78, 135, 50, 265, false, null, true),
|
new PokemonForm("Female", "female", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 92, 65, 100, 75, 78, 45, 50, 265, false, null, true),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(SpeciesId.SNEASLER, 8, false, false, false, "Free Climb Pokémon", PokemonType.FIGHTING, PokemonType.POISON, 1.3, 43, AbilityId.PRESSURE, AbilityId.UNBURDEN, AbilityId.POISON_TOUCH, 510, 80, 130, 60, 40, 80, 120, 135, 50, 102, GrowthRate.MEDIUM_SLOW, 50, false),
|
new PokemonSpecies(SpeciesId.SNEASLER, 8, false, false, false, "Free Climb Pokémon", PokemonType.FIGHTING, PokemonType.POISON, 1.3, 43, AbilityId.PRESSURE, AbilityId.UNBURDEN, AbilityId.POISON_TOUCH, 510, 80, 130, 60, 40, 80, 120, 20, 50, 102, GrowthRate.MEDIUM_SLOW, 50, false),
|
||||||
new PokemonSpecies(SpeciesId.OVERQWIL, 8, false, false, false, "Pin Cluster Pokémon", PokemonType.DARK, PokemonType.POISON, 2.5, 60.5, AbilityId.POISON_POINT, AbilityId.SWIFT_SWIM, AbilityId.INTIMIDATE, 510, 85, 115, 95, 65, 65, 85, 135, 50, 179, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(SpeciesId.OVERQWIL, 8, false, false, false, "Pin Cluster Pokémon", PokemonType.DARK, PokemonType.POISON, 2.5, 60.5, AbilityId.POISON_POINT, AbilityId.SWIFT_SWIM, AbilityId.INTIMIDATE, 510, 85, 115, 95, 65, 65, 85, 45, 50, 179, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(SpeciesId.ENAMORUS, 8, true, false, false, "Love-Hate Pokémon", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.CUTE_CHARM, AbilityId.NONE, AbilityId.CONTRARY, 580, 74, 115, 70, 135, 80, 106, 3, 50, 116, GrowthRate.SLOW, 0, false, true,
|
new PokemonSpecies(SpeciesId.ENAMORUS, 8, true, false, false, "Love-Hate Pokémon", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.CUTE_CHARM, AbilityId.NONE, AbilityId.CONTRARY, 580, 74, 115, 70, 135, 80, 106, 3, 50, 116, GrowthRate.SLOW, 0, false, true,
|
||||||
new PokemonForm("Incarnate Forme", "incarnate", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.CUTE_CHARM, AbilityId.NONE, AbilityId.CONTRARY, 580, 74, 115, 70, 135, 80, 106, 3, 50, 116, false, null, true),
|
new PokemonForm("Incarnate Forme", "incarnate", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.CUTE_CHARM, AbilityId.NONE, AbilityId.CONTRARY, 580, 74, 115, 70, 135, 80, 106, 3, 50, 116, false, null, true),
|
||||||
new PokemonForm("Therian Forme", "therian", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 580, 74, 115, 110, 135, 100, 46, 3, 50, 116),
|
new PokemonForm("Therian Forme", "therian", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 580, 74, 115, 110, 135, 100, 46, 3, 50, 116),
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import type Move from "./moves/move";
|
import type Move from "./moves/move";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { ProtectAttr } from "./moves/move";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export enum TerrainType {
|
export enum TerrainType {
|
||||||
@ -55,7 +54,7 @@ export class Terrain {
|
|||||||
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
|
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
|
||||||
switch (this.terrainType) {
|
switch (this.terrainType) {
|
||||||
case TerrainType.PSYCHIC:
|
case TerrainType.PSYCHIC:
|
||||||
if (!move.hasAttr(ProtectAttr)) {
|
if (!move.hasAttr("ProtectAttr")) {
|
||||||
// Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain
|
// Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain
|
||||||
return (
|
return (
|
||||||
move.getPriority(user) > 0 &&
|
move.getPriority(user) > 0 &&
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { startingWave } from "#app/starting-wave";
|
import { startingWave } from "#app/starting-wave";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import { GameModes } from "#app/game-mode";
|
import { GameModes } from "#enums/game-modes";
|
||||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||||
|
|
||||||
export class TrainerPartyTemplate {
|
export class TrainerPartyTemplate {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { TrainerTierPools } from "#app/data/trainers/typedefs";
|
import type { TrainerTierPools } from "#app/@types/trainer-funcs";
|
||||||
import { TrainerPoolTier } from "#enums/trainer-pool-tier";
|
import { TrainerPoolTier } from "#enums/trainer-pool-tier";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
|
||||||
|