Merge remote-tracking branch 'upstream/beta' into type-move
@ -1,6 +1,17 @@
|
||||
/** @type {import('dependency-cruiser').IConfiguration} */
|
||||
module.exports = {
|
||||
forbidden: [
|
||||
{
|
||||
name: "only-type-imports",
|
||||
severity: "error",
|
||||
comment: "Files in enums and @types may only use type imports.",
|
||||
from: {
|
||||
path: ["(^|/)src/@types", "(^|/)src/enums"],
|
||||
},
|
||||
to: {
|
||||
dependencyTypesNot: ["type-only"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-circular-at-runtime",
|
||||
severity: "warn",
|
||||
@ -31,6 +42,8 @@ module.exports = {
|
||||
"[.]d[.]ts$", // TypeScript declaration files
|
||||
"(^|/)tsconfig[.]json$", // TypeScript config
|
||||
"(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$", // other configs
|
||||
// anything in src/@types
|
||||
"(^|/)src/@types/",
|
||||
],
|
||||
},
|
||||
to: {},
|
||||
@ -205,7 +218,7 @@ module.exports = {
|
||||
module systems it knows of. It's the default because it's the safe option
|
||||
It might come at a performance penalty, though.
|
||||
moduleSystems: ['amd', 'cjs', 'es6', 'tsd']
|
||||
|
||||
|
||||
As in practice only commonjs ('cjs') and ecmascript modules ('es6')
|
||||
are widely used, you can limit the moduleSystems to those.
|
||||
*/
|
||||
@ -213,7 +226,7 @@ module.exports = {
|
||||
// moduleSystems: ['cjs', 'es6'],
|
||||
|
||||
/* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/main/'
|
||||
to open it on your online repo or `vscode://file/${process.cwd()}/` to
|
||||
to open it on your online repo or `vscode://file/${process.cwd()}/` to
|
||||
open it in visual studio code),
|
||||
*/
|
||||
// prefix: `vscode://file/${process.cwd()}/`,
|
||||
@ -258,7 +271,7 @@ module.exports = {
|
||||
to './webpack.conf.js'.
|
||||
|
||||
The (optional) `env` and `arguments` attributes contain the parameters
|
||||
to be passed if your webpack config is a function and takes them (see
|
||||
to be passed if your webpack config is a function and takes them (see
|
||||
webpack documentation for details)
|
||||
*/
|
||||
// webpackConfig: {
|
||||
@ -309,8 +322,8 @@ module.exports = {
|
||||
A list of alias fields in package.jsons
|
||||
See [this specification](https://github.com/defunctzombie/package-browser-field-spec) and
|
||||
the webpack [resolve.alias](https://webpack.js.org/configuration/resolve/#resolvealiasfields)
|
||||
documentation
|
||||
|
||||
documentation
|
||||
|
||||
Defaults to an empty array (= don't use alias fields).
|
||||
*/
|
||||
// aliasFields: ["browser"],
|
||||
|
2
.github/CODEOWNERS
vendored
@ -8,7 +8,7 @@
|
||||
|
||||
# Art Team
|
||||
/public/**/*.png @pagefaultgames/art-team
|
||||
/public/**/*.json @pagefaultgames/art-team
|
||||
/public/**/*.json @pagefaultgames/art-team
|
||||
/public/images @pagefaultgames/art-team
|
||||
/public/battle-anims @pagefaultgames/art-team
|
||||
|
||||
|
37
.github/pull_request_template.md
vendored
@ -2,25 +2,28 @@
|
||||
<!-- Feel free to look at other PRs for examples -->
|
||||
<!--
|
||||
Make sure the title includes categorization (choose the one that best fits):
|
||||
- [Bug]: If the PR is primarily a bug fix
|
||||
- [Move]: If a move has new or changed functionality
|
||||
- [Ability]: If an ability has new or changed functionality
|
||||
- [Item]: For new or modified items
|
||||
- [Mystery]: For new or modified Mystery Encounters
|
||||
- [Test]: If the PR is primarily adding or modifying tests
|
||||
- [UI/UX]: If the PR is changing UI/UX elements
|
||||
- [Audio]: If the PR is adding or changing music/sfx
|
||||
- [Sprite]: If the PR is adding or changing sprites
|
||||
- [Balance]: If the PR is related to game balance
|
||||
- [Challenge]: If the PR is adding or modifying challenges
|
||||
- [Bug]: If the PR is primarily a bug fix
|
||||
- [Move]: If a move has new or changed functionality
|
||||
- [Ability]: If an ability has new or changed functionality
|
||||
- [Item]: For new or modified items
|
||||
- [Mystery]: For new or modified Mystery Encounters
|
||||
- [Test]: If the PR is primarily adding or modifying tests
|
||||
- [UI/UX]: If the PR is changing UI/UX elements
|
||||
- [Audio]: If the PR is adding or changing music/sfx
|
||||
- [Sprite]: If the PR is adding or changing sprites
|
||||
- [Balance]: If the PR is related to game balance
|
||||
- [Challenge]: If the PR is adding or modifying challenges
|
||||
- [Refactor]: If the PR is primarily rewriting existing code
|
||||
- [Docs]: If the PR is just adding or modifying documentation (such as tsdocs/code comments)
|
||||
- [GitHub]: For changes to GitHub workflows/templates/etc
|
||||
- [Misc]: If no other category fits the PR
|
||||
- [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
|
||||
- [Misc]: If no other category fits the PR
|
||||
-->
|
||||
|
||||
<!--
|
||||
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?
|
||||
@ -66,11 +69,11 @@ 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 tested the changes manually?
|
||||
- [ ] 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 made sure that any UI change works for both UI themes (default and legacy)?
|
||||
|
||||
Are there any localization additions or changes? If so:
|
||||
- [ ] Has a locales PR been created on the [locales](https://github.com/pagefaultgames/pokerogue-locales) repo?
|
||||
- [ ] If so, please leave a link to it here:
|
||||
- [ ] Has the translation team been contacted for proofreading/translation?
|
||||
- [ ] Has the translation team been contacted for proofreading/translation?
|
||||
|
2
.github/workflows/deploy.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
||||
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
|
||||
- name: Deploy build on server
|
||||
if: github.event_name == 'push' && github.ref_name == 'main'
|
||||
run: |
|
||||
run: |
|
||||
rsync --del --no-times --checksum -vrm dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_DIR }}
|
||||
ssh -t ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "~/prmanifest --inpath ${{ secrets.DESTINATION_DIR }} --outpath ${{ secrets.DESTINATION_DIR }}/manifest.json"
|
||||
- name: Purge Cloudflare Cache
|
||||
|
2
.github/workflows/quality.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
- 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
|
||||
|
||||
|
10
biome.jsonc
@ -31,7 +31,6 @@
|
||||
"src/overrides.ts",
|
||||
// TODO: these files are too big and complex, ignore them until their respective refactors
|
||||
"src/data/moves/move.ts",
|
||||
"src/data/abilities/ability.ts",
|
||||
|
||||
// this file is just too big:
|
||||
"src/data/balance/tms.ts"
|
||||
@ -42,17 +41,14 @@
|
||||
// TODO: Remove if we ever get down to 0 circular imports
|
||||
"organizeImports": { "enabled": false },
|
||||
"linter": {
|
||||
"ignore": [
|
||||
"src/phases/move-effect-phase.ts" // TODO: unignore after move-effect-phase refactor
|
||||
],
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"correctness": {
|
||||
"noUndeclaredVariables": "off",
|
||||
"noUnusedVariables": "error",
|
||||
"noSwitchDeclarations": "warn", // TODO: refactor and make this an error
|
||||
"noVoidTypeReturn": "warn", // TODO: Refactor and make this an error
|
||||
"noSwitchDeclarations": "error",
|
||||
"noVoidTypeReturn": "error",
|
||||
"noUnusedImports": "error"
|
||||
},
|
||||
"style": {
|
||||
@ -89,7 +85,7 @@
|
||||
"useLiteralKeys": "off",
|
||||
"noForEach": "off", // Foreach vs for of is not that simple.
|
||||
"noUselessSwitchCase": "off", // Explicit > Implicit
|
||||
"noUselessConstructor": "warn", // TODO: Refactor and make this an error
|
||||
"noUselessConstructor": "error",
|
||||
"noBannedTypes": "warn" // TODO: Refactor and make this an error
|
||||
},
|
||||
"nursery": {
|
||||
|
@ -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();
|
2
global.d.ts
vendored
@ -7,7 +7,7 @@ declare global {
|
||||
* Only used in testing.
|
||||
* Can technically be undefined/null but for ease of use we are going to assume it is always defined.
|
||||
* Used to load i18n files exclusively.
|
||||
*
|
||||
*
|
||||
* To set up your own server in a test see `game_data.test.ts`
|
||||
*/
|
||||
var server: SetupServerApi;
|
||||
|
@ -145,6 +145,5 @@
|
||||
</div>
|
||||
<script type="module" src="./src/main.ts"></script>
|
||||
<script src="./src/touch-controls.ts" type="module"></script>
|
||||
<script src="./src/debug.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
@ -9,6 +9,11 @@ pre-commit:
|
||||
- rebase
|
||||
|
||||
post-merge:
|
||||
commands:
|
||||
update-submodules:
|
||||
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:watch": "vitest watch --coverage --no-isolate",
|
||||
"test:silent": "vitest run --silent --no-isolate",
|
||||
"test:create": "node scripts/create-test/create-test.js",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"eslint": "eslint --fix .",
|
||||
"eslint-ci": "eslint .",
|
||||
"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",
|
||||
"depcruise": "depcruise src",
|
||||
"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",
|
||||
"update-version:patch": "npm version patch --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")
|
||||
.disableCrits()
|
||||
.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 Pokemon from "#app/field/pokemon";
|
||||
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;
|
||||
export type AbAttrSuccessFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean) => boolean;
|
||||
// Intentionally re-export all types from the ability attributes module
|
||||
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 PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;
|
||||
export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => 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,9 +1,9 @@
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import type { DamageResult } from "#app/field/pokemon";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
|
||||
export interface AttackMoveResult {
|
||||
move: Moves;
|
||||
move: MoveId;
|
||||
result: DamageResult;
|
||||
damage: number;
|
||||
critical: boolean;
|
||||
|
@ -1,6 +1,7 @@
|
||||
/**
|
||||
* Dex entry for a single Pokemon Species
|
||||
*/
|
||||
export interface DexData {
|
||||
[key: number]: DexEntry;
|
||||
}
|
||||
|
||||
export interface DexEntry {
|
||||
seenAttr: bigint;
|
||||
caughtAttr: bigint;
|
||||
@ -10,7 +11,3 @@ export interface DexEntry {
|
||||
hatchedCount: number;
|
||||
ivs: number[];
|
||||
}
|
||||
|
||||
export interface DexData {
|
||||
[key: number]: DexEntry;
|
||||
}
|
@ -2,7 +2,7 @@ import type { Gender } from "#app/data/gender";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import type { Variant } from "#app/sprites/variant";
|
||||
import type { PokeballType } from "#enums/pokeball";
|
||||
import type { Species } from "#enums/species";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
|
||||
/**
|
||||
* Data pertaining to this Pokemon's Illusion.
|
||||
@ -23,7 +23,7 @@ export interface IllusionData {
|
||||
fusionVariant: Variant;
|
||||
};
|
||||
/** The species of the illusion */
|
||||
species: Species;
|
||||
species: SpeciesId;
|
||||
/** The formIndex of the illusion */
|
||||
formIndex: number;
|
||||
/** The gender of the illusion */
|
||||
|
@ -2,9 +2,6 @@ export interface Localizable {
|
||||
localize(): void;
|
||||
}
|
||||
|
||||
export interface TranslationEntries {
|
||||
[key: string]: string | { [key: string]: string };
|
||||
}
|
||||
export interface SimpleTranslationEntries {
|
||||
[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;
|
@ -1,9 +1,9 @@
|
||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||
import type { PersistentModifier } from "#app/modifier/modifier";
|
||||
import type { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import type { Species } from "#enums/species";
|
||||
import type { TrainerConfig } from "./trainer-config";
|
||||
import type { TrainerPartyTemplate } from "./TrainerPartyTemplate";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
import type { TrainerConfig } from "../data/trainers/trainer-config";
|
||||
import type { TrainerPartyTemplate } from "../data/trainers/TrainerPartyTemplate";
|
||||
|
||||
export type PartyTemplateFunc = () => TrainerPartyTemplate;
|
||||
export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;
|
||||
@ -11,7 +11,7 @@ export type GenModifiersFunc = (party: EnemyPokemon[]) => PersistentModifier[];
|
||||
export type GenAIFunc = (party: EnemyPokemon[]) => void;
|
||||
|
||||
export interface TrainerTierPools {
|
||||
[key: number]: Species[];
|
||||
[key: number]: SpeciesId[];
|
||||
}
|
||||
export interface TrainerConfigs {
|
||||
[key: number]: TrainerConfig;
|
@ -1,9 +1,9 @@
|
||||
import type { BattlerIndex } from "#app/battle";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import type { MoveResult } from "#app/field/pokemon";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
import type { MoveResult } from "#enums/move-result";
|
||||
|
||||
export interface TurnMove {
|
||||
move: Moves;
|
||||
move: MoveId;
|
||||
targets: BattlerIndex[];
|
||||
result?: MoveResult;
|
||||
virtual?: boolean;
|
||||
|
@ -58,35 +58,29 @@ import {
|
||||
getEnemyModifierTypesForWave,
|
||||
getLuckString,
|
||||
getLuckTextTint,
|
||||
getModifierPoolForType,
|
||||
getModifierType,
|
||||
getPartyLuckValue,
|
||||
ModifierPoolType,
|
||||
modifierTypes,
|
||||
PokemonHeldItemModifierType,
|
||||
} 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 {
|
||||
applyAbAttrs,
|
||||
applyPostBattleInitAbAttrs,
|
||||
applyPostItemLostAbAttrs,
|
||||
BlockItemTheftAbAttr,
|
||||
DoubleBattleChanceAbAttr,
|
||||
PostBattleInitAbAttr,
|
||||
PostItemLostAbAttr,
|
||||
} from "#app/data/abilities/ability";
|
||||
import { applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs } from "./data/abilities/apply-ab-attrs";
|
||||
import { allAbilities } from "./data/data-lists";
|
||||
import type { FixedBattleConfig } from "#app/battle";
|
||||
import Battle from "#app/battle";
|
||||
import { BattleType } from "#enums/battle-type";
|
||||
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 SpritePipeline from "#app/pipelines/sprite";
|
||||
import PartyExpBar from "#app/ui/party-exp-bar";
|
||||
import type { TrainerSlot } from "./enums/trainer-slot";
|
||||
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 SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
@ -101,14 +95,12 @@ import type UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
||||
import { addUiThemeOverrides } from "#app/ui/ui-theme";
|
||||
import type PokemonData from "#app/system/pokemon-data";
|
||||
import { Nature } from "#enums/nature";
|
||||
import type { SpeciesFormChange, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
|
||||
import {
|
||||
FormChangeItem,
|
||||
pokemonFormChanges,
|
||||
SpeciesFormChangeManualTrigger,
|
||||
SpeciesFormChangeTimeOfDayTrigger,
|
||||
} from "#app/data/pokemon-forms";
|
||||
import { FormChangePhase } from "#app/phases/form-change-phase";
|
||||
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||
import type { SpeciesFormChangeTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||
import { SpeciesFormChangeTimeOfDayTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||
import { SpeciesFormChangeManualTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||
import { FormChangeItem } from "#enums/form-change-item";
|
||||
import { getTypeRgb } from "#app/data/type";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
|
||||
@ -120,7 +112,7 @@ import { SceneBase } from "#app/scene-base";
|
||||
import CandyBar from "#app/ui/candy-bar";
|
||||
import type { Variant } 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 { InputsController } from "#app/inputs-controller";
|
||||
import { UiInputs } from "#app/ui-inputs";
|
||||
@ -129,12 +121,12 @@ import { ArenaFlyout } from "#app/ui/arena-flyout";
|
||||
import { EaseType } from "#enums/ease-type";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
import { BattleStyle } from "#enums/battle-style";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import type { ExpNotification } from "#enums/exp-notification";
|
||||
import { MoneyFormat } from "#enums/money-format";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { UiTheme } from "#enums/ui-theme";
|
||||
import { TimedEventManager } from "#app/timed-event-manager";
|
||||
import type { PokemonAnimType } from "#enums/pokemon-anim-type";
|
||||
@ -142,50 +134,32 @@ import i18next from "i18next";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { battleSpecDialogue } from "#app/data/dialogue";
|
||||
import { LoadingScene } from "#app/loading-scene";
|
||||
import { LevelCapPhase } from "#app/phases/level-cap-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 type { MovePhase } from "#app/phases/move-phase";
|
||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { allMysteryEncounters, mysteryEncountersByBiome } from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import {
|
||||
allMysteryEncounters,
|
||||
ANTI_VARIANCE_WEIGHT_MODIFIER,
|
||||
AVERAGE_ENCOUNTERS_PER_RUN_TARGET,
|
||||
BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT,
|
||||
MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT,
|
||||
mysteryEncountersByBiome,
|
||||
} from "#app/data/mystery-encounters/mystery-encounters";
|
||||
} from "./constants";
|
||||
import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import type HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||
import { ExpPhase } from "#app/phases/exp-phase";
|
||||
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
|
||||
import type HeldModifierConfig from "#app/@types/held-modifier-config";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { ExpGainsSpeed } from "#enums/exp-gains-speed";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { initGlobalScene } from "#app/global-scene";
|
||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||
import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
|
||||
import { expSpriteKeys } from "./sprites/sprite-keys";
|
||||
import { hasExpSprite } from "./sprites/sprite-utils";
|
||||
import { timedEventManager } from "./global-event-manager";
|
||||
import { starterColors } from "./global-vars/starter-colors";
|
||||
import { startingWave } from "./starting-wave";
|
||||
import { PhaseManager } from "./phase-manager";
|
||||
|
||||
const DEBUG_RNG = false;
|
||||
|
||||
@ -298,18 +272,8 @@ export default class BattleScene extends SceneBase {
|
||||
public gameData: GameData;
|
||||
public sessionSlotId: number;
|
||||
|
||||
/** PhaseQueue: dequeue/remove the first element to get the next phase */
|
||||
public phaseQueue: Phase[];
|
||||
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;
|
||||
/** Manager for the phases active in the battle scene */
|
||||
public readonly phaseManager: PhaseManager;
|
||||
public field: Phaser.GameObjects.Container;
|
||||
public fieldUI: Phaser.GameObjects.Container;
|
||||
public charSprite: CharSprite;
|
||||
@ -397,11 +361,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
constructor() {
|
||||
super("battle");
|
||||
this.phaseQueue = [];
|
||||
this.phaseQueuePrepend = [];
|
||||
this.conditionalQueue = [];
|
||||
this.phaseQueuePrependSpliceIndex = -1;
|
||||
this.nextCommandPhaseQueue = [];
|
||||
this.phaseManager = new PhaseManager();
|
||||
this.eventManager = new TimedEventManager();
|
||||
this.updateGameInfo();
|
||||
initGlobalScene(this);
|
||||
@ -707,20 +667,20 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
ui.setup();
|
||||
|
||||
const defaultMoves = [Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE];
|
||||
const defaultMoves = [MoveId.TACKLE, MoveId.TAIL_WHIP, MoveId.FOCUS_ENERGY, MoveId.STRUGGLE];
|
||||
|
||||
Promise.all([
|
||||
Promise.all(loadPokemonAssets),
|
||||
initCommonAnims().then(() => loadCommonAnimAssets(true)),
|
||||
Promise.all([Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE].map(m => initMoveAnim(m))).then(
|
||||
() => loadMoveAnimAssets(defaultMoves, true),
|
||||
),
|
||||
Promise.all(
|
||||
[MoveId.TACKLE, MoveId.TAIL_WHIP, MoveId.FOCUS_ENERGY, MoveId.STRUGGLE].map(m => initMoveAnim(m)),
|
||||
).then(() => loadMoveAnimAssets(defaultMoves, true)),
|
||||
this.initStarterColors(),
|
||||
]).then(() => {
|
||||
this.pushPhase(new LoginPhase());
|
||||
this.pushPhase(new TitlePhase());
|
||||
this.phaseManager.pushNew("LoginPhase");
|
||||
this.phaseManager.pushNew("TitlePhase");
|
||||
|
||||
this.shiftPhase();
|
||||
this.phaseManager.shiftPhase();
|
||||
});
|
||||
}
|
||||
|
||||
@ -812,6 +772,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add a `getPartyOnSide` function for getting the party of a pokemon
|
||||
public getPlayerParty(): PlayerPokemon[] {
|
||||
return this.party;
|
||||
}
|
||||
@ -899,9 +860,9 @@ export default class BattleScene extends SceneBase {
|
||||
if (allyPokemon?.isActive(true)) {
|
||||
let targetingMovePhase: MovePhase;
|
||||
do {
|
||||
targetingMovePhase = this.findPhase(
|
||||
targetingMovePhase = this.phaseManager.findPhase(
|
||||
mp =>
|
||||
mp instanceof MovePhase &&
|
||||
mp.is("MovePhase") &&
|
||||
mp.targets.length === 1 &&
|
||||
mp.targets[0] === removedPokemon.getBattlerIndex() &&
|
||||
mp.pokemon.isPlayer() !== allyPokemon.isPlayer(),
|
||||
@ -1225,7 +1186,7 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
[this.luckLabelText, this.luckText].map(t => t.setVisible(false));
|
||||
|
||||
this.newArena(Overrides.STARTING_BIOME_OVERRIDE || Biome.TOWN);
|
||||
this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN);
|
||||
|
||||
this.field.setVisible(true);
|
||||
|
||||
@ -1277,7 +1238,7 @@ export default class BattleScene extends SceneBase {
|
||||
duration: 250,
|
||||
ease: "Sine.easeInOut",
|
||||
onComplete: () => {
|
||||
this.clearPhaseQueue();
|
||||
this.phaseManager.clearPhaseQueue();
|
||||
|
||||
this.ui.freeUIData();
|
||||
this.uiContainer.remove(this.ui, true);
|
||||
@ -1294,7 +1255,7 @@ export default class BattleScene extends SceneBase {
|
||||
const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
||||
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
||||
for (const p of playerField) {
|
||||
applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance);
|
||||
applyAbAttrs("DoubleBattleChanceAbAttr", p, null, false, doubleChance);
|
||||
}
|
||||
return Math.max(doubleChance.value, 1);
|
||||
}
|
||||
@ -1450,7 +1411,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
if (lastBattle?.double && !newDouble) {
|
||||
this.tryRemovePhase(p => p instanceof SwitchPhase);
|
||||
this.phaseManager.tryRemovePhase((p: Phase) => p.is("SwitchPhase"));
|
||||
for (const p of this.getPlayerField()) {
|
||||
p.lapseTag(BattlerTagType.COMMANDED);
|
||||
}
|
||||
@ -1492,16 +1453,16 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
playerField.forEach((pokemon, p) => {
|
||||
if (pokemon.isOnField()) {
|
||||
this.pushPhase(new ReturnPhase(p));
|
||||
this.phaseManager.pushNew("ReturnPhase", p);
|
||||
}
|
||||
});
|
||||
|
||||
for (const pokemon of this.getPlayerParty()) {
|
||||
pokemon.resetBattleAndWaveData();
|
||||
pokemon.resetTera();
|
||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
||||
applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon);
|
||||
if (
|
||||
pokemon.hasSpecies(Species.TERAPAGOS) ||
|
||||
pokemon.hasSpecies(SpeciesId.TERAPAGOS) ||
|
||||
(this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)
|
||||
) {
|
||||
this.arena.playerTerasUsed = 0;
|
||||
@ -1509,7 +1470,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
if (!this.trainer.visible) {
|
||||
this.pushPhase(new ShowTrainerPhase());
|
||||
this.phaseManager.pushNew("ShowTrainerPhase");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1518,13 +1479,13 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
if (!this.gameMode.hasRandomBiomes && !isNewBiome) {
|
||||
this.pushPhase(new NextEncounterPhase());
|
||||
this.phaseManager.pushNew("NextEncounterPhase");
|
||||
} else {
|
||||
this.pushPhase(new NewBiomeEncounterPhase());
|
||||
this.phaseManager.pushNew("NewBiomeEncounterPhase");
|
||||
|
||||
const newMaxExpLevel = this.getMaxExpLevel();
|
||||
if (newMaxExpLevel > maxExpLevel) {
|
||||
this.pushPhase(new LevelCapPhase());
|
||||
this.phaseManager.pushNew("LevelCapPhase");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1532,8 +1493,8 @@ export default class BattleScene extends SceneBase {
|
||||
return this.currentBattle;
|
||||
}
|
||||
|
||||
newArena(biome: Biome, playerFaints?: number): Arena {
|
||||
this.arena = new Arena(biome, Biome[biome].toLowerCase(), playerFaints);
|
||||
newArena(biome: BiomeId, playerFaints?: number): Arena {
|
||||
this.arena = new Arena(biome, BiomeId[biome].toLowerCase(), playerFaints);
|
||||
this.eventTarget.dispatchEvent(new NewArenaEvent());
|
||||
|
||||
this.arenaBg.pipelineData = {
|
||||
@ -1589,7 +1550,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(
|
||||
this.getCurrentPhase()?.constructor.name ?? "",
|
||||
this.phaseManager.getCurrentPhase()?.phaseName ?? "",
|
||||
);
|
||||
|
||||
if (
|
||||
@ -1599,7 +1560,7 @@ export default class BattleScene extends SceneBase {
|
||||
!isNullOrUndefined(this.currentBattle.trainer) &&
|
||||
this.currentBattle.trainer.config.hasSpecialtyType()
|
||||
) {
|
||||
if (species.speciesId === Species.WORMADAM) {
|
||||
if (species.speciesId === SpeciesId.WORMADAM) {
|
||||
switch (this.currentBattle.trainer.config.specialtyType) {
|
||||
case PokemonType.GROUND:
|
||||
return 1; // Sandy Cloak
|
||||
@ -1609,7 +1570,7 @@ export default class BattleScene extends SceneBase {
|
||||
return 0; // Plant Cloak
|
||||
}
|
||||
}
|
||||
if (species.speciesId === Species.ROTOM) {
|
||||
if (species.speciesId === SpeciesId.ROTOM) {
|
||||
switch (this.currentBattle.trainer.config.specialtyType) {
|
||||
case PokemonType.FLYING:
|
||||
return 4; // Fan Rotom
|
||||
@ -1625,7 +1586,7 @@ export default class BattleScene extends SceneBase {
|
||||
return 3; // Frost Rotom
|
||||
}
|
||||
}
|
||||
if (species.speciesId === Species.ORICORIO) {
|
||||
if (species.speciesId === SpeciesId.ORICORIO) {
|
||||
switch (this.currentBattle.trainer.config.specialtyType) {
|
||||
case PokemonType.GHOST:
|
||||
return 3; // Sensu Style
|
||||
@ -1637,7 +1598,7 @@ export default class BattleScene extends SceneBase {
|
||||
return 2; // Pa'u Style
|
||||
}
|
||||
}
|
||||
if (species.speciesId === Species.PALDEA_TAUROS) {
|
||||
if (species.speciesId === SpeciesId.PALDEA_TAUROS) {
|
||||
switch (this.currentBattle.trainer.config.specialtyType) {
|
||||
case PokemonType.FIRE:
|
||||
return 1; // Blaze Breed
|
||||
@ -1645,41 +1606,44 @@ export default class BattleScene extends SceneBase {
|
||||
return 2; // Aqua Breed
|
||||
}
|
||||
}
|
||||
if (species.speciesId === Species.SILVALLY || species.speciesId === Species.ARCEUS) {
|
||||
if (species.speciesId === SpeciesId.SILVALLY || species.speciesId === SpeciesId.ARCEUS) {
|
||||
// Would probably never happen, but might as well
|
||||
return this.currentBattle.trainer.config.specialtyType;
|
||||
}
|
||||
}
|
||||
|
||||
switch (species.speciesId) {
|
||||
case Species.UNOWN:
|
||||
case Species.SHELLOS:
|
||||
case Species.GASTRODON:
|
||||
case Species.BASCULIN:
|
||||
case Species.DEERLING:
|
||||
case Species.SAWSBUCK:
|
||||
case Species.SCATTERBUG:
|
||||
case Species.SPEWPA:
|
||||
case Species.VIVILLON:
|
||||
case Species.FLABEBE:
|
||||
case Species.FLOETTE:
|
||||
case Species.FLORGES:
|
||||
case Species.FURFROU:
|
||||
case Species.PUMPKABOO:
|
||||
case Species.GOURGEIST:
|
||||
case Species.ORICORIO:
|
||||
case Species.MAGEARNA:
|
||||
case Species.ZARUDE:
|
||||
case Species.SQUAWKABILLY:
|
||||
case Species.TATSUGIRI:
|
||||
case Species.PALDEA_TAUROS:
|
||||
case SpeciesId.UNOWN:
|
||||
case SpeciesId.SHELLOS:
|
||||
case SpeciesId.GASTRODON:
|
||||
case SpeciesId.BASCULIN:
|
||||
case SpeciesId.DEERLING:
|
||||
case SpeciesId.SAWSBUCK:
|
||||
case SpeciesId.SCATTERBUG:
|
||||
case SpeciesId.SPEWPA:
|
||||
case SpeciesId.VIVILLON:
|
||||
case SpeciesId.FLABEBE:
|
||||
case SpeciesId.FLOETTE:
|
||||
case SpeciesId.FLORGES:
|
||||
case SpeciesId.FURFROU:
|
||||
case SpeciesId.PUMPKABOO:
|
||||
case SpeciesId.GOURGEIST:
|
||||
case SpeciesId.ORICORIO:
|
||||
case SpeciesId.MAGEARNA:
|
||||
case SpeciesId.ZARUDE:
|
||||
case SpeciesId.SQUAWKABILLY:
|
||||
case SpeciesId.TATSUGIRI:
|
||||
case SpeciesId.PALDEA_TAUROS:
|
||||
return randSeedInt(species.forms.length);
|
||||
case Species.PIKACHU:
|
||||
case SpeciesId.MAUSHOLD:
|
||||
case SpeciesId.DUDUNSPARCE:
|
||||
return !randSeedInt(4) ? 1 : 0;
|
||||
case SpeciesId.PIKACHU:
|
||||
if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) {
|
||||
return 0; // Ban Cosplay and Partner Pika from Trainers before wave 30
|
||||
}
|
||||
return randSeedInt(8);
|
||||
case Species.EEVEE:
|
||||
case SpeciesId.EEVEE:
|
||||
if (
|
||||
this.currentBattle?.battleType === BattleType.TRAINER &&
|
||||
this.currentBattle?.waveIndex < 30 &&
|
||||
@ -1688,27 +1652,27 @@ export default class BattleScene extends SceneBase {
|
||||
return 0; // No Partner Eevee for Wave 12 Preschoolers
|
||||
}
|
||||
return randSeedInt(2);
|
||||
case Species.FROAKIE:
|
||||
case Species.FROGADIER:
|
||||
case Species.GRENINJA:
|
||||
case SpeciesId.FROAKIE:
|
||||
case SpeciesId.FROGADIER:
|
||||
case SpeciesId.GRENINJA:
|
||||
if (this.currentBattle?.battleType === BattleType.TRAINER && !isEggPhase) {
|
||||
return 0; // Don't give trainers Battle Bond Greninja, Froakie or Frogadier
|
||||
}
|
||||
return randSeedInt(2);
|
||||
case Species.URSHIFU:
|
||||
case SpeciesId.URSHIFU:
|
||||
return randSeedInt(2);
|
||||
case Species.ZYGARDE:
|
||||
case SpeciesId.ZYGARDE:
|
||||
return randSeedInt(4);
|
||||
case Species.MINIOR:
|
||||
case SpeciesId.MINIOR:
|
||||
return randSeedInt(7);
|
||||
case Species.ALCREMIE:
|
||||
case SpeciesId.ALCREMIE:
|
||||
return randSeedInt(9);
|
||||
case Species.MEOWSTIC:
|
||||
case Species.INDEEDEE:
|
||||
case Species.BASCULEGION:
|
||||
case Species.OINKOLOGNE:
|
||||
case SpeciesId.MEOWSTIC:
|
||||
case SpeciesId.INDEEDEE:
|
||||
case SpeciesId.BASCULEGION:
|
||||
case SpeciesId.OINKOLOGNE:
|
||||
return gender === Gender.FEMALE ? 1 : 0;
|
||||
case Species.TOXTRICITY: {
|
||||
case SpeciesId.TOXTRICITY: {
|
||||
const lowkeyNatures = [
|
||||
Nature.LONELY,
|
||||
Nature.BOLD,
|
||||
@ -1728,7 +1692,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case Species.GIMMIGHOUL:
|
||||
case SpeciesId.GIMMIGHOUL:
|
||||
// Chest form can only be found in Mysterious Chest Encounter, if this is a game mode with MEs
|
||||
if (this.gameMode.hasMysteryEncounters && !isEggPhase) {
|
||||
return 1; // Wandering form
|
||||
@ -1738,10 +1702,10 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
if (ignoreArena) {
|
||||
switch (species.speciesId) {
|
||||
case Species.BURMY:
|
||||
case Species.WORMADAM:
|
||||
case Species.ROTOM:
|
||||
case Species.LYCANROC:
|
||||
case SpeciesId.BURMY:
|
||||
case SpeciesId.WORMADAM:
|
||||
case SpeciesId.ROTOM:
|
||||
case SpeciesId.LYCANROC:
|
||||
return randSeedInt(species.forms.length);
|
||||
}
|
||||
return 0;
|
||||
@ -2172,10 +2136,10 @@ export default class BattleScene extends SceneBase {
|
||||
return filteredSpecies[randSeedInt(filteredSpecies.length)];
|
||||
}
|
||||
|
||||
generateRandomBiome(waveIndex: number): Biome {
|
||||
generateRandomBiome(waveIndex: number): BiomeId {
|
||||
const relWave = waveIndex % 250;
|
||||
const biomes = getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
|
||||
const maxDepth = biomeDepths[Biome.END][0] - 2;
|
||||
const biomes = getEnumValues(BiomeId).filter(b => b !== BiomeId.TOWN && b !== BiomeId.END);
|
||||
const maxDepth = biomeDepths[BiomeId.END][0] - 2;
|
||||
const depthWeights = new Array(maxDepth + 1)
|
||||
.fill(null)
|
||||
.map((_, i: number) => ((1 - Math.min(Math.abs(i / (maxDepth - 1) - relWave / 250) + 0.25, 1)) / 0.75) * 250);
|
||||
@ -2617,286 +2581,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 {
|
||||
this.money = Math.min(this.money + amount, Number.MAX_SAFE_INTEGER);
|
||||
this.updateMoneyText();
|
||||
@ -2944,7 +2628,7 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
} else if (!virtual) {
|
||||
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier);
|
||||
this.queueMessage(
|
||||
this.phaseManager.queueMessage(
|
||||
i18next.t("battle:itemStackFull", {
|
||||
fullItemName: modifier.type.name,
|
||||
itemName: defaultModifierType.name,
|
||||
@ -3055,7 +2739,7 @@ export default class BattleScene extends SceneBase {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
||||
if (source && source.isPlayer() !== target.isPlayer()) {
|
||||
applyAbAttrs(BlockItemTheftAbAttr, source, cancelled);
|
||||
applyAbAttrs("BlockItemTheftAbAttr", source, cancelled);
|
||||
}
|
||||
|
||||
if (cancelled.value) {
|
||||
@ -3089,19 +2773,19 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
const removeOld = itemModifier.stackCount === 0;
|
||||
|
||||
if (!removeOld || !source || this.removeModifier(itemModifier, !source.isPlayer())) {
|
||||
if (!removeOld || !source || this.removeModifier(itemModifier, source.isEnemy())) {
|
||||
const addModifier = () => {
|
||||
if (!matchingModifier || this.removeModifier(matchingModifier, !target.isPlayer())) {
|
||||
if (!matchingModifier || this.removeModifier(matchingModifier, target.isEnemy())) {
|
||||
if (target.isPlayer()) {
|
||||
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant);
|
||||
if (source && itemLost) {
|
||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);
|
||||
applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this.addEnemyModifier(newItemModifier, ignoreUpdate, instant);
|
||||
if (source && itemLost) {
|
||||
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);
|
||||
applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3124,7 +2808,7 @@ export default class BattleScene extends SceneBase {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
||||
if (source && source.isPlayer() !== target.isPlayer()) {
|
||||
applyAbAttrs(BlockItemTheftAbAttr, source, cancelled);
|
||||
applyAbAttrs("BlockItemTheftAbAttr", source, cancelled);
|
||||
}
|
||||
|
||||
if (cancelled.value) {
|
||||
@ -3475,7 +3159,7 @@ export default class BattleScene extends SceneBase {
|
||||
fc => fc.findTrigger(formChangeTriggerType) && fc.canChange(pokemon),
|
||||
);
|
||||
let matchingFormChange: SpeciesFormChange | null;
|
||||
if (pokemon.species.speciesId === Species.NECROZMA && matchingFormChangeOpts.length > 1) {
|
||||
if (pokemon.species.speciesId === SpeciesId.NECROZMA && matchingFormChangeOpts.length > 1) {
|
||||
// Ultra Necrozma is changing its form back, so we need to figure out into which form it devolves.
|
||||
const formChangeItemModifiers = (
|
||||
this.findModifiers(
|
||||
@ -3495,17 +3179,17 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
if (matchingFormChange) {
|
||||
let phase: Phase;
|
||||
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) {
|
||||
phase = new FormChangePhase(pokemon, matchingFormChange, modal);
|
||||
if (pokemon.isPlayer() && !matchingFormChange.quiet) {
|
||||
phase = this.phaseManager.create("FormChangePhase", pokemon, matchingFormChange, modal);
|
||||
} else {
|
||||
phase = new QuietFormChangePhase(pokemon, matchingFormChange);
|
||||
phase = this.phaseManager.create("QuietFormChangePhase", pokemon, matchingFormChange);
|
||||
}
|
||||
if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) {
|
||||
this.overridePhase(phase);
|
||||
if (pokemon.isPlayer() && !matchingFormChange.quiet && modal) {
|
||||
this.phaseManager.overridePhase(phase);
|
||||
} else if (delayed) {
|
||||
this.pushPhase(phase);
|
||||
this.phaseManager.pushPhase(phase);
|
||||
} else {
|
||||
this.unshiftPhase(phase);
|
||||
this.phaseManager.unshiftPhase(phase);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3520,11 +3204,12 @@ export default class BattleScene extends SceneBase {
|
||||
fieldAssets?: Phaser.GameObjects.Sprite[],
|
||||
delayed = false,
|
||||
): boolean {
|
||||
const phase: Phase = new PokemonAnimPhase(battleAnimType, pokemon, fieldAssets);
|
||||
const phaseManager = this.phaseManager;
|
||||
const phase: Phase = phaseManager.create("PokemonAnimPhase", battleAnimType, pokemon, fieldAssets);
|
||||
if (delayed) {
|
||||
this.pushPhase(phase);
|
||||
phaseManager.pushPhase(phase);
|
||||
} else {
|
||||
this.unshiftPhase(phase);
|
||||
phaseManager.unshiftPhase(phase);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3569,21 +3254,18 @@ export default class BattleScene extends SceneBase {
|
||||
gameMode: this.currentBattle ? this.gameMode.getName() : "Title",
|
||||
biome: this.currentBattle ? getBiomeName(this.arena.biomeType) : "",
|
||||
wave: this.currentBattle?.waveIndex ?? 0,
|
||||
party: this.party
|
||||
? this.party.map(p => {
|
||||
return {
|
||||
name: p.name,
|
||||
form: p.getFormKey(),
|
||||
types: p.getTypes().map(type => PokemonType[type]),
|
||||
teraType: PokemonType[p.getTeraType()],
|
||||
isTerastallized: p.isTerastallized,
|
||||
level: p.level,
|
||||
currentHP: p.hp,
|
||||
maxHP: p.getMaxHp(),
|
||||
status: p.status?.effect ? StatusEffect[p.status.effect] : "",
|
||||
};
|
||||
})
|
||||
: [],
|
||||
party:
|
||||
this.party?.map(p => ({
|
||||
name: p.name,
|
||||
form: p.getFormKey(),
|
||||
types: p.getTypes().map(type => PokemonType[type]),
|
||||
teraType: PokemonType[p.getTeraType()],
|
||||
isTerastallized: p.isTerastallized,
|
||||
level: p.level,
|
||||
currentHP: p.hp,
|
||||
maxHP: p.getMaxHp(),
|
||||
status: p.status?.effect ? StatusEffect[p.status.effect] : "",
|
||||
})) ?? [], // TODO: review if this can be nullish
|
||||
modeChain: this.ui?.getModeChain() ?? [],
|
||||
};
|
||||
(window as any).gameInfo = gameInfo;
|
||||
@ -3601,7 +3283,7 @@ export default class BattleScene extends SceneBase {
|
||||
activePokemon = activePokemon.concat(this.getEnemyParty());
|
||||
for (const p of activePokemon) {
|
||||
keys.push(p.getSpriteKey(true));
|
||||
if (p instanceof PlayerPokemon) {
|
||||
if (p.isPlayer()) {
|
||||
keys.push(p.getBattleSpriteKey(true, true));
|
||||
}
|
||||
keys.push(p.species.getCryKey(p.formIndex));
|
||||
@ -3617,7 +3299,7 @@ export default class BattleScene extends SceneBase {
|
||||
* @param pokemon The (enemy) pokemon
|
||||
*/
|
||||
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.ui.showDialogue(
|
||||
battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin,
|
||||
@ -3635,19 +3317,19 @@ export default class BattleScene extends SceneBase {
|
||||
this.currentBattle.double = true;
|
||||
const availablePartyMembers = this.getPlayerParty().filter(p => p.isAllowedInBattle());
|
||||
if (availablePartyMembers.length > 1) {
|
||||
this.pushPhase(new ToggleDoublePositionPhase(true));
|
||||
this.phaseManager.pushNew("ToggleDoublePositionPhase", true);
|
||||
if (!availablePartyMembers[1].isOnField()) {
|
||||
this.pushPhase(new SummonPhase(1));
|
||||
this.phaseManager.pushNew("SummonPhase", 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.shiftPhase();
|
||||
this.phaseManager.shiftPhase();
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.shiftPhase();
|
||||
this.phaseManager.shiftPhase();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3759,10 +3441,10 @@ export default class BattleScene extends SceneBase {
|
||||
|
||||
if (exp) {
|
||||
const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
|
||||
this.unshiftPhase(
|
||||
this.phaseManager.unshiftPhase(
|
||||
expPartyMembers[pm].isOnField()
|
||||
? new ExpPhase(partyMemberIndex, exp)
|
||||
: new ShowPartyExpBarPhase(partyMemberIndex, exp),
|
||||
? this.phaseManager.create("ExpPhase", partyMemberIndex, exp)
|
||||
: this.phaseManager.create("ShowPartyExpBarPhase", partyMemberIndex, exp),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -3966,16 +3648,13 @@ export default class BattleScene extends SceneBase {
|
||||
if (previousEncounter !== null && encounterType === previousEncounter) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
return !(
|
||||
this.mysteryEncounterSaveData.encounteredEvents.length > 0 &&
|
||||
encounterCandidate.maxAllowedEncounters &&
|
||||
encounterCandidate.maxAllowedEncounters > 0 &&
|
||||
this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >=
|
||||
encounterCandidate.maxAllowedEncounters
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
);
|
||||
})
|
||||
.map(m => allMysteryEncounters[m]);
|
||||
// Decrement tier
|
||||
|
209
src/battle.ts
@ -1,5 +1,5 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { Command } from "./ui/command-ui-handler";
|
||||
import type { Command } from "#enums/command";
|
||||
import {
|
||||
randomString,
|
||||
getEnumValues,
|
||||
@ -8,10 +8,12 @@ import {
|
||||
shiftCharCodes,
|
||||
randSeedItem,
|
||||
randInt,
|
||||
randSeedFloat,
|
||||
} 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 { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
||||
import { MoneyMultiplierModifier, type PokemonHeldItemModifier } from "./modifier/modifier";
|
||||
import type { PokeballType } from "#enums/pokeball";
|
||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
@ -20,27 +22,20 @@ import type { TurnMove } from "./@types/turn-move";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { MusicPreference } from "#app/system/settings/settings";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
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 { BattleType } from "#enums/battle-type";
|
||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||
|
||||
export enum BattlerIndex {
|
||||
ATTACKER = -1,
|
||||
PLAYER,
|
||||
PLAYER_2,
|
||||
ENEMY,
|
||||
ENEMY_2,
|
||||
}
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
|
||||
export interface TurnCommand {
|
||||
command: Command;
|
||||
@ -79,7 +74,7 @@ export default class Battle {
|
||||
public battleScore = 0;
|
||||
public postBattleLoot: PokemonHeldItemModifier[] = [];
|
||||
public escapeAttempts = 0;
|
||||
public lastMove: Moves;
|
||||
public lastMove: MoveId;
|
||||
public battleSeed: string = randomString(16, true);
|
||||
private battleSeedState: string | null = null;
|
||||
public moneyScattered = 0;
|
||||
@ -151,7 +146,7 @@ export default class Battle {
|
||||
randSeedGaussForLevel(value: number): number {
|
||||
let rand = 0;
|
||||
for (let i = value; i > 0; i--) {
|
||||
rand += Phaser.Math.RND.realInRange(0, 1);
|
||||
rand += randSeedFloat();
|
||||
}
|
||||
return rand / value;
|
||||
}
|
||||
@ -179,7 +174,7 @@ export default class Battle {
|
||||
this.postBattleLoot.push(
|
||||
...globalScene
|
||||
.findModifiers(
|
||||
m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferable,
|
||||
m => m.is("PokemonHeldItemModifier") && m.pokemonId === enemyPokemon.id && m.isTransferable,
|
||||
false,
|
||||
)
|
||||
.map(i => {
|
||||
@ -206,7 +201,7 @@ export default class Battle {
|
||||
const message = i18next.t("battle:moneyPickedUp", {
|
||||
moneyAmount: formattedMoneyAmount,
|
||||
});
|
||||
globalScene.queueMessage(message, undefined, true);
|
||||
globalScene.phaseManager.queueMessage(message, undefined, true);
|
||||
|
||||
globalScene.currentBattle.moneyScattered = 0;
|
||||
}
|
||||
@ -265,14 +260,14 @@ export default class Battle {
|
||||
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
||||
if (globalScene.musicPreference === MusicPreference.GENFIVE) {
|
||||
switch (pokemon.species.speciesId) {
|
||||
case Species.REGIROCK:
|
||||
case Species.REGICE:
|
||||
case Species.REGISTEEL:
|
||||
case Species.REGIGIGAS:
|
||||
case Species.REGIDRAGO:
|
||||
case Species.REGIELEKI:
|
||||
case SpeciesId.REGIROCK:
|
||||
case SpeciesId.REGICE:
|
||||
case SpeciesId.REGISTEEL:
|
||||
case SpeciesId.REGIGIGAS:
|
||||
case SpeciesId.REGIDRAGO:
|
||||
case SpeciesId.REGIELEKI:
|
||||
return "battle_legendary_regis_g5";
|
||||
case Species.KYUREM:
|
||||
case SpeciesId.KYUREM:
|
||||
return "battle_legendary_kyurem";
|
||||
default:
|
||||
if (pokemon.species.legendary) {
|
||||
@ -283,80 +278,80 @@ export default class Battle {
|
||||
}
|
||||
if (globalScene.musicPreference === MusicPreference.ALLGENS) {
|
||||
switch (pokemon.species.speciesId) {
|
||||
case Species.ARTICUNO:
|
||||
case Species.ZAPDOS:
|
||||
case Species.MOLTRES:
|
||||
case Species.MEWTWO:
|
||||
case Species.MEW:
|
||||
case SpeciesId.ARTICUNO:
|
||||
case SpeciesId.ZAPDOS:
|
||||
case SpeciesId.MOLTRES:
|
||||
case SpeciesId.MEWTWO:
|
||||
case SpeciesId.MEW:
|
||||
return "battle_legendary_kanto";
|
||||
case Species.RAIKOU:
|
||||
case SpeciesId.RAIKOU:
|
||||
return "battle_legendary_raikou";
|
||||
case Species.ENTEI:
|
||||
case SpeciesId.ENTEI:
|
||||
return "battle_legendary_entei";
|
||||
case Species.SUICUNE:
|
||||
case SpeciesId.SUICUNE:
|
||||
return "battle_legendary_suicune";
|
||||
case Species.LUGIA:
|
||||
case SpeciesId.LUGIA:
|
||||
return "battle_legendary_lugia";
|
||||
case Species.HO_OH:
|
||||
case SpeciesId.HO_OH:
|
||||
return "battle_legendary_ho_oh";
|
||||
case Species.REGIROCK:
|
||||
case Species.REGICE:
|
||||
case Species.REGISTEEL:
|
||||
case Species.REGIGIGAS:
|
||||
case Species.REGIDRAGO:
|
||||
case Species.REGIELEKI:
|
||||
case SpeciesId.REGIROCK:
|
||||
case SpeciesId.REGICE:
|
||||
case SpeciesId.REGISTEEL:
|
||||
case SpeciesId.REGIGIGAS:
|
||||
case SpeciesId.REGIDRAGO:
|
||||
case SpeciesId.REGIELEKI:
|
||||
return "battle_legendary_regis_g6";
|
||||
case Species.GROUDON:
|
||||
case Species.KYOGRE:
|
||||
case SpeciesId.GROUDON:
|
||||
case SpeciesId.KYOGRE:
|
||||
return "battle_legendary_gro_kyo";
|
||||
case Species.RAYQUAZA:
|
||||
case SpeciesId.RAYQUAZA:
|
||||
return "battle_legendary_rayquaza";
|
||||
case Species.DEOXYS:
|
||||
case SpeciesId.DEOXYS:
|
||||
return "battle_legendary_deoxys";
|
||||
case Species.UXIE:
|
||||
case Species.MESPRIT:
|
||||
case Species.AZELF:
|
||||
case SpeciesId.UXIE:
|
||||
case SpeciesId.MESPRIT:
|
||||
case SpeciesId.AZELF:
|
||||
return "battle_legendary_lake_trio";
|
||||
case Species.HEATRAN:
|
||||
case Species.CRESSELIA:
|
||||
case Species.DARKRAI:
|
||||
case Species.SHAYMIN:
|
||||
case SpeciesId.HEATRAN:
|
||||
case SpeciesId.CRESSELIA:
|
||||
case SpeciesId.DARKRAI:
|
||||
case SpeciesId.SHAYMIN:
|
||||
return "battle_legendary_sinnoh";
|
||||
case Species.DIALGA:
|
||||
case Species.PALKIA:
|
||||
case SpeciesId.DIALGA:
|
||||
case SpeciesId.PALKIA:
|
||||
if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ORIGIN) {
|
||||
return "battle_legendary_origin_forme";
|
||||
}
|
||||
return "battle_legendary_dia_pal";
|
||||
case Species.GIRATINA:
|
||||
case SpeciesId.GIRATINA:
|
||||
return "battle_legendary_giratina";
|
||||
case Species.ARCEUS:
|
||||
case SpeciesId.ARCEUS:
|
||||
return "battle_legendary_arceus";
|
||||
case Species.COBALION:
|
||||
case Species.TERRAKION:
|
||||
case Species.VIRIZION:
|
||||
case Species.KELDEO:
|
||||
case Species.TORNADUS:
|
||||
case Species.LANDORUS:
|
||||
case Species.THUNDURUS:
|
||||
case Species.MELOETTA:
|
||||
case Species.GENESECT:
|
||||
case SpeciesId.COBALION:
|
||||
case SpeciesId.TERRAKION:
|
||||
case SpeciesId.VIRIZION:
|
||||
case SpeciesId.KELDEO:
|
||||
case SpeciesId.TORNADUS:
|
||||
case SpeciesId.LANDORUS:
|
||||
case SpeciesId.THUNDURUS:
|
||||
case SpeciesId.MELOETTA:
|
||||
case SpeciesId.GENESECT:
|
||||
return "battle_legendary_unova";
|
||||
case Species.KYUREM:
|
||||
case SpeciesId.KYUREM:
|
||||
return "battle_legendary_kyurem";
|
||||
case Species.XERNEAS:
|
||||
case Species.YVELTAL:
|
||||
case Species.ZYGARDE:
|
||||
case SpeciesId.XERNEAS:
|
||||
case SpeciesId.YVELTAL:
|
||||
case SpeciesId.ZYGARDE:
|
||||
return "battle_legendary_xern_yvel";
|
||||
case Species.TAPU_KOKO:
|
||||
case Species.TAPU_LELE:
|
||||
case Species.TAPU_BULU:
|
||||
case Species.TAPU_FINI:
|
||||
case SpeciesId.TAPU_KOKO:
|
||||
case SpeciesId.TAPU_LELE:
|
||||
case SpeciesId.TAPU_BULU:
|
||||
case SpeciesId.TAPU_FINI:
|
||||
return "battle_legendary_tapu";
|
||||
case Species.SOLGALEO:
|
||||
case Species.LUNALA:
|
||||
case SpeciesId.SOLGALEO:
|
||||
case SpeciesId.LUNALA:
|
||||
return "battle_legendary_sol_lun";
|
||||
case Species.NECROZMA:
|
||||
case SpeciesId.NECROZMA:
|
||||
switch (pokemon.getFormKey()) {
|
||||
case "dusk-mane":
|
||||
case "dawn-wings":
|
||||
@ -366,50 +361,50 @@ export default class Battle {
|
||||
default:
|
||||
return "battle_legendary_sol_lun";
|
||||
}
|
||||
case Species.NIHILEGO:
|
||||
case Species.PHEROMOSA:
|
||||
case Species.BUZZWOLE:
|
||||
case Species.XURKITREE:
|
||||
case Species.CELESTEELA:
|
||||
case Species.KARTANA:
|
||||
case Species.GUZZLORD:
|
||||
case Species.POIPOLE:
|
||||
case Species.NAGANADEL:
|
||||
case Species.STAKATAKA:
|
||||
case Species.BLACEPHALON:
|
||||
case SpeciesId.NIHILEGO:
|
||||
case SpeciesId.PHEROMOSA:
|
||||
case SpeciesId.BUZZWOLE:
|
||||
case SpeciesId.XURKITREE:
|
||||
case SpeciesId.CELESTEELA:
|
||||
case SpeciesId.KARTANA:
|
||||
case SpeciesId.GUZZLORD:
|
||||
case SpeciesId.POIPOLE:
|
||||
case SpeciesId.NAGANADEL:
|
||||
case SpeciesId.STAKATAKA:
|
||||
case SpeciesId.BLACEPHALON:
|
||||
return "battle_legendary_ub";
|
||||
case Species.ZACIAN:
|
||||
case Species.ZAMAZENTA:
|
||||
case SpeciesId.ZACIAN:
|
||||
case SpeciesId.ZAMAZENTA:
|
||||
return "battle_legendary_zac_zam";
|
||||
case Species.GLASTRIER:
|
||||
case Species.SPECTRIER:
|
||||
case SpeciesId.GLASTRIER:
|
||||
case SpeciesId.SPECTRIER:
|
||||
return "battle_legendary_glas_spec";
|
||||
case Species.CALYREX:
|
||||
case SpeciesId.CALYREX:
|
||||
if (pokemon.getFormKey() === "ice" || pokemon.getFormKey() === "shadow") {
|
||||
return "battle_legendary_riders";
|
||||
}
|
||||
return "battle_legendary_calyrex";
|
||||
case Species.GALAR_ARTICUNO:
|
||||
case Species.GALAR_ZAPDOS:
|
||||
case Species.GALAR_MOLTRES:
|
||||
case SpeciesId.GALAR_ARTICUNO:
|
||||
case SpeciesId.GALAR_ZAPDOS:
|
||||
case SpeciesId.GALAR_MOLTRES:
|
||||
return "battle_legendary_birds_galar";
|
||||
case Species.WO_CHIEN:
|
||||
case Species.CHIEN_PAO:
|
||||
case Species.TING_LU:
|
||||
case Species.CHI_YU:
|
||||
case SpeciesId.WO_CHIEN:
|
||||
case SpeciesId.CHIEN_PAO:
|
||||
case SpeciesId.TING_LU:
|
||||
case SpeciesId.CHI_YU:
|
||||
return "battle_legendary_ruinous";
|
||||
case Species.KORAIDON:
|
||||
case Species.MIRAIDON:
|
||||
case SpeciesId.KORAIDON:
|
||||
case SpeciesId.MIRAIDON:
|
||||
return "battle_legendary_kor_mir";
|
||||
case Species.OKIDOGI:
|
||||
case Species.MUNKIDORI:
|
||||
case Species.FEZANDIPITI:
|
||||
case SpeciesId.OKIDOGI:
|
||||
case SpeciesId.MUNKIDORI:
|
||||
case SpeciesId.FEZANDIPITI:
|
||||
return "battle_legendary_loyal_three";
|
||||
case Species.OGERPON:
|
||||
case SpeciesId.OGERPON:
|
||||
return "battle_legendary_ogerpon";
|
||||
case Species.TERAPAGOS:
|
||||
case SpeciesId.TERAPAGOS:
|
||||
return "battle_legendary_terapagos";
|
||||
case Species.PECHARUNT:
|
||||
case SpeciesId.PECHARUNT:
|
||||
return "battle_legendary_pecharunt";
|
||||
default:
|
||||
if (pokemon.species.legendary) {
|
||||
|
@ -197,10 +197,7 @@ export function canIAssignThisKey(config, key) {
|
||||
export function canIOverrideThisSetting(config, settingName) {
|
||||
const key = getKeyWithSettingName(config, settingName);
|
||||
// || isTheLatestBind(config, settingName) no longer needed since action and cancel are protected
|
||||
if (config.blacklist?.includes(key)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return !config.blacklist?.includes(key);
|
||||
}
|
||||
|
||||
export function canIDeleteThisKey(config, key) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
|
||||
/** The maximum size of the player's party */
|
||||
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*/
|
||||
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 { Abilities } from "#enums/abilities";
|
||||
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: Abilities;
|
||||
|
||||
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: Abilities, 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 = Abilities[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,41 +7,25 @@ import { MoveTarget } from "#enums/MoveTarget";
|
||||
import { MoveCategory } from "#enums/MoveCategory";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
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 type { BattlerIndex } from "#app/battle";
|
||||
import {
|
||||
BlockNonDirectDamageAbAttr,
|
||||
InfiltratorAbAttr,
|
||||
PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr,
|
||||
ProtectStatAbAttr,
|
||||
applyAbAttrs,
|
||||
applyOnGainAbAttrs,
|
||||
applyOnLoseAbAttrs,
|
||||
} from "#app/data/abilities/ability";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "./abilities/apply-ab-attrs";
|
||||
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 { Abilities } from "#enums/abilities";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
||||
|
||||
export enum ArenaTagSide {
|
||||
BOTH,
|
||||
PLAYER,
|
||||
ENEMY,
|
||||
}
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
|
||||
export abstract class ArenaTag {
|
||||
constructor(
|
||||
public tagType: ArenaTagType,
|
||||
public turnCount: number,
|
||||
public sourceMove?: Moves,
|
||||
public sourceMove?: MoveId,
|
||||
public sourceId?: number,
|
||||
public side: ArenaTagSide = ArenaTagSide.BOTH,
|
||||
) {}
|
||||
@ -54,7 +38,7 @@ export abstract class ArenaTag {
|
||||
|
||||
onRemove(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
{ moveName: this.getMoveName() },
|
||||
@ -116,7 +100,7 @@ export abstract class ArenaTag {
|
||||
*/
|
||||
export class MistTag extends ArenaTag {
|
||||
constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.MIST, turnCount, Moves.MIST, sourceId, side);
|
||||
super(ArenaTagType.MIST, turnCount, MoveId.MIST, sourceId, side);
|
||||
}
|
||||
|
||||
onAdd(arena: Arena, quiet = false): void {
|
||||
@ -126,7 +110,7 @@ export class MistTag extends ArenaTag {
|
||||
const source = globalScene.getPokemonById(this.sourceId);
|
||||
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:mistOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
}),
|
||||
@ -152,7 +136,7 @@ export class MistTag extends ArenaTag {
|
||||
if (attacker) {
|
||||
const bypassed = new BooleanHolder(false);
|
||||
// TODO: Allow this to be simulated
|
||||
applyAbAttrs(InfiltratorAbAttr, attacker, null, false, bypassed);
|
||||
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||
if (bypassed.value) {
|
||||
return false;
|
||||
}
|
||||
@ -161,7 +145,7 @@ export class MistTag extends ArenaTag {
|
||||
cancelled.value = true;
|
||||
|
||||
if (!simulated) {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:mistApply"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mistApply"));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -188,7 +172,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
|
||||
constructor(
|
||||
tagType: ArenaTagType,
|
||||
turnCount: number,
|
||||
sourceMove: Moves,
|
||||
sourceMove: MoveId,
|
||||
sourceId: number,
|
||||
side: ArenaTagSide,
|
||||
weakenedCategories: MoveCategory[],
|
||||
@ -217,7 +201,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
|
||||
): boolean {
|
||||
if (this.weakenedCategories.includes(moveCategory)) {
|
||||
const bypassed = new BooleanHolder(false);
|
||||
applyAbAttrs(InfiltratorAbAttr, attacker, null, false, bypassed);
|
||||
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||
if (bypassed.value) {
|
||||
return false;
|
||||
}
|
||||
@ -230,16 +214,16 @@ export class WeakenMoveScreenTag extends ArenaTag {
|
||||
|
||||
/**
|
||||
* Reduces the damage of physical moves.
|
||||
* Used by {@linkcode Moves.REFLECT}
|
||||
* Used by {@linkcode MoveId.REFLECT}
|
||||
*/
|
||||
class ReflectTag extends WeakenMoveScreenTag {
|
||||
constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side, [MoveCategory.PHYSICAL]);
|
||||
super(ArenaTagType.REFLECT, turnCount, MoveId.REFLECT, sourceId, side, [MoveCategory.PHYSICAL]);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -250,16 +234,16 @@ class ReflectTag extends WeakenMoveScreenTag {
|
||||
|
||||
/**
|
||||
* Reduces the damage of special moves.
|
||||
* Used by {@linkcode Moves.LIGHT_SCREEN}
|
||||
* Used by {@linkcode MoveId.LIGHT_SCREEN}
|
||||
*/
|
||||
class LightScreenTag extends WeakenMoveScreenTag {
|
||||
constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side, [MoveCategory.SPECIAL]);
|
||||
super(ArenaTagType.LIGHT_SCREEN, turnCount, MoveId.LIGHT_SCREEN, sourceId, side, [MoveCategory.SPECIAL]);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -270,11 +254,11 @@ class LightScreenTag extends WeakenMoveScreenTag {
|
||||
|
||||
/**
|
||||
* Reduces the damage of physical and special moves.
|
||||
* Used by {@linkcode Moves.AURORA_VEIL}
|
||||
* Used by {@linkcode MoveId.AURORA_VEIL}
|
||||
*/
|
||||
class AuroraVeilTag extends WeakenMoveScreenTag {
|
||||
constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side, [
|
||||
super(ArenaTagType.AURORA_VEIL, turnCount, MoveId.AURORA_VEIL, sourceId, side, [
|
||||
MoveCategory.SPECIAL,
|
||||
MoveCategory.PHYSICAL,
|
||||
]);
|
||||
@ -282,7 +266,7 @@ class AuroraVeilTag extends WeakenMoveScreenTag {
|
||||
|
||||
onAdd(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -291,7 +275,7 @@ class AuroraVeilTag extends WeakenMoveScreenTag {
|
||||
}
|
||||
}
|
||||
|
||||
type ProtectConditionFunc = (arena: Arena, moveId: Moves) => boolean;
|
||||
type ProtectConditionFunc = (arena: Arena, moveId: MoveId) => boolean;
|
||||
|
||||
/**
|
||||
* Class to implement conditional team protection
|
||||
@ -305,7 +289,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
|
||||
constructor(
|
||||
tagType: ArenaTagType,
|
||||
sourceMove: Moves,
|
||||
sourceMove: MoveId,
|
||||
sourceId: number,
|
||||
side: ArenaTagSide,
|
||||
condition: ProtectConditionFunc,
|
||||
@ -318,7 +302,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
{ moveName: super.getMoveName() },
|
||||
@ -337,7 +321,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
* @param isProtected a {@linkcode BooleanHolder} used to flag if the move is protected against
|
||||
* @param _attacker the attacking {@linkcode Pokemon}
|
||||
* @param defender the defending {@linkcode Pokemon}
|
||||
* @param moveId the {@linkcode Moves | identifier} for the move being used
|
||||
* @param moveId the {@linkcode MoveId | identifier} for the move being used
|
||||
* @param ignoresProtectBypass a {@linkcode BooleanHolder} used to flag if a protection effect supercedes effects that ignore protection
|
||||
* @returns `true` if this tag protected against the attack; `false` otherwise
|
||||
*/
|
||||
@ -347,7 +331,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
isProtected: BooleanHolder,
|
||||
_attacker: Pokemon,
|
||||
defender: Pokemon,
|
||||
moveId: Moves,
|
||||
moveId: MoveId,
|
||||
ignoresProtectBypass: BooleanHolder,
|
||||
): boolean {
|
||||
if ((this.side === ArenaTagSide.PLAYER) === defender.isPlayer() && this.protectConditionFunc(arena, moveId)) {
|
||||
@ -355,7 +339,7 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
isProtected.value = true;
|
||||
if (!simulated) {
|
||||
new CommonBattleAnim(CommonAnim.PROTECT, defender).play();
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:conditionalProtectApply", {
|
||||
moveName: super.getMoveName(),
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(defender),
|
||||
@ -375,15 +359,15 @@ export class ConditionalProtectTag extends ArenaTag {
|
||||
* Condition function for {@link https://bulbapedia.bulbagarden.net/wiki/Quick_Guard_(move) Quick Guard's}
|
||||
* protection effect.
|
||||
* @param _arena {@linkcode Arena} The arena containing the protection effect
|
||||
* @param moveId {@linkcode Moves} The move to check against this condition
|
||||
* @param moveId {@linkcode MoveId} The move to check against this condition
|
||||
* @returns `true` if the incoming move's priority is greater than 0.
|
||||
* This includes moves with modified priorities from abilities (e.g. Prankster)
|
||||
*/
|
||||
const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, 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();
|
||||
if (attacker) {
|
||||
return move.getPriority(attacker) > 0;
|
||||
@ -398,7 +382,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
|
||||
*/
|
||||
class QuickGuardTag extends ConditionalProtectTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.QUICK_GUARD, Moves.QUICK_GUARD, sourceId, side, QuickGuardConditionFunc);
|
||||
super(ArenaTagType.QUICK_GUARD, MoveId.QUICK_GUARD, sourceId, side, QuickGuardConditionFunc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -406,7 +390,7 @@ class QuickGuardTag extends ConditionalProtectTag {
|
||||
* Condition function for {@link https://bulbapedia.bulbagarden.net/wiki/Wide_Guard_(move) Wide Guard's}
|
||||
* protection effect.
|
||||
* @param _arena {@linkcode Arena} The arena containing the protection effect
|
||||
* @param moveId {@linkcode Moves} The move to check against this condition
|
||||
* @param moveId {@linkcode MoveId} The move to check against this condition
|
||||
* @returns `true` if the incoming move is multi-targeted (even if it's only used against one Pokemon).
|
||||
*/
|
||||
const WideGuardConditionFunc: ProtectConditionFunc = (_arena, moveId): boolean => {
|
||||
@ -429,7 +413,7 @@ const WideGuardConditionFunc: ProtectConditionFunc = (_arena, moveId): boolean =
|
||||
*/
|
||||
class WideGuardTag extends ConditionalProtectTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.WIDE_GUARD, Moves.WIDE_GUARD, sourceId, side, WideGuardConditionFunc);
|
||||
super(ArenaTagType.WIDE_GUARD, MoveId.WIDE_GUARD, sourceId, side, WideGuardConditionFunc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,7 +421,7 @@ class WideGuardTag extends ConditionalProtectTag {
|
||||
* Condition function for {@link https://bulbapedia.bulbagarden.net/wiki/Mat_Block_(move) Mat Block's}
|
||||
* protection effect.
|
||||
* @param _arena {@linkcode Arena} The arena containing the protection effect.
|
||||
* @param moveId {@linkcode Moves} The move to check against this condition.
|
||||
* @param moveId {@linkcode MoveId} The move to check against this condition.
|
||||
* @returns `true` if the incoming move is not a Status move.
|
||||
*/
|
||||
const MatBlockConditionFunc: ProtectConditionFunc = (_arena, moveId): boolean => {
|
||||
@ -451,14 +435,14 @@ const MatBlockConditionFunc: ProtectConditionFunc = (_arena, moveId): boolean =>
|
||||
*/
|
||||
class MatBlockTag extends ConditionalProtectTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.MAT_BLOCK, Moves.MAT_BLOCK, sourceId, side, MatBlockConditionFunc);
|
||||
super(ArenaTagType.MAT_BLOCK, MoveId.MAT_BLOCK, sourceId, side, MatBlockConditionFunc);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena) {
|
||||
if (this.sourceId) {
|
||||
const source = globalScene.getPokemonById(this.sourceId);
|
||||
if (source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:matBlockOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
}),
|
||||
@ -474,7 +458,7 @@ class MatBlockTag extends ConditionalProtectTag {
|
||||
* Condition function for {@link https://bulbapedia.bulbagarden.net/wiki/Crafty_Shield_(move) Crafty Shield's}
|
||||
* protection effect.
|
||||
* @param _arena {@linkcode Arena} The arena containing the protection effect
|
||||
* @param moveId {@linkcode Moves} The move to check against this condition
|
||||
* @param moveId {@linkcode MoveId} The move to check against this condition
|
||||
* @returns `true` if the incoming move is a Status move, is not a hazard, and does not target all
|
||||
* Pokemon or sides of the field.
|
||||
*/
|
||||
@ -495,7 +479,7 @@ const CraftyShieldConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
|
||||
*/
|
||||
class CraftyShieldTag extends ConditionalProtectTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.CRAFTY_SHIELD, Moves.CRAFTY_SHIELD, sourceId, side, CraftyShieldConditionFunc, true);
|
||||
super(ArenaTagType.CRAFTY_SHIELD, MoveId.CRAFTY_SHIELD, sourceId, side, CraftyShieldConditionFunc, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,17 +491,17 @@ export class NoCritTag extends ArenaTag {
|
||||
/**
|
||||
* Constructor method for the NoCritTag class
|
||||
* @param turnCount `number` the number of turns this effect lasts
|
||||
* @param sourceMove {@linkcode Moves} the move that created this effect
|
||||
* @param sourceMove {@linkcode MoveId} the move that created this effect
|
||||
* @param sourceId `number` the ID of the {@linkcode Pokemon} that created this effect
|
||||
* @param side {@linkcode ArenaTagSide} the side to which this effect belongs
|
||||
*/
|
||||
constructor(turnCount: number, sourceMove: Moves, sourceId: number, side: ArenaTagSide) {
|
||||
constructor(turnCount: number, sourceMove: MoveId, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.NO_CRIT, turnCount, sourceMove, sourceId, side);
|
||||
}
|
||||
|
||||
/** Queues a message upon adding this effect to the field */
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, {
|
||||
moveName: this.getMoveName(),
|
||||
}),
|
||||
@ -527,7 +511,7 @@ export class NoCritTag extends ArenaTag {
|
||||
/** Queues a message upon removing this effect from the field */
|
||||
onRemove(_arena: Arena): void {
|
||||
const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct?
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:noCritOnRemove", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined),
|
||||
moveName: this.getMoveName(),
|
||||
@ -546,7 +530,7 @@ class WishTag extends ArenaTag {
|
||||
private healHp: number;
|
||||
|
||||
constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.WISH, turnCount, Moves.WISH, sourceId, side);
|
||||
super(ArenaTagType.WISH, turnCount, MoveId.WISH, sourceId, side);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
@ -567,8 +551,8 @@ class WishTag extends ArenaTag {
|
||||
onRemove(_arena: Arena): void {
|
||||
const target = globalScene.getField()[this.battlerIndex];
|
||||
if (target?.isActive(true)) {
|
||||
globalScene.queueMessage(this.triggerMessage);
|
||||
globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false));
|
||||
globalScene.phaseManager.queueMessage(this.triggerMessage);
|
||||
globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), this.healHp, null, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -588,7 +572,7 @@ export class WeakenMoveTypeTag extends ArenaTag {
|
||||
* @param sourceMove - The move that created the tag.
|
||||
* @param sourceId - The ID of the source of the tag.
|
||||
*/
|
||||
constructor(tagType: ArenaTagType, turnCount: number, type: PokemonType, sourceMove: Moves, sourceId: number) {
|
||||
constructor(tagType: ArenaTagType, turnCount: number, type: PokemonType, sourceMove: MoveId, sourceId: number) {
|
||||
super(tagType, turnCount, sourceMove, sourceId);
|
||||
|
||||
this.weakenedType = type;
|
||||
@ -617,15 +601,15 @@ export class WeakenMoveTypeTag extends ArenaTag {
|
||||
*/
|
||||
class MudSportTag extends WeakenMoveTypeTag {
|
||||
constructor(turnCount: number, sourceId: number) {
|
||||
super(ArenaTagType.MUD_SPORT, turnCount, PokemonType.ELECTRIC, Moves.MUD_SPORT, sourceId);
|
||||
super(ArenaTagType.MUD_SPORT, turnCount, PokemonType.ELECTRIC, MoveId.MUD_SPORT, sourceId);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mudSportOnAdd"));
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:mudSportOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mudSportOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -635,15 +619,15 @@ class MudSportTag extends WeakenMoveTypeTag {
|
||||
*/
|
||||
class WaterSportTag extends WeakenMoveTypeTag {
|
||||
constructor(turnCount: number, sourceId: number) {
|
||||
super(ArenaTagType.WATER_SPORT, turnCount, PokemonType.FIRE, Moves.WATER_SPORT, sourceId);
|
||||
super(ArenaTagType.WATER_SPORT, turnCount, PokemonType.FIRE, MoveId.WATER_SPORT, sourceId);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:waterSportOnAdd"));
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:waterSportOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:waterSportOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,13 +637,13 @@ class WaterSportTag extends WeakenMoveTypeTag {
|
||||
* Converts Normal-type moves to Electric type for the rest of the turn.
|
||||
*/
|
||||
export class IonDelugeTag extends ArenaTag {
|
||||
constructor(sourceMove?: Moves) {
|
||||
constructor(sourceMove?: MoveId) {
|
||||
super(ArenaTagType.ION_DELUGE, 1, sourceMove);
|
||||
}
|
||||
|
||||
/** Queues an on-add message */
|
||||
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
|
||||
@ -696,7 +680,7 @@ export class ArenaTrapTag extends ArenaTag {
|
||||
* @param side - The side (player or enemy) the tag affects.
|
||||
* @param maxLayers - The maximum amount of layers this tag can have.
|
||||
*/
|
||||
constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: number, side: ArenaTagSide, maxLayers: number) {
|
||||
constructor(tagType: ArenaTagType, sourceMove: MoveId, sourceId: number, side: ArenaTagSide, maxLayers: number) {
|
||||
super(tagType, 0, sourceMove, sourceId, side);
|
||||
|
||||
this.layers = 1;
|
||||
@ -750,7 +734,7 @@ export class ArenaTrapTag extends ArenaTag {
|
||||
*/
|
||||
class SpikesTag extends ArenaTrapTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.SPIKES, Moves.SPIKES, sourceId, side, 3);
|
||||
super(ArenaTagType.SPIKES, MoveId.SPIKES, sourceId, side, 3);
|
||||
}
|
||||
|
||||
onAdd(arena: Arena, quiet = false): void {
|
||||
@ -758,7 +742,7 @@ class SpikesTag extends ArenaTrapTag {
|
||||
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:spikesOnAdd", {
|
||||
moveName: this.getMoveName(),
|
||||
opponentDesc: source.getOpponentDescriptor(),
|
||||
@ -773,7 +757,7 @@ class SpikesTag extends ArenaTrapTag {
|
||||
}
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
if (simulated || cancelled.value) {
|
||||
return !cancelled.value;
|
||||
}
|
||||
@ -781,7 +765,7 @@ class SpikesTag extends ArenaTrapTag {
|
||||
const damageHpRatio = 1 / (10 - 2 * this.layers);
|
||||
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
||||
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:spikesActivateTrap", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
@ -802,7 +786,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
||||
private neutralized: boolean;
|
||||
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.TOXIC_SPIKES, Moves.TOXIC_SPIKES, sourceId, side, 2);
|
||||
super(ArenaTagType.TOXIC_SPIKES, MoveId.TOXIC_SPIKES, sourceId, side, 2);
|
||||
this.neutralized = false;
|
||||
}
|
||||
|
||||
@ -811,7 +795,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
||||
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:toxicSpikesOnAdd", {
|
||||
moveName: this.getMoveName(),
|
||||
opponentDesc: source.getOpponentDescriptor(),
|
||||
@ -834,7 +818,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
||||
if (pokemon.isOfType(PokemonType.POISON)) {
|
||||
this.neutralized = true;
|
||||
if (globalScene.arena.removeTag(this.tagType)) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:toxicSpikesActivateTrapPoison", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
moveName: this.getMoveName(),
|
||||
@ -867,7 +851,7 @@ class ToxicSpikesTag extends ArenaTrapTag {
|
||||
}
|
||||
|
||||
/**
|
||||
* Arena Tag class for delayed attacks, such as {@linkcode Moves.FUTURE_SIGHT} or {@linkcode Moves.DOOM_DESIRE}.
|
||||
* Arena Tag class for delayed attacks, such as {@linkcode MoveId.FUTURE_SIGHT} or {@linkcode MoveId.DOOM_DESIRE}.
|
||||
* Delays the attack's effect by a set amount of turns, usually 3 (including the turn the move is used),
|
||||
* and deals damage after the turn count is reached.
|
||||
*/
|
||||
@ -876,7 +860,7 @@ export class DelayedAttackTag extends ArenaTag {
|
||||
|
||||
constructor(
|
||||
tagType: ArenaTagType,
|
||||
sourceMove: Moves | undefined,
|
||||
sourceMove: MoveId | undefined,
|
||||
sourceId: number,
|
||||
targetIndex: BattlerIndex,
|
||||
side: ArenaTagSide = ArenaTagSide.BOTH,
|
||||
@ -891,8 +875,13 @@ export class DelayedAttackTag extends ArenaTag {
|
||||
const ret = super.lapse(arena);
|
||||
|
||||
if (!ret) {
|
||||
globalScene.unshiftPhase(
|
||||
new MoveEffectPhase(this.sourceId!, [this.targetIndex], allMoves[this.sourceMove!], false, true),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"MoveEffectPhase",
|
||||
this.sourceId!,
|
||||
[this.targetIndex],
|
||||
allMoves[this.sourceMove!],
|
||||
false,
|
||||
true,
|
||||
); // TODO: are those bangs correct?
|
||||
}
|
||||
|
||||
@ -909,7 +898,7 @@ export class DelayedAttackTag extends ArenaTag {
|
||||
*/
|
||||
class StealthRockTag extends ArenaTrapTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.STEALTH_ROCK, Moves.STEALTH_ROCK, sourceId, side, 1);
|
||||
super(ArenaTagType.STEALTH_ROCK, MoveId.STEALTH_ROCK, sourceId, side, 1);
|
||||
}
|
||||
|
||||
onAdd(arena: Arena, quiet = false): void {
|
||||
@ -917,7 +906,7 @@ class StealthRockTag extends ArenaTrapTag {
|
||||
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:stealthRockOnAdd", {
|
||||
opponentDesc: source.getOpponentDescriptor(),
|
||||
}),
|
||||
@ -956,7 +945,7 @@ class StealthRockTag extends ArenaTrapTag {
|
||||
|
||||
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
if (cancelled.value) {
|
||||
return false;
|
||||
}
|
||||
@ -971,7 +960,7 @@ class StealthRockTag extends ArenaTrapTag {
|
||||
}
|
||||
|
||||
const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio);
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:stealthRockActivateTrap", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
@ -994,14 +983,14 @@ class StealthRockTag extends ArenaTrapTag {
|
||||
*/
|
||||
class StickyWebTag extends ArenaTrapTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.STICKY_WEB, Moves.STICKY_WEB, sourceId, side, 1);
|
||||
super(ArenaTagType.STICKY_WEB, MoveId.STICKY_WEB, sourceId, side, 1);
|
||||
}
|
||||
|
||||
onAdd(arena: Arena, quiet = false): void {
|
||||
super.onAdd(arena);
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (!quiet && source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:stickyWebOnAdd", {
|
||||
moveName: this.getMoveName(),
|
||||
opponentDesc: source.getOpponentDescriptor(),
|
||||
@ -1013,32 +1002,31 @@ class StickyWebTag extends ArenaTrapTag {
|
||||
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
||||
if (pokemon.isGrounded()) {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled);
|
||||
applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled);
|
||||
|
||||
if (simulated) {
|
||||
return !cancelled.value;
|
||||
}
|
||||
|
||||
if (!cancelled.value) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:stickyWebActivateTrap", {
|
||||
pokemonName: pokemon.getNameToRender(),
|
||||
}),
|
||||
);
|
||||
const stages = new NumberHolder(-1);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(
|
||||
pokemon.getBattlerIndex(),
|
||||
false,
|
||||
[Stat.SPD],
|
||||
stages.value,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
false,
|
||||
[Stat.SPD],
|
||||
stages.value,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@ -1055,7 +1043,7 @@ class StickyWebTag extends ArenaTrapTag {
|
||||
*/
|
||||
export class TrickRoomTag extends ArenaTag {
|
||||
constructor(turnCount: number, sourceId: number) {
|
||||
super(ArenaTagType.TRICK_ROOM, turnCount, Moves.TRICK_ROOM, sourceId);
|
||||
super(ArenaTagType.TRICK_ROOM, turnCount, MoveId.TRICK_ROOM, sourceId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1074,7 +1062,7 @@ export class TrickRoomTag extends ArenaTag {
|
||||
onAdd(_arena: Arena): void {
|
||||
const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null;
|
||||
if (source) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:trickRoomOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
}),
|
||||
@ -1083,22 +1071,22 @@ export class TrickRoomTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:trickRoomOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Gravity_(move) Gravity}.
|
||||
* Grounds all Pokémon on the field, including Flying-types and those with
|
||||
* {@linkcode Abilities.LEVITATE} for the duration of the arena tag, usually 5 turns.
|
||||
* {@linkcode AbilityId.LEVITATE} for the duration of the arena tag, usually 5 turns.
|
||||
*/
|
||||
export class GravityTag extends ArenaTag {
|
||||
constructor(turnCount: number) {
|
||||
super(ArenaTagType.GRAVITY, turnCount, Moves.GRAVITY);
|
||||
super(ArenaTagType.GRAVITY, turnCount, MoveId.GRAVITY);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
||||
globalScene.getField(true).forEach(pokemon => {
|
||||
if (pokemon !== null) {
|
||||
pokemon.removeTag(BattlerTagType.FLOATING);
|
||||
@ -1111,7 +1099,7 @@ export class GravityTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:gravityOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1122,12 +1110,12 @@ export class GravityTag extends ArenaTag {
|
||||
*/
|
||||
class TailwindTag extends ArenaTag {
|
||||
constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.TAILWIND, turnCount, Moves.TAILWIND, sourceId, side);
|
||||
super(ArenaTagType.TAILWIND, turnCount, MoveId.TAILWIND, sourceId, side);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1136,31 +1124,33 @@ class TailwindTag extends ArenaTag {
|
||||
|
||||
const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable!
|
||||
const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? [];
|
||||
const phaseManager = globalScene.phaseManager;
|
||||
|
||||
for (const pokemon of party) {
|
||||
// Apply the CHARGED tag to party members with the WIND_POWER ability
|
||||
if (pokemon.hasAbility(Abilities.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
|
||||
if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) {
|
||||
pokemon.addTag(BattlerTagType.CHARGED);
|
||||
globalScene.queueMessage(
|
||||
phaseManager.queueMessage(
|
||||
i18next.t("abilityTriggers:windPowerCharged", {
|
||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||
moveName: this.getMoveName(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Raise attack by one stage if party member has WIND_RIDER ability
|
||||
// TODO: Ability displays should be handled by the ability
|
||||
if (pokemon.hasAbility(Abilities.WIND_RIDER)) {
|
||||
globalScene.queueAbilityDisplay(pokemon, false, true);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true));
|
||||
globalScene.queueAbilityDisplay(pokemon, false, false);
|
||||
if (pokemon.hasAbility(AbilityId.WIND_RIDER)) {
|
||||
phaseManager.queueAbilityDisplay(pokemon, false, true);
|
||||
phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true);
|
||||
phaseManager.queueAbilityDisplay(pokemon, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena, quiet = false): void {
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1171,29 +1161,29 @@ class TailwindTag extends ArenaTag {
|
||||
|
||||
/**
|
||||
* Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Happy_Hour_(move) Happy Hour}.
|
||||
* Doubles the prize money from trainers and money moves like {@linkcode Moves.PAY_DAY} and {@linkcode Moves.MAKE_IT_RAIN}.
|
||||
* Doubles the prize money from trainers and money moves like {@linkcode MoveId.PAY_DAY} and {@linkcode MoveId.MAKE_IT_RAIN}.
|
||||
*/
|
||||
class HappyHourTag extends ArenaTag {
|
||||
constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.HAPPY_HOUR, turnCount, Moves.HAPPY_HOUR, sourceId, side);
|
||||
super(ArenaTagType.HAPPY_HOUR, turnCount, MoveId.HAPPY_HOUR, sourceId, side);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:happyHourOnAdd"));
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:happyHourOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:happyHourOnRemove"));
|
||||
}
|
||||
}
|
||||
|
||||
class SafeguardTag extends ArenaTag {
|
||||
constructor(turnCount: number, sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.SAFEGUARD, turnCount, Moves.SAFEGUARD, sourceId, side);
|
||||
super(ArenaTagType.SAFEGUARD, turnCount, MoveId.SAFEGUARD, sourceId, side);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1201,7 +1191,7 @@ class SafeguardTag extends ArenaTag {
|
||||
}
|
||||
|
||||
onRemove(_arena: Arena): void {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1221,7 +1211,7 @@ class NoneTag extends ArenaTag {
|
||||
*/
|
||||
class ImprisonTag extends ArenaTrapTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.IMPRISON, Moves.IMPRISON, sourceId, side, 1);
|
||||
super(ArenaTagType.IMPRISON, MoveId.IMPRISON, sourceId, side, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1234,10 +1224,10 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
const party = this.getAffectedPokemon();
|
||||
party?.forEach((p: Pokemon) => {
|
||||
if (p.isAllowedInBattle()) {
|
||||
p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
|
||||
p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId);
|
||||
}
|
||||
});
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("battlerTags:imprisonOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
}),
|
||||
@ -1263,7 +1253,7 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
override activateTrap(pokemon: Pokemon): boolean {
|
||||
const source = this.getSourcePokemon();
|
||||
if (source?.isActive(true) && pokemon.isAllowedInBattle()) {
|
||||
pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId);
|
||||
pokemon.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1289,12 +1279,12 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
*/
|
||||
class FireGrassPledgeTag extends ArenaTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.FIRE_GRASS_PLEDGE, 4, Moves.FIRE_PLEDGE, sourceId, side);
|
||||
super(ArenaTagType.FIRE_GRASS_PLEDGE, 4, MoveId.FIRE_PLEDGE, sourceId, side);
|
||||
}
|
||||
|
||||
override onAdd(_arena: Arena): void {
|
||||
// "A sea of fire enveloped your/the opposing team!"
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1309,14 +1299,17 @@ class FireGrassPledgeTag extends ArenaTag {
|
||||
.filter(pokemon => !pokemon.isOfType(PokemonType.FIRE) && !pokemon.switchOutStatus)
|
||||
.forEach(pokemon => {
|
||||
// "{pokemonNameWithAffix} was hurt by the sea of fire!"
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:fireGrassPledgeLapse", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
);
|
||||
// TODO: Replace this with a proper animation
|
||||
globalScene.unshiftPhase(
|
||||
new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"CommonAnimPhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
pokemon.getBattlerIndex(),
|
||||
CommonAnim.MAGMA_STORM,
|
||||
);
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||
});
|
||||
@ -1334,12 +1327,12 @@ class FireGrassPledgeTag extends ArenaTag {
|
||||
*/
|
||||
class WaterFirePledgeTag extends ArenaTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.WATER_FIRE_PLEDGE, 4, Moves.WATER_PLEDGE, sourceId, side);
|
||||
super(ArenaTagType.WATER_FIRE_PLEDGE, 4, MoveId.WATER_PLEDGE, sourceId, side);
|
||||
}
|
||||
|
||||
override onAdd(_arena: Arena): void {
|
||||
// "A rainbow appeared in the sky on your/the opposing team's side!"
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1368,12 +1361,12 @@ class WaterFirePledgeTag extends ArenaTag {
|
||||
*/
|
||||
class GrassWaterPledgeTag extends ArenaTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.GRASS_WATER_PLEDGE, 4, Moves.GRASS_PLEDGE, sourceId, side);
|
||||
super(ArenaTagType.GRASS_WATER_PLEDGE, 4, MoveId.GRASS_PLEDGE, sourceId, side);
|
||||
}
|
||||
|
||||
override onAdd(_arena: Arena): void {
|
||||
// "A swamp enveloped your/the opposing team!"
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t(
|
||||
`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`,
|
||||
),
|
||||
@ -1390,11 +1383,11 @@ class GrassWaterPledgeTag extends ArenaTag {
|
||||
*/
|
||||
export class FairyLockTag extends ArenaTag {
|
||||
constructor(turnCount: number, sourceId: number) {
|
||||
super(ArenaTagType.FAIRY_LOCK, turnCount, Moves.FAIRY_LOCK, sourceId);
|
||||
super(ArenaTagType.FAIRY_LOCK, turnCount, MoveId.FAIRY_LOCK, sourceId);
|
||||
}
|
||||
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1443,20 +1436,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
|
||||
const setter = globalScene
|
||||
.getField()
|
||||
.filter(p => p?.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false))[0];
|
||||
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr));
|
||||
.filter(p => p?.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false))[0];
|
||||
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"));
|
||||
}
|
||||
}
|
||||
|
||||
public override onRemove(_arena: Arena, quiet = false) {
|
||||
this.beingRemoved = true;
|
||||
if (!quiet) {
|
||||
globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
|
||||
}
|
||||
|
||||
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
|
||||
if (pokemon && !pokemon.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false)) {
|
||||
if (pokemon && !pokemon.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false)) {
|
||||
[true, false].forEach(passive => applyOnGainAbAttrs(pokemon, passive));
|
||||
}
|
||||
}
|
||||
@ -1472,7 +1465,7 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
||||
|
||||
private playActivationMessage(pokemon: Pokemon | null) {
|
||||
if (pokemon) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("arenaTag:neutralizingGasOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
@ -1485,7 +1478,7 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
||||
export function getArenaTag(
|
||||
tagType: ArenaTagType,
|
||||
turnCount: number,
|
||||
sourceMove: Moves | undefined,
|
||||
sourceMove: MoveId | undefined,
|
||||
sourceId: number,
|
||||
targetIndex?: BattlerIndex,
|
||||
side: ArenaTagSide = ArenaTagSide.BOTH,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
|
||||
export type SignatureSpecies = {
|
||||
[key in string]: (Species | Species[])[];
|
||||
[key in string]: (SpeciesId | SpeciesId[])[];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -18,87 +18,87 @@ export type SignatureSpecies = {
|
||||
*/
|
||||
export const signatureSpecies: SignatureSpecies = new Proxy({
|
||||
// Gym Leaders- Kanto
|
||||
BROCK: [Species.ONIX, Species.GEODUDE, [Species.OMANYTE, Species.KABUTO], Species.AERODACTYL],
|
||||
MISTY: [Species.STARYU, Species.PSYDUCK, Species.WOOPER, Species.LAPRAS],
|
||||
LT_SURGE: [Species.PICHU, Species.VOLTORB, Species.ELEKID, Species.JOLTEON],
|
||||
ERIKA: [Species.ODDISH, Species.BELLSPROUT, Species.TANGELA, Species.HOPPIP],
|
||||
JANINE: [Species.VENONAT, Species.SPINARAK, Species.ZUBAT, Species.KOFFING],
|
||||
SABRINA: [Species.ABRA, Species.MR_MIME, Species.SMOOCHUM, Species.ESPEON],
|
||||
BLAINE: [Species.GROWLITHE, Species.PONYTA, Species.MAGBY, Species.VULPIX],
|
||||
GIOVANNI: [Species.RHYHORN, Species.MEOWTH, [Species.NIDORAN_F, Species.NIDORAN_M], Species.DIGLETT], // Tera Ground Meowth
|
||||
BROCK: [SpeciesId.ONIX, SpeciesId.GEODUDE, [SpeciesId.OMANYTE, SpeciesId.KABUTO], SpeciesId.AERODACTYL],
|
||||
MISTY: [SpeciesId.STARYU, SpeciesId.PSYDUCK, SpeciesId.WOOPER, SpeciesId.LAPRAS],
|
||||
LT_SURGE: [SpeciesId.PICHU, SpeciesId.VOLTORB, SpeciesId.ELEKID, SpeciesId.JOLTEON],
|
||||
ERIKA: [SpeciesId.ODDISH, SpeciesId.BELLSPROUT, SpeciesId.TANGELA, SpeciesId.HOPPIP],
|
||||
JANINE: [SpeciesId.VENONAT, SpeciesId.SPINARAK, SpeciesId.ZUBAT, SpeciesId.KOFFING],
|
||||
SABRINA: [SpeciesId.ABRA, SpeciesId.MR_MIME, SpeciesId.SMOOCHUM, SpeciesId.ESPEON],
|
||||
BLAINE: [SpeciesId.GROWLITHE, SpeciesId.PONYTA, SpeciesId.MAGBY, SpeciesId.VULPIX],
|
||||
GIOVANNI: [SpeciesId.RHYHORN, SpeciesId.MEOWTH, [SpeciesId.NIDORAN_F, SpeciesId.NIDORAN_M], SpeciesId.DIGLETT], // Tera Ground Meowth
|
||||
// Gym Leaders- Johto
|
||||
FALKNER: [Species.PIDGEY, Species.HOOTHOOT, Species.NATU, Species.MURKROW],
|
||||
BUGSY: [Species.SCYTHER, Species.SHUCKLE, Species.YANMA, [Species.PINSIR, Species.HERACROSS]],
|
||||
WHITNEY: [Species.MILTANK, Species.AIPOM, Species.IGGLYBUFF, [Species.GIRAFARIG, Species.STANTLER]],
|
||||
MORTY: [Species.GASTLY, Species.MISDREAVUS, Species.DUSKULL, Species.SABLEYE],
|
||||
CHUCK: [Species.POLIWRATH, Species.MANKEY, Species.TYROGUE, Species.MACHOP],
|
||||
JASMINE: [Species.STEELIX, Species.MAGNEMITE, Species.PINECO, Species.SKARMORY],
|
||||
PRYCE: [Species.SWINUB, Species.SEEL, Species.SHELLDER, Species.SNEASEL],
|
||||
CLAIR: [Species.HORSEA, Species.DRATINI, Species.MAGIKARP, Species.DRUDDIGON], // Tera Dragon Magikarp
|
||||
FALKNER: [SpeciesId.PIDGEY, SpeciesId.HOOTHOOT, SpeciesId.NATU, SpeciesId.MURKROW],
|
||||
BUGSY: [SpeciesId.SCYTHER, SpeciesId.SHUCKLE, SpeciesId.YANMA, [SpeciesId.PINSIR, SpeciesId.HERACROSS]],
|
||||
WHITNEY: [SpeciesId.MILTANK, SpeciesId.AIPOM, SpeciesId.IGGLYBUFF, [SpeciesId.GIRAFARIG, SpeciesId.STANTLER]],
|
||||
MORTY: [SpeciesId.GASTLY, SpeciesId.MISDREAVUS, SpeciesId.DUSKULL, SpeciesId.SABLEYE],
|
||||
CHUCK: [SpeciesId.POLIWRATH, SpeciesId.MANKEY, SpeciesId.TYROGUE, SpeciesId.MACHOP],
|
||||
JASMINE: [SpeciesId.STEELIX, SpeciesId.MAGNEMITE, SpeciesId.PINECO, SpeciesId.SKARMORY],
|
||||
PRYCE: [SpeciesId.SWINUB, SpeciesId.SEEL, SpeciesId.SHELLDER, SpeciesId.SNEASEL],
|
||||
CLAIR: [SpeciesId.HORSEA, SpeciesId.DRATINI, SpeciesId.MAGIKARP, SpeciesId.DRUDDIGON], // Tera Dragon Magikarp
|
||||
// Gym Leaders- Hoenn
|
||||
ROXANNE: [Species.NOSEPASS, Species.GEODUDE, [Species.LILEEP, Species.ANORITH], Species.ARON],
|
||||
BRAWLY: [Species.MAKUHITA, Species.MACHOP, Species.MEDITITE, Species.SHROOMISH],
|
||||
WATTSON: [Species.ELECTRIKE, Species.VOLTORB, Species.MAGNEMITE, [Species.PLUSLE, Species.MINUN]],
|
||||
FLANNERY: [Species.TORKOAL, Species.SLUGMA, Species.NUMEL, Species.HOUNDOUR],
|
||||
NORMAN: [Species.SLAKOTH, Species.KECLEON, Species.WHISMUR, Species.ZANGOOSE],
|
||||
WINONA: [Species.SWABLU, Species.WINGULL, Species.TROPIUS, Species.SKARMORY],
|
||||
TATE: [Species.SOLROCK, Species.NATU, Species.CHINGLING, Species.GALLADE],
|
||||
LIZA: [Species.LUNATONE, Species.BALTOY, Species.SPOINK, Species.GARDEVOIR],
|
||||
JUAN: [Species.HORSEA, Species.SPHEAL, Species.BARBOACH, Species.CORPHISH],
|
||||
ROXANNE: [SpeciesId.NOSEPASS, SpeciesId.GEODUDE, [SpeciesId.LILEEP, SpeciesId.ANORITH], SpeciesId.ARON],
|
||||
BRAWLY: [SpeciesId.MAKUHITA, SpeciesId.MACHOP, SpeciesId.MEDITITE, SpeciesId.SHROOMISH],
|
||||
WATTSON: [SpeciesId.ELECTRIKE, SpeciesId.VOLTORB, SpeciesId.MAGNEMITE, [SpeciesId.PLUSLE, SpeciesId.MINUN]],
|
||||
FLANNERY: [SpeciesId.TORKOAL, SpeciesId.SLUGMA, SpeciesId.NUMEL, SpeciesId.HOUNDOUR],
|
||||
NORMAN: [SpeciesId.SLAKOTH, SpeciesId.KECLEON, SpeciesId.WHISMUR, SpeciesId.ZANGOOSE],
|
||||
WINONA: [SpeciesId.SWABLU, SpeciesId.WINGULL, SpeciesId.TROPIUS, SpeciesId.SKARMORY],
|
||||
TATE: [SpeciesId.SOLROCK, SpeciesId.NATU, SpeciesId.CHINGLING, SpeciesId.GALLADE],
|
||||
LIZA: [SpeciesId.LUNATONE, SpeciesId.BALTOY, SpeciesId.SPOINK, SpeciesId.GARDEVOIR],
|
||||
JUAN: [SpeciesId.HORSEA, SpeciesId.SPHEAL, SpeciesId.BARBOACH, SpeciesId.CORPHISH],
|
||||
// Gym Leaders- Sinnoh
|
||||
ROARK: [Species.CRANIDOS, Species.GEODUDE, Species.NOSEPASS, Species.LARVITAR],
|
||||
GARDENIA: [Species.BUDEW, Species.CHERUBI, Species.TURTWIG, Species.LEAFEON],
|
||||
MAYLENE: [Species.RIOLU, Species.MEDITITE, Species.CHIMCHAR, Species.CROAGUNK],
|
||||
CRASHER_WAKE: [Species.BUIZEL, Species.WOOPER, Species.PIPLUP, Species.MAGIKARP],
|
||||
FANTINA: [Species.MISDREAVUS, Species.DRIFLOON, Species.DUSKULL, Species.SPIRITOMB],
|
||||
BYRON: [Species.SHIELDON, Species.BRONZOR, Species.ARON, Species.SKARMORY],
|
||||
CANDICE: [Species.FROSLASS, Species.SNOVER, Species.SNEASEL, Species.GLACEON],
|
||||
VOLKNER: [Species.ELEKID, Species.SHINX, Species.CHINCHOU, Species.ROTOM],
|
||||
ROARK: [SpeciesId.CRANIDOS, SpeciesId.GEODUDE, SpeciesId.NOSEPASS, SpeciesId.LARVITAR],
|
||||
GARDENIA: [SpeciesId.BUDEW, SpeciesId.CHERUBI, SpeciesId.TURTWIG, SpeciesId.LEAFEON],
|
||||
MAYLENE: [SpeciesId.RIOLU, SpeciesId.MEDITITE, SpeciesId.CHIMCHAR, SpeciesId.CROAGUNK],
|
||||
CRASHER_WAKE: [SpeciesId.BUIZEL, SpeciesId.WOOPER, SpeciesId.PIPLUP, SpeciesId.MAGIKARP],
|
||||
FANTINA: [SpeciesId.MISDREAVUS, SpeciesId.DRIFLOON, SpeciesId.DUSKULL, SpeciesId.SPIRITOMB],
|
||||
BYRON: [SpeciesId.SHIELDON, SpeciesId.BRONZOR, SpeciesId.ARON, SpeciesId.SKARMORY],
|
||||
CANDICE: [SpeciesId.FROSLASS, SpeciesId.SNOVER, SpeciesId.SNEASEL, SpeciesId.GLACEON],
|
||||
VOLKNER: [SpeciesId.ELEKID, SpeciesId.SHINX, SpeciesId.CHINCHOU, SpeciesId.ROTOM],
|
||||
// Gym Leaders- Unova
|
||||
CILAN: [Species.PANSAGE, Species.SNIVY, Species.MARACTUS, Species.FERROSEED],
|
||||
CHILI: [Species.PANSEAR, Species.TEPIG, Species.HEATMOR, Species.DARUMAKA],
|
||||
CRESS: [Species.PANPOUR, Species.OSHAWOTT, Species.BASCULIN, Species.TYMPOLE],
|
||||
CHEREN: [Species.LILLIPUP, Species.MINCCINO, Species.PIDOVE, Species.BOUFFALANT],
|
||||
LENORA: [Species.PATRAT, Species.DEERLING, Species.AUDINO, Species.BRAVIARY],
|
||||
ROXIE: [Species.VENIPEDE, Species.KOFFING, Species.TRUBBISH, Species.TOXEL],
|
||||
BURGH: [Species.SEWADDLE, Species.DWEBBLE, [Species.KARRABLAST, Species.SHELMET], Species.DURANT],
|
||||
ELESA: [Species.BLITZLE, Species.EMOLGA, Species.JOLTIK, Species.TYNAMO],
|
||||
CLAY: [Species.DRILBUR, Species.SANDILE, Species.TYMPOLE, Species.GOLETT],
|
||||
SKYLA: [Species.DUCKLETT, Species.WOOBAT, [Species.RUFFLET, Species.VULLABY], Species.ARCHEN],
|
||||
BRYCEN: [Species.CRYOGONAL, Species.VANILLITE, Species.CUBCHOO, Species.GALAR_DARUMAKA],
|
||||
DRAYDEN: [Species.AXEW, Species.DRUDDIGON, Species.TRAPINCH, Species.DEINO],
|
||||
MARLON: [Species.FRILLISH, Species.TIRTOUGA, Species.WAILMER, Species.MANTYKE],
|
||||
CILAN: [SpeciesId.PANSAGE, SpeciesId.SNIVY, SpeciesId.MARACTUS, SpeciesId.FERROSEED],
|
||||
CHILI: [SpeciesId.PANSEAR, SpeciesId.TEPIG, SpeciesId.HEATMOR, SpeciesId.DARUMAKA],
|
||||
CRESS: [SpeciesId.PANPOUR, SpeciesId.OSHAWOTT, SpeciesId.BASCULIN, SpeciesId.TYMPOLE],
|
||||
CHEREN: [SpeciesId.LILLIPUP, SpeciesId.MINCCINO, SpeciesId.PIDOVE, SpeciesId.BOUFFALANT],
|
||||
LENORA: [SpeciesId.PATRAT, SpeciesId.DEERLING, SpeciesId.AUDINO, SpeciesId.BRAVIARY],
|
||||
ROXIE: [SpeciesId.VENIPEDE, SpeciesId.KOFFING, SpeciesId.TRUBBISH, SpeciesId.TOXEL],
|
||||
BURGH: [SpeciesId.SEWADDLE, SpeciesId.DWEBBLE, [SpeciesId.KARRABLAST, SpeciesId.SHELMET], SpeciesId.DURANT],
|
||||
ELESA: [SpeciesId.BLITZLE, SpeciesId.EMOLGA, SpeciesId.JOLTIK, SpeciesId.TYNAMO],
|
||||
CLAY: [SpeciesId.DRILBUR, SpeciesId.SANDILE, SpeciesId.TYMPOLE, SpeciesId.GOLETT],
|
||||
SKYLA: [SpeciesId.DUCKLETT, SpeciesId.WOOBAT, [SpeciesId.RUFFLET, SpeciesId.VULLABY], SpeciesId.ARCHEN],
|
||||
BRYCEN: [SpeciesId.CRYOGONAL, SpeciesId.VANILLITE, SpeciesId.CUBCHOO, SpeciesId.GALAR_DARUMAKA],
|
||||
DRAYDEN: [SpeciesId.AXEW, SpeciesId.DRUDDIGON, SpeciesId.TRAPINCH, SpeciesId.DEINO],
|
||||
MARLON: [SpeciesId.FRILLISH, SpeciesId.TIRTOUGA, SpeciesId.WAILMER, SpeciesId.MANTYKE],
|
||||
// Gym Leaders- Kalos
|
||||
VIOLA: [Species.SCATTERBUG, Species.SURSKIT, Species.CUTIEFLY, Species.BLIPBUG],
|
||||
GRANT: [Species.TYRUNT, Species.AMAURA, Species.BINACLE, Species.DWEBBLE],
|
||||
KORRINA: [Species.RIOLU, Species.MIENFOO, Species.HAWLUCHA, Species.PANCHAM],
|
||||
RAMOS: [Species.SKIDDO, Species.HOPPIP, Species.BELLSPROUT, [Species.PHANTUMP, Species.PUMPKABOO]],
|
||||
CLEMONT: [Species.HELIOPTILE, Species.MAGNEMITE, Species.DEDENNE, Species.ROTOM],
|
||||
VALERIE: [Species.SYLVEON, Species.MAWILE, Species.MR_MIME, [Species.SPRITZEE, Species.SWIRLIX]],
|
||||
OLYMPIA: [Species.ESPURR, Species.SIGILYPH, Species.INKAY, Species.SLOWKING],
|
||||
WULFRIC: [Species.BERGMITE, Species.SNOVER, Species.CRYOGONAL, Species.SWINUB],
|
||||
VIOLA: [SpeciesId.SCATTERBUG, SpeciesId.SURSKIT, SpeciesId.CUTIEFLY, SpeciesId.BLIPBUG],
|
||||
GRANT: [SpeciesId.TYRUNT, SpeciesId.AMAURA, SpeciesId.BINACLE, SpeciesId.DWEBBLE],
|
||||
KORRINA: [SpeciesId.RIOLU, SpeciesId.MIENFOO, SpeciesId.HAWLUCHA, SpeciesId.PANCHAM],
|
||||
RAMOS: [SpeciesId.SKIDDO, SpeciesId.HOPPIP, SpeciesId.BELLSPROUT, [SpeciesId.PHANTUMP, SpeciesId.PUMPKABOO]],
|
||||
CLEMONT: [SpeciesId.HELIOPTILE, SpeciesId.MAGNEMITE, SpeciesId.DEDENNE, SpeciesId.ROTOM],
|
||||
VALERIE: [SpeciesId.SYLVEON, SpeciesId.MAWILE, SpeciesId.MR_MIME, [SpeciesId.SPRITZEE, SpeciesId.SWIRLIX]],
|
||||
OLYMPIA: [SpeciesId.ESPURR, SpeciesId.SIGILYPH, SpeciesId.INKAY, SpeciesId.SLOWKING],
|
||||
WULFRIC: [SpeciesId.BERGMITE, SpeciesId.SNOVER, SpeciesId.CRYOGONAL, SpeciesId.SWINUB],
|
||||
// Gym Leaders- Galar
|
||||
MILO: [Species.GOSSIFLEUR, Species.SEEDOT, Species.APPLIN, Species.LOTAD],
|
||||
NESSA: [Species.CHEWTLE, Species.WIMPOD, Species.ARROKUDA, Species.MAREANIE],
|
||||
KABU: [Species.SIZZLIPEDE, Species.VULPIX, Species.GROWLITHE, Species.TORKOAL],
|
||||
BEA: [Species.MACHOP, Species.GALAR_FARFETCHD, Species.CLOBBOPUS, Species.FALINKS],
|
||||
ALLISTER: [Species.GASTLY, Species.GALAR_YAMASK, Species.GALAR_CORSOLA, Species.SINISTEA],
|
||||
OPAL: [Species.MILCERY, Species.GALAR_WEEZING, Species.TOGEPI, Species.MAWILE],
|
||||
BEDE: [Species.HATENNA, Species.GALAR_PONYTA, Species.GARDEVOIR, Species.SYLVEON],
|
||||
GORDIE: [Species.ROLYCOLY, [Species.SHUCKLE, Species.BINACLE], Species.STONJOURNER, Species.LARVITAR],
|
||||
MELONY: [Species.LAPRAS, Species.SNOM, Species.EISCUE, [Species.GALAR_MR_MIME, Species.GALAR_DARUMAKA]],
|
||||
PIERS: [Species.GALAR_ZIGZAGOON, Species.SCRAGGY, Species.TOXEL, Species.INKAY], // Tera Dark Toxel
|
||||
MARNIE: [Species.IMPIDIMP, Species.MORPEKO, Species.PURRLOIN, Species.CROAGUNK], // Tera Dark Croagunk
|
||||
RAIHAN: [Species.DURALUDON, Species.TRAPINCH, Species.GOOMY, Species.TURTONATOR],
|
||||
MILO: [SpeciesId.GOSSIFLEUR, SpeciesId.SEEDOT, SpeciesId.APPLIN, SpeciesId.LOTAD],
|
||||
NESSA: [SpeciesId.CHEWTLE, SpeciesId.WIMPOD, SpeciesId.ARROKUDA, SpeciesId.MAREANIE],
|
||||
KABU: [SpeciesId.SIZZLIPEDE, SpeciesId.VULPIX, SpeciesId.GROWLITHE, SpeciesId.TORKOAL],
|
||||
BEA: [SpeciesId.MACHOP, SpeciesId.GALAR_FARFETCHD, SpeciesId.CLOBBOPUS, SpeciesId.FALINKS],
|
||||
ALLISTER: [SpeciesId.GASTLY, SpeciesId.GALAR_YAMASK, SpeciesId.GALAR_CORSOLA, SpeciesId.SINISTEA],
|
||||
OPAL: [SpeciesId.MILCERY, SpeciesId.GALAR_WEEZING, SpeciesId.TOGEPI, SpeciesId.MAWILE],
|
||||
BEDE: [SpeciesId.HATENNA, SpeciesId.GALAR_PONYTA, SpeciesId.GARDEVOIR, SpeciesId.SYLVEON],
|
||||
GORDIE: [SpeciesId.ROLYCOLY, [SpeciesId.SHUCKLE, SpeciesId.BINACLE], SpeciesId.STONJOURNER, SpeciesId.LARVITAR],
|
||||
MELONY: [SpeciesId.LAPRAS, SpeciesId.SNOM, SpeciesId.EISCUE, [SpeciesId.GALAR_MR_MIME, SpeciesId.GALAR_DARUMAKA]],
|
||||
PIERS: [SpeciesId.GALAR_ZIGZAGOON, SpeciesId.SCRAGGY, SpeciesId.TOXEL, SpeciesId.INKAY], // Tera Dark Toxel
|
||||
MARNIE: [SpeciesId.IMPIDIMP, SpeciesId.MORPEKO, SpeciesId.PURRLOIN, SpeciesId.CROAGUNK], // Tera Dark Croagunk
|
||||
RAIHAN: [SpeciesId.DURALUDON, SpeciesId.TRAPINCH, SpeciesId.GOOMY, SpeciesId.TURTONATOR],
|
||||
// Gym Leaders- Paldea; First slot is Tera
|
||||
KATY: [Species.TEDDIURSA, Species.NYMBLE, Species.TAROUNTULA, Species.RELLOR], // Tera Bug Teddiursa
|
||||
BRASSIUS: [Species.BONSLY, Species.SMOLIV, Species.BRAMBLIN, Species.SUNKERN], // Tera Grass Bonsly
|
||||
IONO: [Species.MISDREAVUS, Species.TADBULB, Species.WATTREL, Species.MAGNEMITE], // Tera Ghost Misdreavus
|
||||
KOFU: [Species.CRABRAWLER, Species.VELUZA, Species.WIGLETT, Species.WINGULL], // Tera Water Crabrawler
|
||||
LARRY: [Species.STARLY, Species.DUNSPARCE, Species.LECHONK, Species.KOMALA], // Tera Normal Starly
|
||||
RYME: [Species.TOXEL, Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU], // Tera Ghost Toxel
|
||||
TULIP: [Species.FLABEBE, Species.FLITTLE, Species.RALTS, Species.GIRAFARIG], // Tera Psychic Flabebe
|
||||
GRUSHA: [Species.SWABLU, Species.CETODDLE, Species.SNOM, Species.CUBCHOO], // Tera Ice Swablu
|
||||
KATY: [SpeciesId.TEDDIURSA, SpeciesId.NYMBLE, SpeciesId.TAROUNTULA, SpeciesId.RELLOR], // Tera Bug Teddiursa
|
||||
BRASSIUS: [SpeciesId.BONSLY, SpeciesId.SMOLIV, SpeciesId.BRAMBLIN, SpeciesId.SUNKERN], // Tera Grass Bonsly
|
||||
IONO: [SpeciesId.MISDREAVUS, SpeciesId.TADBULB, SpeciesId.WATTREL, SpeciesId.MAGNEMITE], // Tera Ghost Misdreavus
|
||||
KOFU: [SpeciesId.CRABRAWLER, SpeciesId.VELUZA, SpeciesId.WIGLETT, SpeciesId.WINGULL], // Tera Water Crabrawler
|
||||
LARRY: [SpeciesId.STARLY, SpeciesId.DUNSPARCE, SpeciesId.LECHONK, SpeciesId.KOMALA], // Tera Normal Starly
|
||||
RYME: [SpeciesId.TOXEL, SpeciesId.GREAVARD, SpeciesId.SHUPPET, SpeciesId.MIMIKYU], // Tera Ghost Toxel
|
||||
TULIP: [SpeciesId.FLABEBE, SpeciesId.FLITTLE, SpeciesId.RALTS, SpeciesId.GIRAFARIG], // Tera Psychic Flabebe
|
||||
GRUSHA: [SpeciesId.SWABLU, SpeciesId.CETODDLE, SpeciesId.SNOM, SpeciesId.CUBCHOO], // Tera Ice Swablu
|
||||
}, {
|
||||
get(target, prop: string) {
|
||||
return target[prop as keyof SignatureSpecies] ?? [];
|
||||
|
@ -1,29 +1,29 @@
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
|
||||
/**
|
||||
* A list of all {@link https://bulbapedia.bulbagarden.net/wiki/Paradox_Pok%C3%A9mon | Paradox Pokemon}, NOT including the legendaries Miraidon and Koraidon.
|
||||
*/
|
||||
export const NON_LEGEND_PARADOX_POKEMON = [
|
||||
Species.GREAT_TUSK,
|
||||
Species.SCREAM_TAIL,
|
||||
Species.BRUTE_BONNET,
|
||||
Species.FLUTTER_MANE,
|
||||
Species.SLITHER_WING,
|
||||
Species.SANDY_SHOCKS,
|
||||
Species.ROARING_MOON,
|
||||
Species.WALKING_WAKE,
|
||||
Species.GOUGING_FIRE,
|
||||
Species.RAGING_BOLT,
|
||||
Species.IRON_TREADS,
|
||||
Species.IRON_BUNDLE,
|
||||
Species.IRON_HANDS,
|
||||
Species.IRON_JUGULIS,
|
||||
Species.IRON_MOTH,
|
||||
Species.IRON_THORNS,
|
||||
Species.IRON_VALIANT,
|
||||
Species.IRON_LEAVES,
|
||||
Species.IRON_BOULDER,
|
||||
Species.IRON_CROWN,
|
||||
SpeciesId.GREAT_TUSK,
|
||||
SpeciesId.SCREAM_TAIL,
|
||||
SpeciesId.BRUTE_BONNET,
|
||||
SpeciesId.FLUTTER_MANE,
|
||||
SpeciesId.SLITHER_WING,
|
||||
SpeciesId.SANDY_SHOCKS,
|
||||
SpeciesId.ROARING_MOON,
|
||||
SpeciesId.WALKING_WAKE,
|
||||
SpeciesId.GOUGING_FIRE,
|
||||
SpeciesId.RAGING_BOLT,
|
||||
SpeciesId.IRON_TREADS,
|
||||
SpeciesId.IRON_BUNDLE,
|
||||
SpeciesId.IRON_HANDS,
|
||||
SpeciesId.IRON_JUGULIS,
|
||||
SpeciesId.IRON_MOTH,
|
||||
SpeciesId.IRON_THORNS,
|
||||
SpeciesId.IRON_VALIANT,
|
||||
SpeciesId.IRON_LEAVES,
|
||||
SpeciesId.IRON_BOULDER,
|
||||
SpeciesId.IRON_CROWN,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -32,15 +32,15 @@ export const NON_LEGEND_PARADOX_POKEMON = [
|
||||
* Note that all of these Ultra Beasts are still considered Sub-Legendary.
|
||||
*/
|
||||
export const NON_LEGEND_ULTRA_BEASTS = [
|
||||
Species.NIHILEGO,
|
||||
Species.BUZZWOLE,
|
||||
Species.PHEROMOSA,
|
||||
Species.XURKITREE,
|
||||
Species.CELESTEELA,
|
||||
Species.KARTANA,
|
||||
Species.GUZZLORD,
|
||||
Species.POIPOLE,
|
||||
Species.NAGANADEL,
|
||||
Species.STAKATAKA,
|
||||
Species.BLACEPHALON,
|
||||
SpeciesId.NIHILEGO,
|
||||
SpeciesId.BUZZWOLE,
|
||||
SpeciesId.PHEROMOSA,
|
||||
SpeciesId.XURKITREE,
|
||||
SpeciesId.CELESTEELA,
|
||||
SpeciesId.KARTANA,
|
||||
SpeciesId.GUZZLORD,
|
||||
SpeciesId.POIPOLE,
|
||||
SpeciesId.NAGANADEL,
|
||||
SpeciesId.STAKATAKA,
|
||||
SpeciesId.BLACEPHALON,
|
||||
];
|
||||
|
136105
src/data/balance/tms.ts
@ -1,111 +1,16 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove } from "./moves/move";
|
||||
import { allMoves } from "./data-lists";
|
||||
import { MoveFlags } from "#enums/MoveFlags";
|
||||
import type Pokemon from "../field/pokemon";
|
||||
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils/common";
|
||||
import type { BattlerIndex } from "../battle";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName, coerceArray } from "../utils/common";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SubstituteTag } from "./battler-tags";
|
||||
import { isNullOrUndefined } from "../utils/common";
|
||||
import Phaser from "phaser";
|
||||
import { EncounterAnim } from "#enums/encounter-anims";
|
||||
|
||||
export enum AnimFrameTarget {
|
||||
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,
|
||||
}
|
||||
import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
|
||||
export class AnimConfig {
|
||||
public id: number;
|
||||
@ -498,7 +403,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
||||
}
|
||||
}
|
||||
|
||||
export const moveAnims = new Map<Moves, AnimConfig | [AnimConfig, AnimConfig] | null>();
|
||||
export const moveAnims = new Map<MoveId, AnimConfig | [AnimConfig, AnimConfig] | null>();
|
||||
export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig] | null>();
|
||||
export const commonAnims = new Map<CommonAnim, AnimConfig>();
|
||||
export const encounterAnims = new Map<EncounterAnim, AnimConfig>();
|
||||
@ -521,7 +426,7 @@ export function initCommonAnims(): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export function initMoveAnim(move: Moves): Promise<void> {
|
||||
export function initMoveAnim(move: MoveId): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
if (moveAnims.has(move)) {
|
||||
if (moveAnims.get(move) !== null) {
|
||||
@ -531,7 +436,7 @@ export function initMoveAnim(move: Moves): Promise<void> {
|
||||
if (moveAnims.get(move) !== null) {
|
||||
const chargeAnimSource = allMoves[move].isChargingMove()
|
||||
? 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) {
|
||||
return;
|
||||
}
|
||||
@ -542,14 +447,13 @@ export function initMoveAnim(move: Moves): Promise<void> {
|
||||
}
|
||||
} else {
|
||||
moveAnims.set(move, null);
|
||||
const defaultMoveAnim =
|
||||
allMoves[move] instanceof AttackMove
|
||||
? Moves.TACKLE
|
||||
: allMoves[move] instanceof SelfStatusMove
|
||||
? Moves.FOCUS_ENERGY
|
||||
: Moves.TAIL_WHIP;
|
||||
const defaultMoveAnim = allMoves[move].is("AttackMove")
|
||||
? MoveId.TACKLE
|
||||
: allMoves[move].is("SelfStatusMove")
|
||||
? MoveId.FOCUS_ENERGY
|
||||
: MoveId.TAIL_WHIP;
|
||||
|
||||
const fetchAnimAndResolve = (move: Moves) => {
|
||||
const fetchAnimAndResolve = (move: MoveId) => {
|
||||
globalScene
|
||||
.cachedFetch(`./battle-anims/${animationFileName(move)}.json`)
|
||||
.then(response => {
|
||||
@ -570,7 +474,7 @@ export function initMoveAnim(move: Moves): Promise<void> {
|
||||
}
|
||||
const chargeAnimSource = allMoves[move].isChargingMove()
|
||||
? allMoves[move]
|
||||
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
|
||||
: (allMoves[move].getAttrs("DelayedAttackAttr")[0] ?? allMoves[move].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||
if (chargeAnimSource) {
|
||||
initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
|
||||
} else {
|
||||
@ -594,7 +498,7 @@ export function initMoveAnim(move: Moves): Promise<void> {
|
||||
* @param move the move to populate an animation for
|
||||
* @param defaultMoveAnim the move to use as the default animation
|
||||
*/
|
||||
function useDefaultAnim(move: Moves, defaultMoveAnim: Moves) {
|
||||
function useDefaultAnim(move: MoveId, defaultMoveAnim: MoveId) {
|
||||
populateMoveAnim(move, moveAnims.get(defaultMoveAnim));
|
||||
}
|
||||
|
||||
@ -606,7 +510,7 @@ function useDefaultAnim(move: Moves, defaultMoveAnim: Moves) {
|
||||
*
|
||||
* @remarks use {@linkcode useDefaultAnim} to use a default animation
|
||||
*/
|
||||
function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
|
||||
function logMissingMoveAnim(move: MoveId, ...optionalParams: any[]) {
|
||||
const moveName = animationFileName(move);
|
||||
console.warn(`Could not load animation file for move '${moveName}'`, ...optionalParams);
|
||||
}
|
||||
@ -616,7 +520,7 @@ function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
|
||||
* @param encounterAnim one or more animations to fetch
|
||||
*/
|
||||
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 encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
|
||||
for (const anim of anims) {
|
||||
@ -664,7 +568,7 @@ export function initMoveChargeAnim(chargeAnim: ChargeAnim): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function populateMoveAnim(move: Moves, animSource: any): void {
|
||||
function populateMoveAnim(move: MoveId, animSource: any): void {
|
||||
const moveAnim = new AnimConfig(animSource);
|
||||
if (moveAnims.get(move) === null) {
|
||||
moveAnims.set(move, moveAnim);
|
||||
@ -697,13 +601,13 @@ export async function loadEncounterAnimAssets(startLoad?: boolean): Promise<void
|
||||
await loadAnimAssets(Array.from(encounterAnims.values()), startLoad);
|
||||
}
|
||||
|
||||
export function loadMoveAnimAssets(moveIds: Moves[], startLoad?: boolean): Promise<void> {
|
||||
export function loadMoveAnimAssets(moveIds: MoveId[], startLoad?: boolean): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
const moveAnimations = moveIds.flatMap(m => moveAnims.get(m) as AnimConfig);
|
||||
for (const moveId of moveIds) {
|
||||
const chargeAnimSource = allMoves[moveId].isChargingMove()
|
||||
? allMoves[moveId]
|
||||
: (allMoves[moveId].getAttrs(DelayedAttackAttr)[0] ?? allMoves[moveId].getAttrs(BeakBlastHeaderAttr)[0]);
|
||||
: (allMoves[moveId].getAttrs("DelayedAttackAttr")[0] ?? allMoves[moveId].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||
if (chargeAnimSource) {
|
||||
const moveChargeAnims = chargeAnims.get(chargeAnimSource.chargeAnim);
|
||||
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims![0]); // TODO: is the bang correct?
|
||||
@ -867,7 +771,7 @@ export abstract class BattleAnim {
|
||||
const user = !isOppAnim ? this.user : this.target;
|
||||
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 userInitialY = user!.y; // TODO: is this bang correct?
|
||||
@ -1425,9 +1329,9 @@ export class CommonBattleAnim extends BattleAnim {
|
||||
}
|
||||
|
||||
export class MoveAnim extends BattleAnim {
|
||||
public move: Moves;
|
||||
public move: MoveId;
|
||||
|
||||
constructor(move: Moves, user: Pokemon, target: BattlerIndex, playOnEmptyField = false) {
|
||||
constructor(move: MoveId, user: Pokemon, target: BattlerIndex, playOnEmptyField = false) {
|
||||
// Set target to the user pokemon if no target is found to avoid crashes
|
||||
super(user, globalScene.getField()[target] ?? user, playOnEmptyField);
|
||||
|
||||
@ -1456,7 +1360,7 @@ export class MoveAnim extends BattleAnim {
|
||||
export class MoveChargeAnim extends MoveAnim {
|
||||
private chargeAnim: ChargeAnim;
|
||||
|
||||
constructor(chargeAnim: ChargeAnim, move: Moves, user: Pokemon) {
|
||||
constructor(chargeAnim: ChargeAnim, move: MoveId, user: Pokemon) {
|
||||
super(move, user, 0);
|
||||
|
||||
this.chargeAnim = chargeAnim;
|
||||
@ -1502,8 +1406,8 @@ export async function populateAnims() {
|
||||
const chargeAnimIds = getEnumValues(ChargeAnim) as ChargeAnim[];
|
||||
const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/;
|
||||
const moveNameToId = {};
|
||||
for (const move of getEnumValues(Moves).slice(1)) {
|
||||
const moveName = Moves[move].toUpperCase().replace(/\_/g, "");
|
||||
for (const move of getEnumValues(MoveId).slice(1)) {
|
||||
const moveName = MoveId[move].toUpperCase().replace(/\_/g, "");
|
||||
moveNameToId[moveName] = move;
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,13 @@
|
||||
import { getPokemonNameWithAffix } from "../messages";
|
||||
import type Pokemon from "../field/pokemon";
|
||||
import { HitResult } from "../field/pokemon";
|
||||
import { HitResult } from "#enums/hit-result";
|
||||
import { getStatusEffectHealText } from "./status-effect";
|
||||
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common";
|
||||
import {
|
||||
DoubleBerryEffectAbAttr,
|
||||
ReduceBerryUseThresholdAbAttr,
|
||||
applyAbAttrs,
|
||||
} from "./abilities/ability";
|
||||
import { applyAbAttrs } from "./abilities/apply-ab-attrs";
|
||||
import i18next from "i18next";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Stat, type BattleStat } from "#app/enums/stat";
|
||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
export function getBerryName(berryType: BerryType): string {
|
||||
@ -44,25 +38,25 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
||||
const threshold = new NumberHolder(0.25);
|
||||
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
|
||||
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;
|
||||
};
|
||||
case BerryType.LANSAT:
|
||||
return (pokemon: Pokemon) => {
|
||||
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);
|
||||
};
|
||||
case BerryType.STARF:
|
||||
return (pokemon: Pokemon) => {
|
||||
const threshold = new NumberHolder(0.25);
|
||||
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||
return pokemon.getHpRatio() < 0.25;
|
||||
};
|
||||
case BerryType.LEPPA:
|
||||
return (pokemon: Pokemon) => {
|
||||
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());
|
||||
};
|
||||
}
|
||||
@ -78,24 +72,23 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
case BerryType.ENIGMA:
|
||||
{
|
||||
const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4));
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, hpHealed);
|
||||
globalScene.unshiftPhase(
|
||||
new PokemonHealPhase(
|
||||
consumer.getBattlerIndex(),
|
||||
hpHealed.value,
|
||||
i18next.t("battle:hpHealBerry", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
|
||||
berryName: getBerryName(berryType),
|
||||
}),
|
||||
true,
|
||||
),
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, hpHealed);
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"PokemonHealPhase",
|
||||
consumer.getBattlerIndex(),
|
||||
hpHealed.value,
|
||||
i18next.t("battle:hpHealBerry", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
|
||||
berryName: getBerryName(berryType),
|
||||
}),
|
||||
true,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case BerryType.LUM:
|
||||
{
|
||||
if (consumer.status) {
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
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.
|
||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||
const statStages = new NumberHolder(1);
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, statStages);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(consumer.getBattlerIndex(), true, [stat], statStages.value),
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, statStages);
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
consumer.getBattlerIndex(),
|
||||
true,
|
||||
[stat],
|
||||
statStages.value,
|
||||
);
|
||||
}
|
||||
break;
|
||||
@ -129,9 +126,13 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
{
|
||||
const randStat = randSeedInt(Stat.SPD, Stat.ATK);
|
||||
const stages = new NumberHolder(2);
|
||||
applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, stages);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(consumer.getBattlerIndex(), true, [randStat], stages.value),
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, stages);
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
consumer.getBattlerIndex(),
|
||||
true,
|
||||
[randStat],
|
||||
stages.value,
|
||||
);
|
||||
}
|
||||
break;
|
||||
@ -144,7 +145,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
consumer.getMoveset().find(m => m.ppUsed < m.getMovePp());
|
||||
if (ppRestoreMove) {
|
||||
ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0);
|
||||
globalScene.queueMessage(
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("battle:ppHealBerry", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(consumer),
|
||||
moveName: ppRestoreMove.getName(),
|
||||
|
@ -2,115 +2,35 @@ import { BooleanHolder, type NumberHolder, randSeedItem } from "#app/utils/commo
|
||||
import { deepCopy } from "#app/utils/data";
|
||||
import i18next from "i18next";
|
||||
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 { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
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 { getRandomTrainerFunc } from "#app/battle";
|
||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||
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 { Challenges } from "#enums/challenges";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { Nature } from "#enums/nature";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
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 { pokemonFormChanges } from "./pokemon-forms";
|
||||
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 */
|
||||
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.
|
||||
*/
|
||||
@ -305,11 +225,11 @@ export abstract class Challenge {
|
||||
|
||||
/**
|
||||
* An apply function for STARTER_COST challenges. Derived classes should alter this.
|
||||
* @param _species {@link Species} The pokemon to change the cost of.
|
||||
* @param _species {@link SpeciesId} The pokemon to change the cost of.
|
||||
* @param _cost {@link NumberHolder} The cost of the starter.
|
||||
* @returns {@link boolean} Whether this function did anything.
|
||||
*/
|
||||
applyStarterCost(_species: Species, _cost: NumberHolder): boolean {
|
||||
applyStarterCost(_species: SpeciesId, _cost: NumberHolder): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -395,11 +315,11 @@ export abstract class Challenge {
|
||||
* An apply function for MOVE_ACCESS. Derived classes should alter this.
|
||||
* @param _pokemon {@link Pokemon} What pokemon would learn the move.
|
||||
* @param _moveSource {@link MoveSourceType} What source the pokemon would get the move from.
|
||||
* @param _move {@link Moves} The move in question.
|
||||
* @param _move {@link MoveId} The move in question.
|
||||
* @param _level {@link NumberHolder} The level threshold for access.
|
||||
* @returns {@link boolean} Whether this function did anything.
|
||||
*/
|
||||
applyMoveAccessLevel(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: Moves, _level: NumberHolder): boolean {
|
||||
applyMoveAccessLevel(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: MoveId, _level: NumberHolder): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -407,11 +327,11 @@ export abstract class Challenge {
|
||||
* An apply function for MOVE_WEIGHT. Derived classes should alter this.
|
||||
* @param _pokemon {@link Pokemon} What pokemon would learn the move.
|
||||
* @param _moveSource {@link MoveSourceType} What source the pokemon would get the move from.
|
||||
* @param _move {@link Moves} The move in question.
|
||||
* @param _move {@link MoveId} The move in question.
|
||||
* @param _weight {@link NumberHolder} The base weight of the move
|
||||
* @returns {@link boolean} Whether this function did anything.
|
||||
*/
|
||||
applyMoveWeight(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: Moves, _level: NumberHolder): boolean {
|
||||
applyMoveWeight(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: MoveId, _level: NumberHolder): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -696,7 +616,7 @@ export class SingleGenerationChallenge extends Challenge {
|
||||
|
||||
interface monotypeOverride {
|
||||
/** The species to override */
|
||||
species: Species;
|
||||
species: SpeciesId;
|
||||
/** The type to count as */
|
||||
type: PokemonType;
|
||||
/** If part of a fusion, should we check the fused species instead of the base species? */
|
||||
@ -708,7 +628,7 @@ interface monotypeOverride {
|
||||
*/
|
||||
export class SingleTypeChallenge extends Challenge {
|
||||
private static TYPE_OVERRIDES: monotypeOverride[] = [
|
||||
{ species: Species.CASTFORM, type: PokemonType.NORMAL, fusion: false },
|
||||
{ species: SpeciesId.CASTFORM, type: PokemonType.NORMAL, fusion: false },
|
||||
];
|
||||
// TODO: Find a solution for all Pokemon with this ssui issue, including Basculin and Burmy
|
||||
|
||||
@ -804,7 +724,7 @@ export class FreshStartChallenge extends Challenge {
|
||||
return false;
|
||||
}
|
||||
|
||||
applyStarterCost(species: Species, cost: NumberHolder): boolean {
|
||||
applyStarterCost(species: SpeciesId, cost: NumberHolder): boolean {
|
||||
if (defaultStarterSpecies.includes(species)) {
|
||||
cost.value = speciesStarterCosts[species];
|
||||
return true;
|
||||
@ -992,13 +912,13 @@ export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, poi
|
||||
/**
|
||||
* Apply all challenges that modify the cost of a starter.
|
||||
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_COST
|
||||
* @param species {@link Species} The pokemon to change the cost of.
|
||||
* @param species {@link SpeciesId} The pokemon to change the cost of.
|
||||
* @param points {@link NumberHolder} The cost of the pokemon.
|
||||
* @returns True if any challenge was successfully applied.
|
||||
*/
|
||||
export function applyChallenges(
|
||||
challengeType: ChallengeType.STARTER_COST,
|
||||
species: Species,
|
||||
species: SpeciesId,
|
||||
cost: NumberHolder,
|
||||
): boolean;
|
||||
/**
|
||||
@ -1090,7 +1010,7 @@ export function applyChallenges(challengeType: ChallengeType.GAME_MODE_MODIFY):
|
||||
* @param challengeType {@link ChallengeType} ChallengeType.MOVE_ACCESS
|
||||
* @param pokemon {@link Pokemon} What pokemon would learn the move.
|
||||
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
|
||||
* @param move {@link Moves} The move in question.
|
||||
* @param move {@link MoveId} The move in question.
|
||||
* @param level {@link NumberHolder} The level threshold for access.
|
||||
* @returns True if any challenge was successfully applied.
|
||||
*/
|
||||
@ -1098,7 +1018,7 @@ export function applyChallenges(
|
||||
challengeType: ChallengeType.MOVE_ACCESS,
|
||||
pokemon: Pokemon,
|
||||
moveSource: MoveSourceType,
|
||||
move: Moves,
|
||||
move: MoveId,
|
||||
level: NumberHolder,
|
||||
): boolean;
|
||||
/**
|
||||
@ -1106,7 +1026,7 @@ export function applyChallenges(
|
||||
* @param challengeType {@link ChallengeType} ChallengeType.MOVE_WEIGHT
|
||||
* @param pokemon {@link Pokemon} What pokemon would learn the move.
|
||||
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
|
||||
* @param move {@link Moves} The move in question.
|
||||
* @param move {@link MoveId} The move in question.
|
||||
* @param weight {@link NumberHolder} The weight of the move.
|
||||
* @returns True if any challenge was successfully applied.
|
||||
*/
|
||||
@ -1114,7 +1034,7 @@ export function applyChallenges(
|
||||
challengeType: ChallengeType.MOVE_WEIGHT,
|
||||
pokemon: Pokemon,
|
||||
moveSource: MoveSourceType,
|
||||
move: Moves,
|
||||
move: MoveId,
|
||||
weight: NumberHolder,
|
||||
): boolean;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import type { Species } from "#enums/species";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type { Starter } from "#app/ui/starter-select-ui-handler";
|
||||
@ -8,7 +8,7 @@ import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||
import { Biome } from "#app/enums/biome";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
|
||||
export interface DailyRunConfig {
|
||||
seed: number;
|
||||
@ -34,7 +34,7 @@ export function getDailyRunStarters(seed: string): Starter[] {
|
||||
for (let s = 0; s < 3; s++) {
|
||||
const offset = 6 + s * 6;
|
||||
const starterSpeciesForm = getPokemonSpeciesForm(
|
||||
Number.parseInt(seed.slice(offset, offset + 4)) as Species,
|
||||
Number.parseInt(seed.slice(offset, offset + 4)) as SpeciesId,
|
||||
Number.parseInt(seed.slice(offset + 4, offset + 6)),
|
||||
);
|
||||
starters.push(getDailyRunStarter(starterSpeciesForm, startingLevel));
|
||||
@ -50,7 +50,7 @@ export function getDailyRunStarters(seed: string): Starter[] {
|
||||
for (let c = 0; c < starterCosts.length; c++) {
|
||||
const cost = starterCosts[c];
|
||||
const costSpecies = Object.keys(speciesStarterCosts)
|
||||
.map(s => Number.parseInt(s) as Species)
|
||||
.map(s => Number.parseInt(s) as SpeciesId)
|
||||
.filter(s => speciesStarterCosts[s] === cost);
|
||||
const randPkmSpecies = getPokemonSpecies(randSeedItem(costSpecies));
|
||||
const starterSpecies = getPokemonSpecies(
|
||||
@ -102,48 +102,48 @@ interface BiomeWeights {
|
||||
// Town and End are set to 0 however
|
||||
// And some other biomes were balanced +1/-1 based on average size of the total daily.
|
||||
const dailyBiomeWeights: BiomeWeights = {
|
||||
[Biome.CAVE]: 3,
|
||||
[Biome.LAKE]: 3,
|
||||
[Biome.PLAINS]: 3,
|
||||
[Biome.SNOWY_FOREST]: 3,
|
||||
[Biome.SWAMP]: 3, // 2 -> 3
|
||||
[Biome.TALL_GRASS]: 3, // 2 -> 3
|
||||
[BiomeId.CAVE]: 3,
|
||||
[BiomeId.LAKE]: 3,
|
||||
[BiomeId.PLAINS]: 3,
|
||||
[BiomeId.SNOWY_FOREST]: 3,
|
||||
[BiomeId.SWAMP]: 3, // 2 -> 3
|
||||
[BiomeId.TALL_GRASS]: 3, // 2 -> 3
|
||||
|
||||
[Biome.ABYSS]: 2, // 3 -> 2
|
||||
[Biome.RUINS]: 2,
|
||||
[Biome.BADLANDS]: 2,
|
||||
[Biome.BEACH]: 2,
|
||||
[Biome.CONSTRUCTION_SITE]: 2,
|
||||
[Biome.DESERT]: 2,
|
||||
[Biome.DOJO]: 2, // 3 -> 2
|
||||
[Biome.FACTORY]: 2,
|
||||
[Biome.FAIRY_CAVE]: 2,
|
||||
[Biome.FOREST]: 2,
|
||||
[Biome.GRASS]: 2, // 1 -> 2
|
||||
[Biome.MEADOW]: 2,
|
||||
[Biome.MOUNTAIN]: 2, // 3 -> 2
|
||||
[Biome.SEA]: 2,
|
||||
[Biome.SEABED]: 2,
|
||||
[Biome.SLUM]: 2,
|
||||
[Biome.TEMPLE]: 2, // 3 -> 2
|
||||
[Biome.VOLCANO]: 2,
|
||||
[BiomeId.ABYSS]: 2, // 3 -> 2
|
||||
[BiomeId.RUINS]: 2,
|
||||
[BiomeId.BADLANDS]: 2,
|
||||
[BiomeId.BEACH]: 2,
|
||||
[BiomeId.CONSTRUCTION_SITE]: 2,
|
||||
[BiomeId.DESERT]: 2,
|
||||
[BiomeId.DOJO]: 2, // 3 -> 2
|
||||
[BiomeId.FACTORY]: 2,
|
||||
[BiomeId.FAIRY_CAVE]: 2,
|
||||
[BiomeId.FOREST]: 2,
|
||||
[BiomeId.GRASS]: 2, // 1 -> 2
|
||||
[BiomeId.MEADOW]: 2,
|
||||
[BiomeId.MOUNTAIN]: 2, // 3 -> 2
|
||||
[BiomeId.SEA]: 2,
|
||||
[BiomeId.SEABED]: 2,
|
||||
[BiomeId.SLUM]: 2,
|
||||
[BiomeId.TEMPLE]: 2, // 3 -> 2
|
||||
[BiomeId.VOLCANO]: 2,
|
||||
|
||||
[Biome.GRAVEYARD]: 1,
|
||||
[Biome.ICE_CAVE]: 1,
|
||||
[Biome.ISLAND]: 1,
|
||||
[Biome.JUNGLE]: 1,
|
||||
[Biome.LABORATORY]: 1,
|
||||
[Biome.METROPOLIS]: 1,
|
||||
[Biome.POWER_PLANT]: 1,
|
||||
[Biome.SPACE]: 1,
|
||||
[Biome.WASTELAND]: 1,
|
||||
[BiomeId.GRAVEYARD]: 1,
|
||||
[BiomeId.ICE_CAVE]: 1,
|
||||
[BiomeId.ISLAND]: 1,
|
||||
[BiomeId.JUNGLE]: 1,
|
||||
[BiomeId.LABORATORY]: 1,
|
||||
[BiomeId.METROPOLIS]: 1,
|
||||
[BiomeId.POWER_PLANT]: 1,
|
||||
[BiomeId.SPACE]: 1,
|
||||
[BiomeId.WASTELAND]: 1,
|
||||
|
||||
[Biome.TOWN]: 0,
|
||||
[Biome.END]: 0,
|
||||
[BiomeId.TOWN]: 0,
|
||||
[BiomeId.END]: 0,
|
||||
};
|
||||
|
||||
export function getDailyStartingBiome(): Biome {
|
||||
const biomes = getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
|
||||
export function getDailyStartingBiome(): BiomeId {
|
||||
const biomes = getEnumValues(BiomeId).filter(b => b !== BiomeId.TOWN && b !== BiomeId.END);
|
||||
|
||||
let totalWeight = 0;
|
||||
const biomeThresholds: number[] = [];
|
||||
|
@ -1,5 +1,9 @@
|
||||
import type { Ability } from "./abilities/ability-class";
|
||||
import type { ModifierTypes } from "#app/modifier/modifier-type";
|
||||
import type { Ability } from "./abilities/ability";
|
||||
import type Move from "./moves/move";
|
||||
|
||||
export const allAbilities: Ability[] = [];
|
||||
export const allMoves: Move[] = [];
|
||||
|
||||
// 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 = {
|
||||
[BattleSpec.FINAL_BOSS]: {
|
||||
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 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
|
||||
|
@ -10,7 +10,7 @@ import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import i18next from "i18next";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { EggSourceType } from "#enums/egg-source-types";
|
||||
import {
|
||||
MANAPHY_EGG_MANAPHY_RATE,
|
||||
@ -67,7 +67,7 @@ export interface IEggOptions {
|
||||
/** Sets how many waves it will take till this egg hatches. */
|
||||
hatchWaves?: number;
|
||||
/** Sets the exact species that will hatch from this egg. */
|
||||
species?: Species;
|
||||
species?: SpeciesId;
|
||||
/** Defines if the hatched pokemon will be a shiny. */
|
||||
isShiny?: boolean;
|
||||
/** Defines the variant of the pokemon that will hatch from this egg. If no `variantTier` is given the normal variant rates will apply. */
|
||||
@ -94,7 +94,7 @@ export class Egg {
|
||||
private _hatchWaves: number;
|
||||
private _timestamp: number;
|
||||
|
||||
private _species: Species;
|
||||
private _species: SpeciesId;
|
||||
private _isShiny: boolean;
|
||||
private _variantTier: VariantTier;
|
||||
private _eggMoveIndex: number;
|
||||
@ -134,7 +134,7 @@ export class Egg {
|
||||
return this._timestamp;
|
||||
}
|
||||
|
||||
get species(): Species {
|
||||
get species(): SpeciesId {
|
||||
return this._species;
|
||||
}
|
||||
|
||||
@ -221,8 +221,8 @@ export class Egg {
|
||||
|
||||
public isManaphyEgg(): boolean {
|
||||
return (
|
||||
this._species === Species.PHIONE ||
|
||||
this._species === Species.MANAPHY ||
|
||||
this._species === SpeciesId.PHIONE ||
|
||||
this._species === SpeciesId.MANAPHY ||
|
||||
(this._tier === EggTier.COMMON && !(this._id % 204) && !this._species)
|
||||
);
|
||||
}
|
||||
@ -247,8 +247,10 @@ export class Egg {
|
||||
|
||||
let pokemonSpecies = getPokemonSpecies(this._species);
|
||||
// Special condition to have Phione eggs also have a chance of generating Manaphy
|
||||
if (this._species === Species.PHIONE && this._sourceType === EggSourceType.SAME_SPECIES_EGG) {
|
||||
pokemonSpecies = getPokemonSpecies(randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? Species.PHIONE : Species.MANAPHY);
|
||||
if (this._species === SpeciesId.PHIONE && this._sourceType === EggSourceType.SAME_SPECIES_EGG) {
|
||||
pokemonSpecies = getPokemonSpecies(
|
||||
randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? SpeciesId.PHIONE : SpeciesId.MANAPHY,
|
||||
);
|
||||
}
|
||||
|
||||
// Sets the hidden ability if a hidden ability exists and
|
||||
@ -371,7 +373,7 @@ export class Egg {
|
||||
}
|
||||
|
||||
private getEggTierDefaultHatchWaves(eggTier?: EggTier): number {
|
||||
if (this._species === Species.PHIONE || this._species === Species.MANAPHY) {
|
||||
if (this._species === SpeciesId.PHIONE || this._species === SpeciesId.MANAPHY) {
|
||||
return HATCH_WAVES_MANAPHY_EGG;
|
||||
}
|
||||
|
||||
@ -399,7 +401,7 @@ export class Egg {
|
||||
: EggTier.LEGENDARY;
|
||||
}
|
||||
|
||||
private rollSpecies(): Species | null {
|
||||
private rollSpecies(): SpeciesId | null {
|
||||
if (!globalScene) {
|
||||
return null;
|
||||
}
|
||||
@ -415,7 +417,7 @@ export class Egg {
|
||||
* check pass when Utils.randSeedInt(8) = 0, we can tell them apart during tests.
|
||||
*/
|
||||
const rand = randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1;
|
||||
return rand ? Species.PHIONE : Species.MANAPHY;
|
||||
return rand ? SpeciesId.PHIONE : SpeciesId.MANAPHY;
|
||||
}
|
||||
if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY) {
|
||||
if (!randSeedInt(2)) {
|
||||
@ -445,11 +447,11 @@ export class Egg {
|
||||
break;
|
||||
}
|
||||
|
||||
const ignoredSpecies = [Species.PHIONE, Species.MANAPHY, Species.ETERNATUS];
|
||||
const ignoredSpecies = [SpeciesId.PHIONE, SpeciesId.MANAPHY, SpeciesId.ETERNATUS];
|
||||
|
||||
let speciesPool = Object.keys(speciesEggTiers)
|
||||
.filter(s => speciesEggTiers[s] === this.tier)
|
||||
.map(s => Number.parseInt(s) as Species)
|
||||
.map(s => Number.parseInt(s) as SpeciesId)
|
||||
.filter(
|
||||
s =>
|
||||
!pokemonPrevolutions.hasOwnProperty(s) &&
|
||||
@ -496,7 +498,7 @@ export class Egg {
|
||||
totalWeight += weight;
|
||||
}
|
||||
|
||||
let species: Species;
|
||||
let species: SpeciesId;
|
||||
|
||||
const rand = randSeedInt(totalWeight);
|
||||
for (let s = 0; s < speciesWeights.length; s++) {
|
||||
@ -606,17 +608,17 @@ export class Egg {
|
||||
////
|
||||
}
|
||||
|
||||
export function getValidLegendaryGachaSpecies(): Species[] {
|
||||
export function getValidLegendaryGachaSpecies(): SpeciesId[] {
|
||||
return Object.entries(speciesEggTiers)
|
||||
.filter(s => s[1] === EggTier.LEGENDARY)
|
||||
.map(s => Number.parseInt(s[0]))
|
||||
.filter(s => getPokemonSpecies(s).isObtainable() && s !== Species.ETERNATUS);
|
||||
.filter(s => getPokemonSpecies(s).isObtainable() && s !== SpeciesId.ETERNATUS);
|
||||
}
|
||||
|
||||
export function getLegendaryGachaSpeciesForTimestamp(timestamp: number): Species {
|
||||
export function getLegendaryGachaSpeciesForTimestamp(timestamp: number): SpeciesId {
|
||||
const legendarySpecies = getValidLegendaryGachaSpecies();
|
||||
|
||||
let ret: Species;
|
||||
let ret: SpeciesId;
|
||||
|
||||
// 86400000 is the number of miliseconds in one day
|
||||
const timeDate = new Date(timestamp);
|
||||
|
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,257 +1,257 @@
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
|
||||
/** Set of moves that cannot be called by {@linkcode Moves.METRONOME Metronome} */
|
||||
export const invalidMetronomeMoves: ReadonlySet<Moves> = new Set([
|
||||
Moves.AFTER_YOU,
|
||||
Moves.ASSIST,
|
||||
Moves.BANEFUL_BUNKER,
|
||||
Moves.BEAK_BLAST,
|
||||
Moves.BELCH,
|
||||
Moves.BESTOW,
|
||||
Moves.COMEUPPANCE,
|
||||
Moves.COPYCAT,
|
||||
Moves.COUNTER,
|
||||
Moves.CRAFTY_SHIELD,
|
||||
Moves.DESTINY_BOND,
|
||||
Moves.DETECT,
|
||||
Moves.ENDURE,
|
||||
Moves.FEINT,
|
||||
Moves.FOCUS_PUNCH,
|
||||
Moves.FOLLOW_ME,
|
||||
Moves.HELPING_HAND,
|
||||
Moves.INSTRUCT,
|
||||
Moves.KINGS_SHIELD,
|
||||
Moves.MAT_BLOCK,
|
||||
Moves.ME_FIRST,
|
||||
Moves.METRONOME,
|
||||
Moves.MIMIC,
|
||||
Moves.MIRROR_COAT,
|
||||
Moves.MIRROR_MOVE,
|
||||
Moves.OBSTRUCT,
|
||||
Moves.PROTECT,
|
||||
Moves.QUASH,
|
||||
Moves.QUICK_GUARD,
|
||||
Moves.RAGE_POWDER,
|
||||
Moves.REVIVAL_BLESSING,
|
||||
Moves.SHELL_TRAP,
|
||||
Moves.SILK_TRAP,
|
||||
Moves.SKETCH,
|
||||
Moves.SLEEP_TALK,
|
||||
Moves.SNATCH,
|
||||
Moves.SNORE,
|
||||
Moves.SPIKY_SHIELD,
|
||||
Moves.SPOTLIGHT,
|
||||
Moves.STRUGGLE,
|
||||
Moves.TRANSFORM,
|
||||
Moves.WIDE_GUARD,
|
||||
/** Set of moves that cannot be called by {@linkcode MoveId.METRONOME Metronome} */
|
||||
export const invalidMetronomeMoves: ReadonlySet<MoveId> = new Set([
|
||||
MoveId.AFTER_YOU,
|
||||
MoveId.ASSIST,
|
||||
MoveId.BANEFUL_BUNKER,
|
||||
MoveId.BEAK_BLAST,
|
||||
MoveId.BELCH,
|
||||
MoveId.BESTOW,
|
||||
MoveId.COMEUPPANCE,
|
||||
MoveId.COPYCAT,
|
||||
MoveId.COUNTER,
|
||||
MoveId.CRAFTY_SHIELD,
|
||||
MoveId.DESTINY_BOND,
|
||||
MoveId.DETECT,
|
||||
MoveId.ENDURE,
|
||||
MoveId.FEINT,
|
||||
MoveId.FOCUS_PUNCH,
|
||||
MoveId.FOLLOW_ME,
|
||||
MoveId.HELPING_HAND,
|
||||
MoveId.INSTRUCT,
|
||||
MoveId.KINGS_SHIELD,
|
||||
MoveId.MAT_BLOCK,
|
||||
MoveId.ME_FIRST,
|
||||
MoveId.METRONOME,
|
||||
MoveId.MIMIC,
|
||||
MoveId.MIRROR_COAT,
|
||||
MoveId.MIRROR_MOVE,
|
||||
MoveId.OBSTRUCT,
|
||||
MoveId.PROTECT,
|
||||
MoveId.QUASH,
|
||||
MoveId.QUICK_GUARD,
|
||||
MoveId.RAGE_POWDER,
|
||||
MoveId.REVIVAL_BLESSING,
|
||||
MoveId.SHELL_TRAP,
|
||||
MoveId.SILK_TRAP,
|
||||
MoveId.SKETCH,
|
||||
MoveId.SLEEP_TALK,
|
||||
MoveId.SNATCH,
|
||||
MoveId.SNORE,
|
||||
MoveId.SPIKY_SHIELD,
|
||||
MoveId.SPOTLIGHT,
|
||||
MoveId.STRUGGLE,
|
||||
MoveId.TRANSFORM,
|
||||
MoveId.WIDE_GUARD,
|
||||
]);
|
||||
|
||||
/** Set of moves that cannot be called by {@linkcode Moves.ASSIST Assist} */
|
||||
export const invalidAssistMoves: ReadonlySet<Moves> = new Set([
|
||||
Moves.ASSIST,
|
||||
Moves.BANEFUL_BUNKER,
|
||||
Moves.BEAK_BLAST,
|
||||
Moves.BELCH,
|
||||
Moves.BESTOW,
|
||||
Moves.BOUNCE,
|
||||
Moves.CELEBRATE,
|
||||
Moves.CHATTER,
|
||||
Moves.CIRCLE_THROW,
|
||||
Moves.COPYCAT,
|
||||
Moves.COUNTER,
|
||||
Moves.DESTINY_BOND,
|
||||
Moves.DETECT,
|
||||
Moves.DIG,
|
||||
Moves.DIVE,
|
||||
Moves.DRAGON_TAIL,
|
||||
Moves.ENDURE,
|
||||
Moves.FEINT,
|
||||
Moves.FLY,
|
||||
Moves.FOCUS_PUNCH,
|
||||
Moves.FOLLOW_ME,
|
||||
Moves.HELPING_HAND,
|
||||
Moves.HOLD_HANDS,
|
||||
Moves.KINGS_SHIELD,
|
||||
Moves.MAT_BLOCK,
|
||||
Moves.ME_FIRST,
|
||||
Moves.METRONOME,
|
||||
Moves.MIMIC,
|
||||
Moves.MIRROR_COAT,
|
||||
Moves.MIRROR_MOVE,
|
||||
Moves.NATURE_POWER,
|
||||
Moves.PHANTOM_FORCE,
|
||||
Moves.PROTECT,
|
||||
Moves.RAGE_POWDER,
|
||||
Moves.ROAR,
|
||||
Moves.SHADOW_FORCE,
|
||||
Moves.SHELL_TRAP,
|
||||
Moves.SKETCH,
|
||||
Moves.SKY_DROP,
|
||||
Moves.SLEEP_TALK,
|
||||
Moves.SNATCH,
|
||||
Moves.SPIKY_SHIELD,
|
||||
Moves.SPOTLIGHT,
|
||||
Moves.STRUGGLE,
|
||||
Moves.SWITCHEROO,
|
||||
Moves.TRANSFORM,
|
||||
Moves.TRICK,
|
||||
Moves.WHIRLWIND,
|
||||
/** Set of moves that cannot be called by {@linkcode MoveId.ASSIST Assist} */
|
||||
export const invalidAssistMoves: ReadonlySet<MoveId> = new Set([
|
||||
MoveId.ASSIST,
|
||||
MoveId.BANEFUL_BUNKER,
|
||||
MoveId.BEAK_BLAST,
|
||||
MoveId.BELCH,
|
||||
MoveId.BESTOW,
|
||||
MoveId.BOUNCE,
|
||||
MoveId.CELEBRATE,
|
||||
MoveId.CHATTER,
|
||||
MoveId.CIRCLE_THROW,
|
||||
MoveId.COPYCAT,
|
||||
MoveId.COUNTER,
|
||||
MoveId.DESTINY_BOND,
|
||||
MoveId.DETECT,
|
||||
MoveId.DIG,
|
||||
MoveId.DIVE,
|
||||
MoveId.DRAGON_TAIL,
|
||||
MoveId.ENDURE,
|
||||
MoveId.FEINT,
|
||||
MoveId.FLY,
|
||||
MoveId.FOCUS_PUNCH,
|
||||
MoveId.FOLLOW_ME,
|
||||
MoveId.HELPING_HAND,
|
||||
MoveId.HOLD_HANDS,
|
||||
MoveId.KINGS_SHIELD,
|
||||
MoveId.MAT_BLOCK,
|
||||
MoveId.ME_FIRST,
|
||||
MoveId.METRONOME,
|
||||
MoveId.MIMIC,
|
||||
MoveId.MIRROR_COAT,
|
||||
MoveId.MIRROR_MOVE,
|
||||
MoveId.NATURE_POWER,
|
||||
MoveId.PHANTOM_FORCE,
|
||||
MoveId.PROTECT,
|
||||
MoveId.RAGE_POWDER,
|
||||
MoveId.ROAR,
|
||||
MoveId.SHADOW_FORCE,
|
||||
MoveId.SHELL_TRAP,
|
||||
MoveId.SKETCH,
|
||||
MoveId.SKY_DROP,
|
||||
MoveId.SLEEP_TALK,
|
||||
MoveId.SNATCH,
|
||||
MoveId.SPIKY_SHIELD,
|
||||
MoveId.SPOTLIGHT,
|
||||
MoveId.STRUGGLE,
|
||||
MoveId.SWITCHEROO,
|
||||
MoveId.TRANSFORM,
|
||||
MoveId.TRICK,
|
||||
MoveId.WHIRLWIND,
|
||||
]);
|
||||
|
||||
/** Set of moves that cannot be called by {@linkcode Moves.SLEEP_TALK Sleep Talk} */
|
||||
export const invalidSleepTalkMoves: ReadonlySet<Moves> = new Set([
|
||||
Moves.ASSIST,
|
||||
Moves.BELCH,
|
||||
Moves.BEAK_BLAST,
|
||||
Moves.BIDE,
|
||||
Moves.BOUNCE,
|
||||
Moves.COPYCAT,
|
||||
Moves.DIG,
|
||||
Moves.DIVE,
|
||||
Moves.FREEZE_SHOCK,
|
||||
Moves.FLY,
|
||||
Moves.FOCUS_PUNCH,
|
||||
Moves.GEOMANCY,
|
||||
Moves.ICE_BURN,
|
||||
Moves.ME_FIRST,
|
||||
Moves.METRONOME,
|
||||
Moves.MIRROR_MOVE,
|
||||
Moves.MIMIC,
|
||||
Moves.PHANTOM_FORCE,
|
||||
Moves.RAZOR_WIND,
|
||||
Moves.SHADOW_FORCE,
|
||||
Moves.SHELL_TRAP,
|
||||
Moves.SKETCH,
|
||||
Moves.SKULL_BASH,
|
||||
Moves.SKY_ATTACK,
|
||||
Moves.SKY_DROP,
|
||||
Moves.SLEEP_TALK,
|
||||
Moves.SOLAR_BLADE,
|
||||
Moves.SOLAR_BEAM,
|
||||
Moves.STRUGGLE,
|
||||
Moves.UPROAR,
|
||||
/** Set of moves that cannot be called by {@linkcode MoveId.SLEEP_TALK Sleep Talk} */
|
||||
export const invalidSleepTalkMoves: ReadonlySet<MoveId> = new Set([
|
||||
MoveId.ASSIST,
|
||||
MoveId.BELCH,
|
||||
MoveId.BEAK_BLAST,
|
||||
MoveId.BIDE,
|
||||
MoveId.BOUNCE,
|
||||
MoveId.COPYCAT,
|
||||
MoveId.DIG,
|
||||
MoveId.DIVE,
|
||||
MoveId.FREEZE_SHOCK,
|
||||
MoveId.FLY,
|
||||
MoveId.FOCUS_PUNCH,
|
||||
MoveId.GEOMANCY,
|
||||
MoveId.ICE_BURN,
|
||||
MoveId.ME_FIRST,
|
||||
MoveId.METRONOME,
|
||||
MoveId.MIRROR_MOVE,
|
||||
MoveId.MIMIC,
|
||||
MoveId.PHANTOM_FORCE,
|
||||
MoveId.RAZOR_WIND,
|
||||
MoveId.SHADOW_FORCE,
|
||||
MoveId.SHELL_TRAP,
|
||||
MoveId.SKETCH,
|
||||
MoveId.SKULL_BASH,
|
||||
MoveId.SKY_ATTACK,
|
||||
MoveId.SKY_DROP,
|
||||
MoveId.SLEEP_TALK,
|
||||
MoveId.SOLAR_BLADE,
|
||||
MoveId.SOLAR_BEAM,
|
||||
MoveId.STRUGGLE,
|
||||
MoveId.UPROAR,
|
||||
]);
|
||||
|
||||
/** Set of moves that cannot be copied by {@linkcode Moves.COPYCAT Copycat} */
|
||||
export const invalidCopycatMoves: ReadonlySet<Moves> = new Set([
|
||||
Moves.ASSIST,
|
||||
Moves.BANEFUL_BUNKER,
|
||||
Moves.BEAK_BLAST,
|
||||
Moves.BESTOW,
|
||||
Moves.CELEBRATE,
|
||||
Moves.CHATTER,
|
||||
Moves.CIRCLE_THROW,
|
||||
Moves.COPYCAT,
|
||||
Moves.COUNTER,
|
||||
Moves.DESTINY_BOND,
|
||||
Moves.DETECT,
|
||||
Moves.DRAGON_TAIL,
|
||||
Moves.ENDURE,
|
||||
Moves.FEINT,
|
||||
Moves.FOCUS_PUNCH,
|
||||
Moves.FOLLOW_ME,
|
||||
Moves.HELPING_HAND,
|
||||
Moves.HOLD_HANDS,
|
||||
Moves.KINGS_SHIELD,
|
||||
Moves.MAT_BLOCK,
|
||||
Moves.ME_FIRST,
|
||||
Moves.METRONOME,
|
||||
Moves.MIMIC,
|
||||
Moves.MIRROR_COAT,
|
||||
Moves.MIRROR_MOVE,
|
||||
Moves.PROTECT,
|
||||
Moves.RAGE_POWDER,
|
||||
Moves.ROAR,
|
||||
Moves.SHELL_TRAP,
|
||||
Moves.SKETCH,
|
||||
Moves.SLEEP_TALK,
|
||||
Moves.SNATCH,
|
||||
Moves.SPIKY_SHIELD,
|
||||
Moves.SPOTLIGHT,
|
||||
Moves.STRUGGLE,
|
||||
Moves.SWITCHEROO,
|
||||
Moves.TRANSFORM,
|
||||
Moves.TRICK,
|
||||
Moves.WHIRLWIND,
|
||||
/** Set of moves that cannot be copied by {@linkcode MoveId.COPYCAT Copycat} */
|
||||
export const invalidCopycatMoves: ReadonlySet<MoveId> = new Set([
|
||||
MoveId.ASSIST,
|
||||
MoveId.BANEFUL_BUNKER,
|
||||
MoveId.BEAK_BLAST,
|
||||
MoveId.BESTOW,
|
||||
MoveId.CELEBRATE,
|
||||
MoveId.CHATTER,
|
||||
MoveId.CIRCLE_THROW,
|
||||
MoveId.COPYCAT,
|
||||
MoveId.COUNTER,
|
||||
MoveId.DESTINY_BOND,
|
||||
MoveId.DETECT,
|
||||
MoveId.DRAGON_TAIL,
|
||||
MoveId.ENDURE,
|
||||
MoveId.FEINT,
|
||||
MoveId.FOCUS_PUNCH,
|
||||
MoveId.FOLLOW_ME,
|
||||
MoveId.HELPING_HAND,
|
||||
MoveId.HOLD_HANDS,
|
||||
MoveId.KINGS_SHIELD,
|
||||
MoveId.MAT_BLOCK,
|
||||
MoveId.ME_FIRST,
|
||||
MoveId.METRONOME,
|
||||
MoveId.MIMIC,
|
||||
MoveId.MIRROR_COAT,
|
||||
MoveId.MIRROR_MOVE,
|
||||
MoveId.PROTECT,
|
||||
MoveId.RAGE_POWDER,
|
||||
MoveId.ROAR,
|
||||
MoveId.SHELL_TRAP,
|
||||
MoveId.SKETCH,
|
||||
MoveId.SLEEP_TALK,
|
||||
MoveId.SNATCH,
|
||||
MoveId.SPIKY_SHIELD,
|
||||
MoveId.SPOTLIGHT,
|
||||
MoveId.STRUGGLE,
|
||||
MoveId.SWITCHEROO,
|
||||
MoveId.TRANSFORM,
|
||||
MoveId.TRICK,
|
||||
MoveId.WHIRLWIND,
|
||||
]);
|
||||
|
||||
export const invalidMirrorMoveMoves: ReadonlySet<Moves> = new Set([
|
||||
Moves.ACUPRESSURE,
|
||||
Moves.AFTER_YOU,
|
||||
Moves.AROMATIC_MIST,
|
||||
Moves.BEAK_BLAST,
|
||||
Moves.BELCH,
|
||||
Moves.CHILLY_RECEPTION,
|
||||
Moves.COACHING,
|
||||
Moves.CONVERSION_2,
|
||||
Moves.COUNTER,
|
||||
Moves.CRAFTY_SHIELD,
|
||||
Moves.CURSE,
|
||||
Moves.DECORATE,
|
||||
Moves.DOODLE,
|
||||
Moves.DOOM_DESIRE,
|
||||
Moves.DRAGON_CHEER,
|
||||
Moves.ELECTRIC_TERRAIN,
|
||||
Moves.FINAL_GAMBIT,
|
||||
Moves.FLORAL_HEALING,
|
||||
Moves.FLOWER_SHIELD,
|
||||
Moves.FOCUS_PUNCH,
|
||||
Moves.FUTURE_SIGHT,
|
||||
Moves.GEAR_UP,
|
||||
Moves.GRASSY_TERRAIN,
|
||||
Moves.GRAVITY,
|
||||
Moves.GUARD_SPLIT,
|
||||
Moves.HAIL,
|
||||
Moves.HAZE,
|
||||
Moves.HEAL_PULSE,
|
||||
Moves.HELPING_HAND,
|
||||
Moves.HOLD_HANDS,
|
||||
Moves.INSTRUCT,
|
||||
Moves.ION_DELUGE,
|
||||
Moves.MAGNETIC_FLUX,
|
||||
Moves.MAT_BLOCK,
|
||||
Moves.ME_FIRST,
|
||||
Moves.MIMIC,
|
||||
Moves.MIRROR_COAT,
|
||||
Moves.MIRROR_MOVE,
|
||||
Moves.MIST,
|
||||
Moves.MISTY_TERRAIN,
|
||||
Moves.MUD_SPORT,
|
||||
Moves.PERISH_SONG,
|
||||
Moves.POWER_SPLIT,
|
||||
Moves.PSYCH_UP,
|
||||
Moves.PSYCHIC_TERRAIN,
|
||||
Moves.PURIFY,
|
||||
Moves.QUICK_GUARD,
|
||||
Moves.RAIN_DANCE,
|
||||
Moves.REFLECT_TYPE,
|
||||
Moves.ROLE_PLAY,
|
||||
Moves.ROTOTILLER,
|
||||
Moves.SANDSTORM,
|
||||
Moves.SHELL_TRAP,
|
||||
Moves.SKETCH,
|
||||
Moves.SNOWSCAPE,
|
||||
Moves.SPIT_UP,
|
||||
Moves.SPOTLIGHT,
|
||||
Moves.STRUGGLE,
|
||||
Moves.SUNNY_DAY,
|
||||
Moves.TEATIME,
|
||||
Moves.TRANSFORM,
|
||||
Moves.WATER_SPORT,
|
||||
Moves.WIDE_GUARD,
|
||||
export const invalidMirrorMoveMoves: ReadonlySet<MoveId> = new Set([
|
||||
MoveId.ACUPRESSURE,
|
||||
MoveId.AFTER_YOU,
|
||||
MoveId.AROMATIC_MIST,
|
||||
MoveId.BEAK_BLAST,
|
||||
MoveId.BELCH,
|
||||
MoveId.CHILLY_RECEPTION,
|
||||
MoveId.COACHING,
|
||||
MoveId.CONVERSION_2,
|
||||
MoveId.COUNTER,
|
||||
MoveId.CRAFTY_SHIELD,
|
||||
MoveId.CURSE,
|
||||
MoveId.DECORATE,
|
||||
MoveId.DOODLE,
|
||||
MoveId.DOOM_DESIRE,
|
||||
MoveId.DRAGON_CHEER,
|
||||
MoveId.ELECTRIC_TERRAIN,
|
||||
MoveId.FINAL_GAMBIT,
|
||||
MoveId.FLORAL_HEALING,
|
||||
MoveId.FLOWER_SHIELD,
|
||||
MoveId.FOCUS_PUNCH,
|
||||
MoveId.FUTURE_SIGHT,
|
||||
MoveId.GEAR_UP,
|
||||
MoveId.GRASSY_TERRAIN,
|
||||
MoveId.GRAVITY,
|
||||
MoveId.GUARD_SPLIT,
|
||||
MoveId.HAIL,
|
||||
MoveId.HAZE,
|
||||
MoveId.HEAL_PULSE,
|
||||
MoveId.HELPING_HAND,
|
||||
MoveId.HOLD_HANDS,
|
||||
MoveId.INSTRUCT,
|
||||
MoveId.ION_DELUGE,
|
||||
MoveId.MAGNETIC_FLUX,
|
||||
MoveId.MAT_BLOCK,
|
||||
MoveId.ME_FIRST,
|
||||
MoveId.MIMIC,
|
||||
MoveId.MIRROR_COAT,
|
||||
MoveId.MIRROR_MOVE,
|
||||
MoveId.MIST,
|
||||
MoveId.MISTY_TERRAIN,
|
||||
MoveId.MUD_SPORT,
|
||||
MoveId.PERISH_SONG,
|
||||
MoveId.POWER_SPLIT,
|
||||
MoveId.PSYCH_UP,
|
||||
MoveId.PSYCHIC_TERRAIN,
|
||||
MoveId.PURIFY,
|
||||
MoveId.QUICK_GUARD,
|
||||
MoveId.RAIN_DANCE,
|
||||
MoveId.REFLECT_TYPE,
|
||||
MoveId.ROLE_PLAY,
|
||||
MoveId.ROTOTILLER,
|
||||
MoveId.SANDSTORM,
|
||||
MoveId.SHELL_TRAP,
|
||||
MoveId.SKETCH,
|
||||
MoveId.SNOWSCAPE,
|
||||
MoveId.SPIT_UP,
|
||||
MoveId.SPOTLIGHT,
|
||||
MoveId.STRUGGLE,
|
||||
MoveId.SUNNY_DAY,
|
||||
MoveId.TEATIME,
|
||||
MoveId.TRANSFORM,
|
||||
MoveId.WATER_SPORT,
|
||||
MoveId.WIDE_GUARD,
|
||||
]);
|
||||
|
||||
/** Set of moves that can never have their type overridden by an ability like Pixilate or Normalize
|
||||
*
|
||||
* Excludes tera blast and tera starstorm, as these are only conditionally forbidden
|
||||
*/
|
||||
export const noAbilityTypeOverrideMoves: ReadonlySet<Moves> = new Set([
|
||||
Moves.WEATHER_BALL,
|
||||
Moves.JUDGMENT,
|
||||
Moves.REVELATION_DANCE,
|
||||
Moves.MULTI_ATTACK,
|
||||
Moves.TERRAIN_PULSE,
|
||||
Moves.NATURAL_GIFT,
|
||||
Moves.TECHNO_BLAST,
|
||||
Moves.HIDDEN_POWER,
|
||||
export const noAbilityTypeOverrideMoves: ReadonlySet<MoveId> = new Set([
|
||||
MoveId.WEATHER_BALL,
|
||||
MoveId.JUDGMENT,
|
||||
MoveId.REVELATION_DANCE,
|
||||
MoveId.MULTI_ATTACK,
|
||||
MoveId.TERRAIN_PULSE,
|
||||
MoveId.NATURAL_GIFT,
|
||||
MoveId.TECHNO_BLAST,
|
||||
MoveId.HIDDEN_POWER,
|
||||
]);
|
||||
|
@ -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 { 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
|
||||
@ -18,3 +27,88 @@ export function isFieldTargeted(move: Move): boolean {
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
93
src/data/moves/pokemon-move.ts
Normal file
@ -0,0 +1,93 @@
|
||||
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;
|
||||
public virtual: boolean;
|
||||
|
||||
/**
|
||||
* 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, virtual = false, maxPpOverride?: number) {
|
||||
this.moveId = moveId;
|
||||
this.ppUsed = ppUsed;
|
||||
this.ppUp = ppUp;
|
||||
this.virtual = virtual;
|
||||
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 {
|
||||
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.virtual, source.maxPpOverride);
|
||||
}
|
||||
}
|
@ -12,16 +12,15 @@ import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounte
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
import i18next from "i18next";
|
||||
import type { IEggOptions } from "#app/data/egg";
|
||||
import { EggSourceType } from "#enums/egg-source-types";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { ModifierTier } from "#enums/modifier-tier";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
@ -54,27 +53,27 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
switch (randSeedInt(5)) {
|
||||
case 1:
|
||||
trainerType = TrainerType.CHERYL;
|
||||
spriteKeys = getSpriteKeysFromSpecies(Species.BLISSEY);
|
||||
spriteKeys = getSpriteKeysFromSpecies(SpeciesId.BLISSEY);
|
||||
trainerNameKey = "cheryl";
|
||||
break;
|
||||
case 2:
|
||||
trainerType = TrainerType.MARLEY;
|
||||
spriteKeys = getSpriteKeysFromSpecies(Species.ARCANINE);
|
||||
spriteKeys = getSpriteKeysFromSpecies(SpeciesId.ARCANINE);
|
||||
trainerNameKey = "marley";
|
||||
break;
|
||||
case 3:
|
||||
trainerType = TrainerType.MIRA;
|
||||
spriteKeys = getSpriteKeysFromSpecies(Species.ALAKAZAM, false, 1);
|
||||
spriteKeys = getSpriteKeysFromSpecies(SpeciesId.ALAKAZAM, false, 1);
|
||||
trainerNameKey = "mira";
|
||||
break;
|
||||
case 4:
|
||||
trainerType = TrainerType.RILEY;
|
||||
spriteKeys = getSpriteKeysFromSpecies(Species.LUCARIO, false, 1);
|
||||
spriteKeys = getSpriteKeysFromSpecies(SpeciesId.LUCARIO, false, 1);
|
||||
trainerNameKey = "riley";
|
||||
break;
|
||||
default:
|
||||
trainerType = TrainerType.BUCK;
|
||||
spriteKeys = getSpriteKeysFromSpecies(Species.CLAYDOL);
|
||||
spriteKeys = getSpriteKeysFromSpecies(SpeciesId.CLAYDOL);
|
||||
trainerNameKey = "buck";
|
||||
break;
|
||||
}
|
||||
@ -182,7 +181,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
async () => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Full heal party
|
||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
||||
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
|
||||
|
||||
const eggOptions: IEggOptions = {
|
||||
pulled: false,
|
||||
|
@ -7,11 +7,12 @@ import {
|
||||
transitionMysteryEncounterIntroVisuals,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
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 { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -22,10 +23,10 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { BerryModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { randInt } from "#app/utils/common";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import {
|
||||
applyModifierTypeToPlayerPokemon,
|
||||
catchPokemon,
|
||||
@ -33,9 +34,8 @@ import {
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
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 { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import i18next from "i18next";
|
||||
|
||||
@ -59,7 +59,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
// This sprite has the shadow
|
||||
spriteKey: "",
|
||||
fileRoot: "",
|
||||
species: Species.GREEDENT,
|
||||
species: SpeciesId.GREEDENT,
|
||||
hasShadow: true,
|
||||
alpha: 0.001,
|
||||
repeat: true,
|
||||
@ -68,7 +68,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
{
|
||||
spriteKey: "",
|
||||
fileRoot: "",
|
||||
species: Species.GREEDENT,
|
||||
species: SpeciesId.GREEDENT,
|
||||
hasShadow: false,
|
||||
repeat: true,
|
||||
x: -5,
|
||||
@ -228,17 +228,21 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
levelAdditiveModifier: 1,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.GREEDENT),
|
||||
species: getPokemonSpecies(SpeciesId.GREEDENT),
|
||||
isBoss: true,
|
||||
bossSegments: 3,
|
||||
shiny: false, // Shiny lock because of consistency issues between the different options
|
||||
moveSet: [Moves.THRASH, Moves.CRUNCH, Moves.BODY_PRESS, Moves.SLACK_OFF],
|
||||
moveSet: [MoveId.THRASH, MoveId.CRUNCH, MoveId.BODY_PRESS, MoveId.SLACK_OFF],
|
||||
modifierConfigs: bossModifierConfigs,
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
statChangesForBattle,
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -246,7 +250,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
};
|
||||
|
||||
encounter.enemyPartyConfigs = [config];
|
||||
encounter.setDialogueToken("greedentName", getPokemonSpecies(Species.GREEDENT).getName());
|
||||
encounter.setDialogueToken("greedentName", getPokemonSpecies(SpeciesId.GREEDENT).getName());
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -302,7 +306,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
encounter.startOfBattleEffects.push({
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.ENEMY],
|
||||
move: new PokemonMove(Moves.STUFF_CHEEKS),
|
||||
move: new PokemonMove(MoveId.STUFF_CHEEKS),
|
||||
ignorePp: true,
|
||||
});
|
||||
|
||||
@ -373,12 +377,12 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
// Let it have the food
|
||||
// Greedent joins the team, level equal to 2 below highest party member (shiny locked)
|
||||
const level = getHighestLevelPlayerPokemon(false, true).level - 2;
|
||||
const greedent = new EnemyPokemon(getPokemonSpecies(Species.GREEDENT), level, TrainerSlot.NONE, false, true);
|
||||
const greedent = new EnemyPokemon(getPokemonSpecies(SpeciesId.GREEDENT), level, TrainerSlot.NONE, false, true);
|
||||
greedent.moveset = [
|
||||
new PokemonMove(Moves.THRASH),
|
||||
new PokemonMove(Moves.BODY_PRESS),
|
||||
new PokemonMove(Moves.STUFF_CHEEKS),
|
||||
new PokemonMove(Moves.SLACK_OFF),
|
||||
new PokemonMove(MoveId.THRASH),
|
||||
new PokemonMove(MoveId.BODY_PRESS),
|
||||
new PokemonMove(MoveId.STUFF_CHEEKS),
|
||||
new PokemonMove(MoveId.SLACK_OFF),
|
||||
];
|
||||
greedent.passive = true;
|
||||
|
||||
|
@ -4,9 +4,9 @@ import {
|
||||
setEncounterExp,
|
||||
updatePlayerMoney,
|
||||
} 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 { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -22,7 +22,6 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
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 i18next from "i18next";
|
||||
|
||||
@ -50,7 +49,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
|
||||
.withScenePartySizeRequirement(2, 6, true) // Must have at least 2 pokemon in party
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: Species.LIEPARD.toString(),
|
||||
spriteKey: SpeciesId.LIEPARD.toString(),
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
@ -112,7 +111,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
|
||||
|
||||
const shinyCharm = generateModifierType(modifierTypes.SHINY_CHARM);
|
||||
encounter.setDialogueToken("itemName", shinyCharm?.name ?? i18next.t("modifierType:ModifierType.SHINY_CHARM.name"));
|
||||
encounter.setDialogueToken("liepardName", getPokemonSpecies(Species.LIEPARD).getName());
|
||||
encounter.setDialogueToken("liepardName", getPokemonSpecies(SpeciesId.LIEPARD).getName());
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -137,7 +136,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
|
||||
})
|
||||
.withOptionPhase(async () => {
|
||||
// Give the player a Shiny Charm
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.SHINY_CHARM);
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
.build(),
|
||||
@ -167,7 +166,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
|
||||
// Update money and remove pokemon from party
|
||||
updatePlayerMoney(encounter.misc.price);
|
||||
|
||||
setEncounterExp(encounter.options[1].primaryPokemon!.id, getPokemonSpecies(Species.LIEPARD).baseExp, true);
|
||||
setEncounterExp(encounter.options[1].primaryPokemon!.id, getPokemonSpecies(SpeciesId.LIEPARD).baseExp, true);
|
||||
|
||||
leaveEncounterWithoutBattle(true);
|
||||
})
|
||||
|
@ -12,7 +12,9 @@ import {
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
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 { BattlerTagType } from "#enums/battler-tag-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 { BerryType } from "#enums/berry-type";
|
||||
import { PERMANENT_STATS, Stat } from "#enums/stat";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
/** 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].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.2.boss_enraged`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
statChangesForBattle,
|
||||
1,
|
||||
);
|
||||
};
|
||||
setEncounterRewards(
|
||||
|
@ -21,13 +21,12 @@ import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounte
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import type { PlayerPokemon } 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 { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
@ -39,7 +38,7 @@ import {
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { PokemonType } from "#enums/pokemon-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 {
|
||||
AttackTypeBoosterModifier,
|
||||
@ -51,7 +50,7 @@ import {
|
||||
import i18next from "i18next";
|
||||
import MoveInfoOverlay from "#app/ui/move-info-overlay";
|
||||
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 { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
|
||||
@ -59,110 +58,116 @@ import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/enc
|
||||
const namespace = "mysteryEncounters/bugTypeSuperfan";
|
||||
|
||||
const POOL_1_POKEMON = [
|
||||
Species.PARASECT,
|
||||
Species.VENOMOTH,
|
||||
Species.LEDIAN,
|
||||
Species.ARIADOS,
|
||||
Species.YANMA,
|
||||
Species.BEAUTIFLY,
|
||||
Species.DUSTOX,
|
||||
Species.MASQUERAIN,
|
||||
Species.NINJASK,
|
||||
Species.VOLBEAT,
|
||||
Species.ILLUMISE,
|
||||
Species.ANORITH,
|
||||
Species.KRICKETUNE,
|
||||
Species.WORMADAM,
|
||||
Species.MOTHIM,
|
||||
Species.SKORUPI,
|
||||
Species.JOLTIK,
|
||||
Species.LARVESTA,
|
||||
Species.VIVILLON,
|
||||
Species.CHARJABUG,
|
||||
Species.RIBOMBEE,
|
||||
Species.SPIDOPS,
|
||||
Species.LOKIX,
|
||||
SpeciesId.PARASECT,
|
||||
SpeciesId.VENOMOTH,
|
||||
SpeciesId.LEDIAN,
|
||||
SpeciesId.ARIADOS,
|
||||
SpeciesId.YANMA,
|
||||
SpeciesId.BEAUTIFLY,
|
||||
SpeciesId.DUSTOX,
|
||||
SpeciesId.MASQUERAIN,
|
||||
SpeciesId.NINJASK,
|
||||
SpeciesId.VOLBEAT,
|
||||
SpeciesId.ILLUMISE,
|
||||
SpeciesId.ANORITH,
|
||||
SpeciesId.KRICKETUNE,
|
||||
SpeciesId.WORMADAM,
|
||||
SpeciesId.MOTHIM,
|
||||
SpeciesId.SKORUPI,
|
||||
SpeciesId.JOLTIK,
|
||||
SpeciesId.LARVESTA,
|
||||
SpeciesId.VIVILLON,
|
||||
SpeciesId.CHARJABUG,
|
||||
SpeciesId.RIBOMBEE,
|
||||
SpeciesId.SPIDOPS,
|
||||
SpeciesId.LOKIX,
|
||||
];
|
||||
|
||||
const POOL_2_POKEMON = [
|
||||
Species.SCYTHER,
|
||||
Species.PINSIR,
|
||||
Species.HERACROSS,
|
||||
Species.FORRETRESS,
|
||||
Species.SCIZOR,
|
||||
Species.SHUCKLE,
|
||||
Species.SHEDINJA,
|
||||
Species.ARMALDO,
|
||||
Species.VESPIQUEN,
|
||||
Species.DRAPION,
|
||||
Species.YANMEGA,
|
||||
Species.LEAVANNY,
|
||||
Species.SCOLIPEDE,
|
||||
Species.CRUSTLE,
|
||||
Species.ESCAVALIER,
|
||||
Species.ACCELGOR,
|
||||
Species.GALVANTULA,
|
||||
Species.VIKAVOLT,
|
||||
Species.ARAQUANID,
|
||||
Species.ORBEETLE,
|
||||
Species.CENTISKORCH,
|
||||
Species.FROSMOTH,
|
||||
Species.KLEAVOR,
|
||||
SpeciesId.SCYTHER,
|
||||
SpeciesId.PINSIR,
|
||||
SpeciesId.HERACROSS,
|
||||
SpeciesId.FORRETRESS,
|
||||
SpeciesId.SCIZOR,
|
||||
SpeciesId.SHUCKLE,
|
||||
SpeciesId.SHEDINJA,
|
||||
SpeciesId.ARMALDO,
|
||||
SpeciesId.VESPIQUEN,
|
||||
SpeciesId.DRAPION,
|
||||
SpeciesId.YANMEGA,
|
||||
SpeciesId.LEAVANNY,
|
||||
SpeciesId.SCOLIPEDE,
|
||||
SpeciesId.CRUSTLE,
|
||||
SpeciesId.ESCAVALIER,
|
||||
SpeciesId.ACCELGOR,
|
||||
SpeciesId.GALVANTULA,
|
||||
SpeciesId.VIKAVOLT,
|
||||
SpeciesId.ARAQUANID,
|
||||
SpeciesId.ORBEETLE,
|
||||
SpeciesId.CENTISKORCH,
|
||||
SpeciesId.FROSMOTH,
|
||||
SpeciesId.KLEAVOR,
|
||||
];
|
||||
|
||||
const POOL_3_POKEMON: { species: Species; formIndex?: number }[] = [
|
||||
const POOL_3_POKEMON: { species: SpeciesId; formIndex?: number }[] = [
|
||||
{
|
||||
species: Species.PINSIR,
|
||||
species: SpeciesId.PINSIR,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: Species.SCIZOR,
|
||||
species: SpeciesId.SCIZOR,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: Species.HERACROSS,
|
||||
species: SpeciesId.HERACROSS,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: Species.ORBEETLE,
|
||||
species: SpeciesId.ORBEETLE,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: Species.CENTISKORCH,
|
||||
species: SpeciesId.CENTISKORCH,
|
||||
formIndex: 1,
|
||||
},
|
||||
{
|
||||
species: Species.DURANT,
|
||||
species: SpeciesId.DURANT,
|
||||
},
|
||||
{
|
||||
species: Species.VOLCARONA,
|
||||
species: SpeciesId.VOLCARONA,
|
||||
},
|
||||
{
|
||||
species: Species.GOLISOPOD,
|
||||
species: SpeciesId.GOLISOPOD,
|
||||
},
|
||||
];
|
||||
|
||||
const POOL_4_POKEMON = [Species.GENESECT, Species.SLITHER_WING, Species.BUZZWOLE, Species.PHEROMOSA];
|
||||
const POOL_4_POKEMON = [SpeciesId.GENESECT, SpeciesId.SLITHER_WING, SpeciesId.BUZZWOLE, SpeciesId.PHEROMOSA];
|
||||
|
||||
const PHYSICAL_TUTOR_MOVES = [Moves.MEGAHORN, Moves.ATTACK_ORDER, Moves.BUG_BITE, Moves.FIRST_IMPRESSION, Moves.LUNGE];
|
||||
const PHYSICAL_TUTOR_MOVES = [
|
||||
MoveId.MEGAHORN,
|
||||
MoveId.ATTACK_ORDER,
|
||||
MoveId.BUG_BITE,
|
||||
MoveId.FIRST_IMPRESSION,
|
||||
MoveId.LUNGE,
|
||||
];
|
||||
|
||||
const SPECIAL_TUTOR_MOVES = [
|
||||
Moves.SILVER_WIND,
|
||||
Moves.SIGNAL_BEAM,
|
||||
Moves.BUG_BUZZ,
|
||||
Moves.POLLEN_PUFF,
|
||||
Moves.STRUGGLE_BUG,
|
||||
MoveId.SILVER_WIND,
|
||||
MoveId.SIGNAL_BEAM,
|
||||
MoveId.BUG_BUZZ,
|
||||
MoveId.POLLEN_PUFF,
|
||||
MoveId.STRUGGLE_BUG,
|
||||
];
|
||||
|
||||
const STATUS_TUTOR_MOVES = [
|
||||
Moves.STRING_SHOT,
|
||||
Moves.DEFEND_ORDER,
|
||||
Moves.RAGE_POWDER,
|
||||
Moves.STICKY_WEB,
|
||||
Moves.SILK_TRAP,
|
||||
MoveId.STRING_SHOT,
|
||||
MoveId.DEFEND_ORDER,
|
||||
MoveId.RAGE_POWDER,
|
||||
MoveId.STICKY_WEB,
|
||||
MoveId.SILK_TRAP,
|
||||
];
|
||||
|
||||
const MISC_TUTOR_MOVES = [Moves.LEECH_LIFE, Moves.U_TURN, Moves.HEAL_ORDER, Moves.QUIVER_DANCE, Moves.INFESTATION];
|
||||
const MISC_TUTOR_MOVES = [MoveId.LEECH_LIFE, MoveId.U_TURN, MoveId.HEAL_ORDER, MoveId.QUIVER_DANCE, MoveId.INFESTATION];
|
||||
|
||||
/**
|
||||
* Wave breakpoints that determine how strong to make the Bug-Type Superfan's team
|
||||
@ -213,12 +218,12 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
|
||||
let beedrillKeys: { spriteKey: string; fileRoot: string }, butterfreeKeys: { spriteKey: string; fileRoot: string };
|
||||
if (globalScene.currentBattle.waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
|
||||
beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false);
|
||||
butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false);
|
||||
beedrillKeys = getSpriteKeysFromSpecies(SpeciesId.BEEDRILL, false);
|
||||
butterfreeKeys = getSpriteKeysFromSpecies(SpeciesId.BUTTERFREE, false);
|
||||
} else {
|
||||
// Mega Beedrill/Gmax Butterfree
|
||||
beedrillKeys = getSpriteKeysFromSpecies(Species.BEEDRILL, false, 1);
|
||||
butterfreeKeys = getSpriteKeysFromSpecies(Species.BUTTERFREE, false, 1);
|
||||
beedrillKeys = getSpriteKeysFromSpecies(SpeciesId.BEEDRILL, false, 1);
|
||||
butterfreeKeys = getSpriteKeysFromSpecies(SpeciesId.BUTTERFREE, false, 1);
|
||||
}
|
||||
|
||||
encounter.spriteConfigs = [
|
||||
@ -519,26 +524,26 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
if (waveIndex < WAVE_LEVEL_BREAKPOINTS[0]) {
|
||||
// Use default template (2 AVG)
|
||||
config
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.BEEDRILL], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.BUTTERFREE], TrainerSlot.TRAINER, true));
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([SpeciesId.BEEDRILL], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([SpeciesId.BUTTERFREE], TrainerSlot.TRAINER, true));
|
||||
} else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[1]) {
|
||||
config
|
||||
.setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE))
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.BEEDRILL], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.BUTTERFREE], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([SpeciesId.BEEDRILL], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([SpeciesId.BUTTERFREE], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true));
|
||||
} else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[2]) {
|
||||
config
|
||||
.setPartyTemplates(new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE))
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.BEEDRILL], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.BUTTERFREE], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([SpeciesId.BEEDRILL], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([SpeciesId.BUTTERFREE], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true));
|
||||
} else if (waveIndex < WAVE_LEVEL_BREAKPOINTS[3]) {
|
||||
config
|
||||
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.BEEDRILL], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.BUTTERFREE], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([SpeciesId.BEEDRILL], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([SpeciesId.BUTTERFREE], TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_1_POKEMON, TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true))
|
||||
.setPartyMemberFunc(4, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true));
|
||||
@ -547,7 +552,7 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc([Species.BEEDRILL], TrainerSlot.TRAINER, true, p => {
|
||||
getRandomPartyMemberFunc([SpeciesId.BEEDRILL], TrainerSlot.TRAINER, true, p => {
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
p.generateName();
|
||||
@ -555,7 +560,7 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc([Species.BUTTERFREE], TrainerSlot.TRAINER, true, p => {
|
||||
getRandomPartyMemberFunc([SpeciesId.BUTTERFREE], TrainerSlot.TRAINER, true, p => {
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
p.generateName();
|
||||
@ -580,7 +585,7 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
.setPartyTemplates(new TrainerPartyTemplate(5, PartyMemberStrength.AVERAGE))
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc([Species.BEEDRILL], TrainerSlot.TRAINER, true, p => {
|
||||
getRandomPartyMemberFunc([SpeciesId.BEEDRILL], TrainerSlot.TRAINER, true, p => {
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
p.generateName();
|
||||
@ -588,7 +593,7 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc([Species.BUTTERFREE], TrainerSlot.TRAINER, true, p => {
|
||||
getRandomPartyMemberFunc([SpeciesId.BUTTERFREE], TrainerSlot.TRAINER, true, p => {
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
p.generateName();
|
||||
@ -625,7 +630,7 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc([Species.BEEDRILL], TrainerSlot.TRAINER, true, p => {
|
||||
getRandomPartyMemberFunc([SpeciesId.BEEDRILL], TrainerSlot.TRAINER, true, p => {
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
p.generateName();
|
||||
@ -633,7 +638,7 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc([Species.BUTTERFREE], TrainerSlot.TRAINER, true, p => {
|
||||
getRandomPartyMemberFunc([SpeciesId.BUTTERFREE], TrainerSlot.TRAINER, true, p => {
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
p.generateName();
|
||||
@ -663,7 +668,7 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc([Species.BEEDRILL], TrainerSlot.TRAINER, true, p => {
|
||||
getRandomPartyMemberFunc([SpeciesId.BEEDRILL], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
@ -672,7 +677,7 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc([Species.BUTTERFREE], TrainerSlot.TRAINER, true, p => {
|
||||
getRandomPartyMemberFunc([SpeciesId.BUTTERFREE], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
@ -760,8 +765,10 @@ function doBugTypeMoveTutor(): Promise<void> {
|
||||
|
||||
// Option select complete, handle if they are learning a move
|
||||
if (result && result.selectedOptionIndex < moveOptions.length) {
|
||||
globalScene.unshiftPhase(
|
||||
new LearnMovePhase(result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"LearnMovePhase",
|
||||
result.selectedPokemonIndex,
|
||||
moveOptions[result.selectedOptionIndex].moveId,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11,19 +11,20 @@ import {
|
||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||
import { TrainerPartyCompoundTemplate } 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 { ModifierPoolType, modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import {
|
||||
applyAbilityOverrideToPokemon,
|
||||
applyModifierTypeToPlayerPokemon,
|
||||
@ -37,38 +38,38 @@ import { UiMode } from "#enums/ui-mode";
|
||||
import i18next from "i18next";
|
||||
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { Ability } from "#app/data/abilities/ability-class";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { BerryModifier } from "#app/modifier/modifier";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||
import { MoveCategory } from "#enums/MoveCategory";
|
||||
import { CustomPokemonData } from "#app/data/pokemon/pokemon-data";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { EncounterAnim } from "#enums/encounter-anims";
|
||||
import { Challenges } from "#enums/challenges";
|
||||
import { allAbilities } from "#app/data/data-lists";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounters/clowningAround";
|
||||
|
||||
const RANDOM_ABILITY_POOL = [
|
||||
Abilities.STURDY,
|
||||
Abilities.PICKUP,
|
||||
Abilities.INTIMIDATE,
|
||||
Abilities.GUTS,
|
||||
Abilities.DROUGHT,
|
||||
Abilities.DRIZZLE,
|
||||
Abilities.SNOW_WARNING,
|
||||
Abilities.SAND_STREAM,
|
||||
Abilities.ELECTRIC_SURGE,
|
||||
Abilities.PSYCHIC_SURGE,
|
||||
Abilities.GRASSY_SURGE,
|
||||
Abilities.MISTY_SURGE,
|
||||
Abilities.MAGICIAN,
|
||||
Abilities.SHEER_FORCE,
|
||||
Abilities.PRANKSTER,
|
||||
AbilityId.STURDY,
|
||||
AbilityId.PICKUP,
|
||||
AbilityId.INTIMIDATE,
|
||||
AbilityId.GUTS,
|
||||
AbilityId.DROUGHT,
|
||||
AbilityId.DRIZZLE,
|
||||
AbilityId.SNOW_WARNING,
|
||||
AbilityId.SAND_STREAM,
|
||||
AbilityId.ELECTRIC_SURGE,
|
||||
AbilityId.PSYCHIC_SURGE,
|
||||
AbilityId.GRASSY_SURGE,
|
||||
AbilityId.MISTY_SURGE,
|
||||
AbilityId.MAGICIAN,
|
||||
AbilityId.SHEER_FORCE,
|
||||
AbilityId.PRANKSTER,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -86,7 +87,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
.withAutoHideIntroVisuals(false)
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: Species.MR_MIME.toString(),
|
||||
spriteKey: SpeciesId.MR_MIME.toString(),
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
@ -96,7 +97,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
yShadow: -3,
|
||||
},
|
||||
{
|
||||
spriteKey: Species.BLACEPHALON.toString(),
|
||||
spriteKey: SpeciesId.BLACEPHALON.toString(),
|
||||
fileRoot: "pokemon/exp",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
@ -139,7 +140,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
|
||||
// Generate random ability for Blacephalon from pool
|
||||
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 };
|
||||
|
||||
// Decide the random types for Blacephalon. They should not be the same.
|
||||
@ -154,28 +155,28 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
pokemonConfigs: [
|
||||
// Overrides first 2 pokemon to be Mr. Mime and Blacephalon
|
||||
{
|
||||
species: getPokemonSpecies(Species.MR_MIME),
|
||||
species: getPokemonSpecies(SpeciesId.MR_MIME),
|
||||
isBoss: true,
|
||||
moveSet: [Moves.TEETER_DANCE, Moves.ALLY_SWITCH, Moves.DAZZLING_GLEAM, Moves.PSYCHIC],
|
||||
moveSet: [MoveId.TEETER_DANCE, MoveId.ALLY_SWITCH, MoveId.DAZZLING_GLEAM, MoveId.PSYCHIC],
|
||||
},
|
||||
{
|
||||
// Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
|
||||
species: getPokemonSpecies(Species.BLACEPHALON),
|
||||
species: getPokemonSpecies(SpeciesId.BLACEPHALON),
|
||||
customPokemonData: new CustomPokemonData({
|
||||
ability: ability,
|
||||
types: [firstType, secondType],
|
||||
}),
|
||||
isBoss: true,
|
||||
moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN],
|
||||
moveSet: [MoveId.TRICK, MoveId.HYPNOSIS, MoveId.SHADOW_BALL, MoveId.MIND_BLOWN],
|
||||
},
|
||||
],
|
||||
doubleBattle: true,
|
||||
});
|
||||
|
||||
// Load animations/sfx for start of fight moves
|
||||
loadCustomMovesForEncounter([Moves.ROLE_PLAY, Moves.TAUNT]);
|
||||
loadCustomMovesForEncounter([MoveId.ROLE_PLAY, MoveId.TAUNT]);
|
||||
|
||||
encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName());
|
||||
encounter.setDialogueToken("blacephalonName", getPokemonSpecies(SpeciesId.BLACEPHALON).getName());
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -208,19 +209,19 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
// Mr. Mime copies the Blacephalon's random ability
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.ENEMY_2],
|
||||
move: new PokemonMove(Moves.ROLE_PLAY),
|
||||
move: new PokemonMove(MoveId.ROLE_PLAY),
|
||||
ignorePp: true,
|
||||
},
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
move: new PokemonMove(Moves.TAUNT),
|
||||
move: new PokemonMove(MoveId.TAUNT),
|
||||
ignorePp: true,
|
||||
},
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||
targets: [BattlerIndex.PLAYER_2],
|
||||
move: new PokemonMove(Moves.TAUNT),
|
||||
move: new PokemonMove(MoveId.TAUNT),
|
||||
ignorePp: true,
|
||||
},
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -23,22 +23,21 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import type { PlayerPokemon } 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 { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import { EncounterAnim } from "#enums/encounter-anims";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import i18next from "i18next";
|
||||
|
||||
@ -47,46 +46,46 @@ const namespace = "mysteryEncounters/dancingLessons";
|
||||
|
||||
// Fire form
|
||||
const BAILE_STYLE_BIOMES = [
|
||||
Biome.VOLCANO,
|
||||
Biome.BEACH,
|
||||
Biome.ISLAND,
|
||||
Biome.WASTELAND,
|
||||
Biome.MOUNTAIN,
|
||||
Biome.BADLANDS,
|
||||
Biome.DESERT,
|
||||
BiomeId.VOLCANO,
|
||||
BiomeId.BEACH,
|
||||
BiomeId.ISLAND,
|
||||
BiomeId.WASTELAND,
|
||||
BiomeId.MOUNTAIN,
|
||||
BiomeId.BADLANDS,
|
||||
BiomeId.DESERT,
|
||||
];
|
||||
|
||||
// Electric form
|
||||
const POM_POM_STYLE_BIOMES = [
|
||||
Biome.CONSTRUCTION_SITE,
|
||||
Biome.POWER_PLANT,
|
||||
Biome.FACTORY,
|
||||
Biome.LABORATORY,
|
||||
Biome.SLUM,
|
||||
Biome.METROPOLIS,
|
||||
Biome.DOJO,
|
||||
BiomeId.CONSTRUCTION_SITE,
|
||||
BiomeId.POWER_PLANT,
|
||||
BiomeId.FACTORY,
|
||||
BiomeId.LABORATORY,
|
||||
BiomeId.SLUM,
|
||||
BiomeId.METROPOLIS,
|
||||
BiomeId.DOJO,
|
||||
];
|
||||
|
||||
// Psychic form
|
||||
const PAU_STYLE_BIOMES = [
|
||||
Biome.JUNGLE,
|
||||
Biome.FAIRY_CAVE,
|
||||
Biome.MEADOW,
|
||||
Biome.PLAINS,
|
||||
Biome.GRASS,
|
||||
Biome.TALL_GRASS,
|
||||
Biome.FOREST,
|
||||
BiomeId.JUNGLE,
|
||||
BiomeId.FAIRY_CAVE,
|
||||
BiomeId.MEADOW,
|
||||
BiomeId.PLAINS,
|
||||
BiomeId.GRASS,
|
||||
BiomeId.TALL_GRASS,
|
||||
BiomeId.FOREST,
|
||||
];
|
||||
|
||||
// Ghost form
|
||||
const SENSU_STYLE_BIOMES = [
|
||||
Biome.RUINS,
|
||||
Biome.SWAMP,
|
||||
Biome.CAVE,
|
||||
Biome.ABYSS,
|
||||
Biome.GRAVEYARD,
|
||||
Biome.LAKE,
|
||||
Biome.TEMPLE,
|
||||
BiomeId.RUINS,
|
||||
BiomeId.SWAMP,
|
||||
BiomeId.CAVE,
|
||||
BiomeId.ABYSS,
|
||||
BiomeId.GRAVEYARD,
|
||||
BiomeId.LAKE,
|
||||
BiomeId.TEMPLE,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -127,14 +126,14 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
const species = getPokemonSpecies(Species.ORICORIO);
|
||||
const species = getPokemonSpecies(SpeciesId.ORICORIO);
|
||||
const level = getEncounterPokemonLevelForWave(STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||
const enemyPokemon = new EnemyPokemon(species, level, TrainerSlot.NONE, false);
|
||||
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === Moves.REVELATION_DANCE)) {
|
||||
if (!enemyPokemon.moveset.some(m => m && m.getMove().id === MoveId.REVELATION_DANCE)) {
|
||||
if (enemyPokemon.moveset.length < 4) {
|
||||
enemyPokemon.moveset.push(new PokemonMove(Moves.REVELATION_DANCE));
|
||||
enemyPokemon.moveset.push(new PokemonMove(MoveId.REVELATION_DANCE));
|
||||
} else {
|
||||
enemyPokemon.moveset[0] = new PokemonMove(Moves.REVELATION_DANCE);
|
||||
enemyPokemon.moveset[0] = new PokemonMove(MoveId.REVELATION_DANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,13 +175,12 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.1.boss_enraged`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF],
|
||||
1,
|
||||
),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF],
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -193,7 +191,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
oricorioData,
|
||||
};
|
||||
|
||||
encounter.setDialogueToken("oricorioName", getPokemonSpecies(Species.ORICORIO).getName());
|
||||
encounter.setDialogueToken("oricorioName", getPokemonSpecies(SpeciesId.ORICORIO).getName());
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -215,7 +213,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
encounter.startOfBattleEffects.push({
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
move: new PokemonMove(Moves.REVELATION_DANCE),
|
||||
move: new PokemonMove(MoveId.REVELATION_DANCE),
|
||||
ignorePp: true,
|
||||
});
|
||||
|
||||
@ -245,8 +243,10 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||
globalScene.unshiftPhase(
|
||||
new LearnMovePhase(globalScene.getPlayerParty().indexOf(pokemon), Moves.REVELATION_DANCE),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"LearnMovePhase",
|
||||
globalScene.getPlayerParty().indexOf(pokemon),
|
||||
MoveId.REVELATION_DANCE,
|
||||
);
|
||||
|
||||
// Play animation again to "learn" the dance
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { PokemonType } from "#enums/pokemon-type";
|
||||
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
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 type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -16,7 +16,6 @@ import {
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
@ -27,68 +26,68 @@ const namespace = "mysteryEncounters/darkDeal";
|
||||
|
||||
/** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and Mythicals */
|
||||
const excludedBosses = [
|
||||
Species.NECROZMA,
|
||||
Species.COSMOG,
|
||||
Species.COSMOEM,
|
||||
Species.SOLGALEO,
|
||||
Species.LUNALA,
|
||||
Species.ETERNATUS,
|
||||
Species.NIHILEGO,
|
||||
Species.BUZZWOLE,
|
||||
Species.PHEROMOSA,
|
||||
Species.XURKITREE,
|
||||
Species.CELESTEELA,
|
||||
Species.KARTANA,
|
||||
Species.GUZZLORD,
|
||||
Species.POIPOLE,
|
||||
Species.NAGANADEL,
|
||||
Species.STAKATAKA,
|
||||
Species.BLACEPHALON,
|
||||
Species.GREAT_TUSK,
|
||||
Species.SCREAM_TAIL,
|
||||
Species.BRUTE_BONNET,
|
||||
Species.FLUTTER_MANE,
|
||||
Species.SLITHER_WING,
|
||||
Species.SANDY_SHOCKS,
|
||||
Species.ROARING_MOON,
|
||||
Species.KORAIDON,
|
||||
Species.WALKING_WAKE,
|
||||
Species.GOUGING_FIRE,
|
||||
Species.RAGING_BOLT,
|
||||
Species.IRON_TREADS,
|
||||
Species.IRON_BUNDLE,
|
||||
Species.IRON_HANDS,
|
||||
Species.IRON_JUGULIS,
|
||||
Species.IRON_MOTH,
|
||||
Species.IRON_THORNS,
|
||||
Species.IRON_VALIANT,
|
||||
Species.MIRAIDON,
|
||||
Species.IRON_LEAVES,
|
||||
Species.IRON_BOULDER,
|
||||
Species.IRON_CROWN,
|
||||
Species.MEW,
|
||||
Species.CELEBI,
|
||||
Species.DEOXYS,
|
||||
Species.JIRACHI,
|
||||
Species.DARKRAI,
|
||||
Species.PHIONE,
|
||||
Species.MANAPHY,
|
||||
Species.ARCEUS,
|
||||
Species.SHAYMIN,
|
||||
Species.VICTINI,
|
||||
Species.MELOETTA,
|
||||
Species.KELDEO,
|
||||
Species.GENESECT,
|
||||
Species.DIANCIE,
|
||||
Species.HOOPA,
|
||||
Species.VOLCANION,
|
||||
Species.MAGEARNA,
|
||||
Species.MARSHADOW,
|
||||
Species.ZERAORA,
|
||||
Species.ZARUDE,
|
||||
Species.MELTAN,
|
||||
Species.MELMETAL,
|
||||
Species.PECHARUNT,
|
||||
SpeciesId.NECROZMA,
|
||||
SpeciesId.COSMOG,
|
||||
SpeciesId.COSMOEM,
|
||||
SpeciesId.SOLGALEO,
|
||||
SpeciesId.LUNALA,
|
||||
SpeciesId.ETERNATUS,
|
||||
SpeciesId.NIHILEGO,
|
||||
SpeciesId.BUZZWOLE,
|
||||
SpeciesId.PHEROMOSA,
|
||||
SpeciesId.XURKITREE,
|
||||
SpeciesId.CELESTEELA,
|
||||
SpeciesId.KARTANA,
|
||||
SpeciesId.GUZZLORD,
|
||||
SpeciesId.POIPOLE,
|
||||
SpeciesId.NAGANADEL,
|
||||
SpeciesId.STAKATAKA,
|
||||
SpeciesId.BLACEPHALON,
|
||||
SpeciesId.GREAT_TUSK,
|
||||
SpeciesId.SCREAM_TAIL,
|
||||
SpeciesId.BRUTE_BONNET,
|
||||
SpeciesId.FLUTTER_MANE,
|
||||
SpeciesId.SLITHER_WING,
|
||||
SpeciesId.SANDY_SHOCKS,
|
||||
SpeciesId.ROARING_MOON,
|
||||
SpeciesId.KORAIDON,
|
||||
SpeciesId.WALKING_WAKE,
|
||||
SpeciesId.GOUGING_FIRE,
|
||||
SpeciesId.RAGING_BOLT,
|
||||
SpeciesId.IRON_TREADS,
|
||||
SpeciesId.IRON_BUNDLE,
|
||||
SpeciesId.IRON_HANDS,
|
||||
SpeciesId.IRON_JUGULIS,
|
||||
SpeciesId.IRON_MOTH,
|
||||
SpeciesId.IRON_THORNS,
|
||||
SpeciesId.IRON_VALIANT,
|
||||
SpeciesId.MIRAIDON,
|
||||
SpeciesId.IRON_LEAVES,
|
||||
SpeciesId.IRON_BOULDER,
|
||||
SpeciesId.IRON_CROWN,
|
||||
SpeciesId.MEW,
|
||||
SpeciesId.CELEBI,
|
||||
SpeciesId.DEOXYS,
|
||||
SpeciesId.JIRACHI,
|
||||
SpeciesId.DARKRAI,
|
||||
SpeciesId.PHIONE,
|
||||
SpeciesId.MANAPHY,
|
||||
SpeciesId.ARCEUS,
|
||||
SpeciesId.SHAYMIN,
|
||||
SpeciesId.VICTINI,
|
||||
SpeciesId.MELOETTA,
|
||||
SpeciesId.KELDEO,
|
||||
SpeciesId.GENESECT,
|
||||
SpeciesId.DIANCIE,
|
||||
SpeciesId.HOOPA,
|
||||
SpeciesId.VOLCANION,
|
||||
SpeciesId.MAGEARNA,
|
||||
SpeciesId.MARSHADOW,
|
||||
SpeciesId.ZERAORA,
|
||||
SpeciesId.ZARUDE,
|
||||
SpeciesId.MELTAN,
|
||||
SpeciesId.MELMETAL,
|
||||
SpeciesId.PECHARUNT,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -165,7 +164,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE
|
||||
.withOptionPhase(async () => {
|
||||
// Give the player 5 Rogue Balls
|
||||
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
|
||||
// If this is a mono-type challenge, always ensure the required type is filtered for
|
||||
|
@ -28,15 +28,14 @@ import {
|
||||
PreserveBerryModifier,
|
||||
} from "#app/modifier/modifier";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { randSeedItem } from "#app/utils/common";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { timedEventManager } from "#app/global-event-manager";
|
||||
|
||||
/** the i18n namespace for this encounter */
|
||||
@ -65,10 +64,10 @@ const doEventReward = () => {
|
||||
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount());
|
||||
});
|
||||
if (candidates.length > 0) {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)]));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes[randSeedItem(candidates)]);
|
||||
} else {
|
||||
// At max stacks, give a Voucher instead
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.VOUCHER));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.VOUCHER);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -95,7 +94,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
{
|
||||
spriteKey: "",
|
||||
fileRoot: "",
|
||||
species: Species.DELIBIRD,
|
||||
species: SpeciesId.DELIBIRD,
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
startFrame: 38,
|
||||
@ -104,7 +103,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
{
|
||||
spriteKey: "",
|
||||
fileRoot: "",
|
||||
species: Species.DELIBIRD,
|
||||
species: SpeciesId.DELIBIRD,
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
scale: 1.06,
|
||||
@ -112,7 +111,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
{
|
||||
spriteKey: "",
|
||||
fileRoot: "",
|
||||
species: Species.DELIBIRD,
|
||||
species: SpeciesId.DELIBIRD,
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
startFrame: 65,
|
||||
@ -137,7 +136,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
])
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName());
|
||||
encounter.setDialogueToken("delibirdName", getPokemonSpecies(SpeciesId.DELIBIRD).getName());
|
||||
|
||||
globalScene.loadBgm("mystery_encounter_delibirdy", "mystery_encounter_delibirdy.mp3");
|
||||
return true;
|
||||
@ -181,7 +180,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
);
|
||||
doEventReward();
|
||||
} else {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.AMULET_COIN);
|
||||
doEventReward();
|
||||
}
|
||||
|
||||
@ -266,7 +265,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
);
|
||||
doEventReward();
|
||||
} else {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.CANDY_JAR);
|
||||
doEventReward();
|
||||
}
|
||||
} else {
|
||||
@ -288,7 +287,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
);
|
||||
doEventReward();
|
||||
} else {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.BERRY_POUCH);
|
||||
doEventReward();
|
||||
}
|
||||
}
|
||||
@ -372,7 +371,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
);
|
||||
doEventReward();
|
||||
} else {
|
||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM));
|
||||
globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.HEALING_CHARM);
|
||||
doEventReward();
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,11 @@ import {
|
||||
leaveEncounterWithoutBattle,
|
||||
setEncounterRewards,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { ModifierTypeFunc } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import type { ModifierTypeFunc } from "#app/@types/modifier-types";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
@ -35,7 +35,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu
|
||||
{
|
||||
spriteKey: "",
|
||||
fileRoot: "",
|
||||
species: Species.FURFROU,
|
||||
species: SpeciesId.FURFROU,
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
x: 30,
|
||||
|
@ -7,8 +7,9 @@ import {
|
||||
setEncounterExp,
|
||||
setEncounterRewards,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
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 { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
generateModifierType,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -20,14 +20,14 @@ import {
|
||||
CombinationPokemonRequirement,
|
||||
TypeRequirement,
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Gender } from "#app/data/gender";
|
||||
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 { PokemonMove } from "#app/field/pokemon";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
||||
@ -42,12 +42,11 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { EncounterAnim } from "#enums/encounter-anims";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
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 { allAbilities } from "#app/data/data-lists";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounters/fieryFallout";
|
||||
@ -83,7 +82,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mons
|
||||
const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA);
|
||||
const volcaronaSpecies = getPokemonSpecies(SpeciesId.VOLCARONA);
|
||||
const config: EnemyPartyConfig = {
|
||||
pokemonConfigs: [
|
||||
{
|
||||
@ -92,8 +91,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
gender: Gender.MALE,
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.SPDEF, Stat.SPD],
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -103,8 +106,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
gender: Gender.FEMALE,
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.SPDEF, Stat.SPD],
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -119,7 +126,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
{
|
||||
spriteKey: "",
|
||||
fileRoot: "",
|
||||
species: Species.VOLCARONA,
|
||||
species: SpeciesId.VOLCARONA,
|
||||
repeat: true,
|
||||
hidden: true,
|
||||
hasShadow: true,
|
||||
@ -129,7 +136,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
{
|
||||
spriteKey: "",
|
||||
fileRoot: "",
|
||||
species: Species.VOLCARONA,
|
||||
species: SpeciesId.VOLCARONA,
|
||||
repeat: true,
|
||||
hidden: true,
|
||||
hasShadow: true,
|
||||
@ -138,12 +145,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
];
|
||||
|
||||
// Load animations/sfx for Volcarona moves
|
||||
loadCustomMovesForEncounter([Moves.FIRE_SPIN, Moves.QUIVER_DANCE]);
|
||||
loadCustomMovesForEncounter([MoveId.FIRE_SPIN, MoveId.QUIVER_DANCE]);
|
||||
|
||||
const pokemon = globalScene.getEnemyPokemon();
|
||||
globalScene.arena.trySetWeather(WeatherType.SUNNY, pokemon);
|
||||
|
||||
encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName());
|
||||
encounter.setDialogueToken("volcaronaName", getPokemonSpecies(SpeciesId.VOLCARONA).getName());
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -193,13 +200,13 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
move: new PokemonMove(Moves.FIRE_SPIN),
|
||||
move: new PokemonMove(MoveId.FIRE_SPIN),
|
||||
ignorePp: true,
|
||||
},
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||
targets: [BattlerIndex.PLAYER_2],
|
||||
move: new PokemonMove(Moves.FIRE_SPIN),
|
||||
move: new PokemonMove(MoveId.FIRE_SPIN),
|
||||
ignorePp: true,
|
||||
},
|
||||
);
|
||||
@ -239,11 +246,11 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
|
||||
// Burn applied
|
||||
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
|
||||
encounter.setDialogueToken("abilityName", new Ability(Abilities.HEATPROOF, 3).name);
|
||||
encounter.setDialogueToken("abilityName", allAbilities[AbilityId.HEATPROOF].name);
|
||||
queueEncounterMessage(`${namespace}:option.2.target_burned`);
|
||||
|
||||
// Also permanently change the burned Pokemon's ability to Heatproof
|
||||
applyAbilityOverrideToPokemon(chosenPokemon, Abilities.HEATPROOF);
|
||||
applyAbilityOverrideToPokemon(chosenPokemon, AbilityId.HEATPROOF);
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,7 +290,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
|
||||
|
||||
const primary = encounter.options[2].primaryPokemon!;
|
||||
|
||||
setEncounterExp([primary.id], getPokemonSpecies(Species.VOLCARONA).baseExp * 2);
|
||||
setEncounterExp([primary.id], getPokemonSpecies(SpeciesId.VOLCARONA).baseExp * 2);
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build(),
|
||||
|
@ -9,13 +9,10 @@ import {
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
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 {
|
||||
getPlayerModifierTypeOptions,
|
||||
ModifierPoolType,
|
||||
regenerateModifierPoolThresholds,
|
||||
} from "#app/modifier/modifier-type";
|
||||
import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
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 { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
@ -76,7 +72,13 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
||||
// Randomly boost 1 stat 2 stages
|
||||
// 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,22 +13,20 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import type { PlayerPokemon } 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 { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import i18next from "i18next";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
||||
import { addPokeballOpenParticles } from "#app/field/anims";
|
||||
import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase";
|
||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
@ -91,7 +89,7 @@ export const FunAndGamesEncounter: MysteryEncounter = MysteryEncounterBuilder.wi
|
||||
.withOnInit(() => {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
globalScene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3");
|
||||
encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(Species.WOBBUFFET).getName());
|
||||
encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(SpeciesId.WOBBUFFET).getName());
|
||||
return true;
|
||||
})
|
||||
.withOnVisualsStart(() => {
|
||||
@ -214,7 +212,7 @@ async function summonPlayerPokemon() {
|
||||
});
|
||||
|
||||
// Also loads Wobbuffet data (cannot be shiny)
|
||||
const enemySpecies = getPokemonSpecies(Species.WOBBUFFET);
|
||||
const enemySpecies = getPokemonSpecies(SpeciesId.WOBBUFFET);
|
||||
globalScene.currentBattle.enemyParty = [];
|
||||
const wobbuffet = globalScene.addEnemyPokemon(
|
||||
enemySpecies,
|
||||
@ -411,13 +409,13 @@ function summonPlayerPokemonAnimation(pokemon: PlayerPokemon): Promise<void> {
|
||||
pokemon.resetSummonData();
|
||||
globalScene.time.delayedCall(1000, () => {
|
||||
if (pokemon.isShiny()) {
|
||||
globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
|
||||
globalScene.phaseManager.unshiftNew("ShinySparklePhase", pokemon.getBattlerIndex());
|
||||
}
|
||||
|
||||
pokemon.resetTurnData();
|
||||
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
||||
globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex()));
|
||||
globalScene.phaseManager.pushNew("PostSummonPhase", pokemon.getBattlerIndex());
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
|
@ -4,20 +4,17 @@ import {
|
||||
setEncounterRewards,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
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 type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||
import {
|
||||
getPlayerModifierTypeOptions,
|
||||
ModifierPoolType,
|
||||
regenerateModifierPoolThresholds,
|
||||
} from "#app/modifier/modifier-type";
|
||||
import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { getTypeRgb } from "#app/data/type";
|
||||
@ -33,7 +30,8 @@ import {
|
||||
} from "#app/utils/common";
|
||||
import type { PlayerPokemon } 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 {
|
||||
HiddenAbilityRateBoosterModifier,
|
||||
@ -64,39 +62,39 @@ const WONDER_TRADE_SHINY_CHANCE = 512;
|
||||
const MAX_WONDER_TRADE_SHINY_CHANCE = 4096;
|
||||
|
||||
const LEGENDARY_TRADE_POOLS = {
|
||||
1: [Species.RATTATA, Species.PIDGEY, Species.WEEDLE],
|
||||
2: [Species.SENTRET, Species.HOOTHOOT, Species.LEDYBA],
|
||||
3: [Species.POOCHYENA, Species.ZIGZAGOON, Species.TAILLOW],
|
||||
4: [Species.BIDOOF, Species.STARLY, Species.KRICKETOT],
|
||||
5: [Species.PATRAT, Species.PURRLOIN, Species.PIDOVE],
|
||||
6: [Species.BUNNELBY, Species.LITLEO, Species.SCATTERBUG],
|
||||
7: [Species.PIKIPEK, Species.YUNGOOS, Species.ROCKRUFF],
|
||||
8: [Species.SKWOVET, Species.WOOLOO, Species.ROOKIDEE],
|
||||
9: [Species.LECHONK, Species.FIDOUGH, Species.TAROUNTULA],
|
||||
1: [SpeciesId.RATTATA, SpeciesId.PIDGEY, SpeciesId.WEEDLE],
|
||||
2: [SpeciesId.SENTRET, SpeciesId.HOOTHOOT, SpeciesId.LEDYBA],
|
||||
3: [SpeciesId.POOCHYENA, SpeciesId.ZIGZAGOON, SpeciesId.TAILLOW],
|
||||
4: [SpeciesId.BIDOOF, SpeciesId.STARLY, SpeciesId.KRICKETOT],
|
||||
5: [SpeciesId.PATRAT, SpeciesId.PURRLOIN, SpeciesId.PIDOVE],
|
||||
6: [SpeciesId.BUNNELBY, SpeciesId.LITLEO, SpeciesId.SCATTERBUG],
|
||||
7: [SpeciesId.PIKIPEK, SpeciesId.YUNGOOS, SpeciesId.ROCKRUFF],
|
||||
8: [SpeciesId.SKWOVET, SpeciesId.WOOLOO, SpeciesId.ROOKIDEE],
|
||||
9: [SpeciesId.LECHONK, SpeciesId.FIDOUGH, SpeciesId.TAROUNTULA],
|
||||
};
|
||||
|
||||
/** Exclude Paradox mons as they aren't considered legendary/mythical */
|
||||
const EXCLUDED_TRADE_SPECIES = [
|
||||
Species.GREAT_TUSK,
|
||||
Species.SCREAM_TAIL,
|
||||
Species.BRUTE_BONNET,
|
||||
Species.FLUTTER_MANE,
|
||||
Species.SLITHER_WING,
|
||||
Species.SANDY_SHOCKS,
|
||||
Species.ROARING_MOON,
|
||||
Species.WALKING_WAKE,
|
||||
Species.GOUGING_FIRE,
|
||||
Species.RAGING_BOLT,
|
||||
Species.IRON_TREADS,
|
||||
Species.IRON_BUNDLE,
|
||||
Species.IRON_HANDS,
|
||||
Species.IRON_JUGULIS,
|
||||
Species.IRON_MOTH,
|
||||
Species.IRON_THORNS,
|
||||
Species.IRON_VALIANT,
|
||||
Species.IRON_LEAVES,
|
||||
Species.IRON_BOULDER,
|
||||
Species.IRON_CROWN,
|
||||
SpeciesId.GREAT_TUSK,
|
||||
SpeciesId.SCREAM_TAIL,
|
||||
SpeciesId.BRUTE_BONNET,
|
||||
SpeciesId.FLUTTER_MANE,
|
||||
SpeciesId.SLITHER_WING,
|
||||
SpeciesId.SANDY_SHOCKS,
|
||||
SpeciesId.ROARING_MOON,
|
||||
SpeciesId.WALKING_WAKE,
|
||||
SpeciesId.GOUGING_FIRE,
|
||||
SpeciesId.RAGING_BOLT,
|
||||
SpeciesId.IRON_TREADS,
|
||||
SpeciesId.IRON_BUNDLE,
|
||||
SpeciesId.IRON_HANDS,
|
||||
SpeciesId.IRON_JUGULIS,
|
||||
SpeciesId.IRON_MOTH,
|
||||
SpeciesId.IRON_THORNS,
|
||||
SpeciesId.IRON_VALIANT,
|
||||
SpeciesId.IRON_LEAVES,
|
||||
SpeciesId.IRON_BOULDER,
|
||||
SpeciesId.IRON_CROWN,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { Species } from "#app/enums/species";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -11,10 +11,10 @@ import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encount
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
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 = Moves.SURF;
|
||||
const OPTION_2_REQUIRED_MOVE = Moves.FLY;
|
||||
const OPTION_1_REQUIRED_MOVE = MoveId.SURF;
|
||||
const OPTION_2_REQUIRED_MOVE = MoveId.FLY;
|
||||
/**
|
||||
* Damage percentage taken when wandering aimlessly.
|
||||
* Can be a number between `0` - `100`.
|
||||
@ -129,7 +129,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||
* Generic handler for using a guiding pokemon to guide you back.
|
||||
*/
|
||||
function handlePokemonGuidingYouPhase() {
|
||||
const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
|
||||
const laprasSpecies = getPokemonSpecies(SpeciesId.LAPRAS);
|
||||
const { mysteryEncounter } = globalScene.currentBattle;
|
||||
|
||||
if (mysteryEncounter?.selectedOption?.primaryPokemon?.id) {
|
||||
|
@ -7,8 +7,8 @@ import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||
import { trainerPartyTemplates } from "#app/data/trainers/TrainerPartyTemplate";
|
||||
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { ModifierTier } from "#enums/modifier-tier";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
@ -16,14 +16,13 @@ import {
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||
import { ModifierTier } from "#enums/modifier-tier";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
|
||||
/** i18n namespace for encounter */
|
||||
const namespace = "mysteryEncounters/mysteriousChest";
|
||||
@ -86,17 +85,17 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
disableSwitch: true,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.GIMMIGHOUL),
|
||||
species: getPokemonSpecies(SpeciesId.GIMMIGHOUL),
|
||||
formIndex: 0,
|
||||
isBoss: true,
|
||||
moveSet: [Moves.NASTY_PLOT, Moves.SHADOW_BALL, Moves.POWER_GEM, Moves.THIEF],
|
||||
moveSet: [MoveId.NASTY_PLOT, MoveId.SHADOW_BALL, MoveId.POWER_GEM, MoveId.THIEF],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
encounter.enemyPartyConfigs = [config];
|
||||
|
||||
encounter.setDialogueToken("gimmighoulName", getPokemonSpecies(Species.GIMMIGHOUL).getName());
|
||||
encounter.setDialogueToken("gimmighoulName", getPokemonSpecies(SpeciesId.GIMMIGHOUL).getName());
|
||||
encounter.setDialogueToken("trapPercent", TRAP_PERCENT.toString());
|
||||
encounter.setDialogueToken("commonPercent", COMMON_REWARDS_PERCENT.toString());
|
||||
encounter.setDialogueToken("ultraPercent", ULTRA_REWARDS_PERCENT.toString());
|
||||
@ -189,8 +188,8 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
const allowedPokemon = globalScene.getPokemonAllowedInBattle();
|
||||
if (allowedPokemon.length === 0) {
|
||||
// If there are no longer any legal pokemon in the party, game over.
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.unshiftPhase(new GameOverPhase());
|
||||
globalScene.phaseManager.clearPhaseQueue();
|
||||
globalScene.phaseManager.unshiftNew("GameOverPhase");
|
||||
} else {
|
||||
// Show which Pokemon was KOed, then start battle against Gimmighoul
|
||||
await transitionMysteryEncounterIntroVisuals(true, true, 500);
|
||||
|
@ -29,8 +29,6 @@ import { getEncounterText, showEncounterText } from "#app/data/mystery-encounter
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
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 { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups";
|
||||
|
||||
@ -276,7 +274,7 @@ async function summonSafariPokemon() {
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
// Message pokemon remaining
|
||||
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
|
||||
// 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.safariPokemonRemaining -= 1;
|
||||
|
||||
globalScene.unshiftPhase(new SummonPhase(0, false));
|
||||
globalScene.phaseManager.unshiftNew("SummonPhase", 0, false);
|
||||
|
||||
encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon));
|
||||
|
||||
@ -336,7 +334,7 @@ async function summonSafariPokemon() {
|
||||
|
||||
const ivScannerModifier = globalScene.findModifier(m => m instanceof 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);
|
||||
}
|
||||
} else {
|
||||
globalScene.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000);
|
||||
globalScene.phaseManager.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000);
|
||||
initSubsequentOptionSelect({
|
||||
overrideOptions: safariZoneGameOptions,
|
||||
startingCursorIndex: cursorIndex,
|
||||
|
@ -7,10 +7,10 @@ import {
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PlayerPokemon } 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 { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -49,7 +49,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
.withPrimaryPokemonHealthRatioRequirement([0.51, 1]) // At least 1 Pokemon must have above half HP
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: Species.KROOKODILE.toString(),
|
||||
spriteKey: SpeciesId.KROOKODILE.toString(),
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
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 { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -20,13 +20,13 @@ import {
|
||||
} from "../utils/encounter-phase-utils";
|
||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { AiType, PokemonMove } from "#app/field/pokemon";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { AiType } from "#enums/ai-type";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CustomPokemonData } from "#app/data/pokemon/pokemon-data";
|
||||
@ -50,7 +50,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
.withFleeAllowed(false)
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: Species.SNORLAX.toString(),
|
||||
spriteKey: SpeciesId.SNORLAX.toString(),
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: true,
|
||||
tint: 0.25,
|
||||
@ -69,14 +69,14 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
console.log(encounter);
|
||||
|
||||
// Calculate boss mon
|
||||
const bossSpecies = getPokemonSpecies(Species.SNORLAX);
|
||||
const bossSpecies = getPokemonSpecies(SpeciesId.SNORLAX);
|
||||
const pokemonConfig: EnemyPokemonConfig = {
|
||||
species: bossSpecies,
|
||||
isBoss: true,
|
||||
shiny: false, // Shiny lock because shiny is rolled only if the battle option is picked
|
||||
status: [StatusEffect.SLEEP, 6], // Extra turns on timer for Snorlax's start of fight moves
|
||||
nature: Nature.DOCILE,
|
||||
moveSet: [Moves.BODY_SLAM, Moves.CRUNCH, Moves.SLEEP_TALK, Moves.REST],
|
||||
moveSet: [MoveId.BODY_SLAM, MoveId.CRUNCH, MoveId.SLEEP_TALK, MoveId.REST],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType,
|
||||
@ -106,9 +106,9 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
encounter.enemyPartyConfigs = [config];
|
||||
|
||||
// Load animations/sfx for Snorlax fight start moves
|
||||
loadCustomMovesForEncounter([Moves.SNORE]);
|
||||
loadCustomMovesForEncounter([MoveId.SNORE]);
|
||||
|
||||
encounter.setDialogueToken("snorlaxName", getPokemonSpecies(Species.SNORLAX).getName());
|
||||
encounter.setDialogueToken("snorlaxName", getPokemonSpecies(SpeciesId.SNORLAX).getName());
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -136,7 +136,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
encounter.startOfBattleEffects.push({
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
move: new PokemonMove(Moves.SNORE),
|
||||
move: new PokemonMove(MoveId.SNORE),
|
||||
ignorePp: true,
|
||||
});
|
||||
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
|
||||
@ -155,7 +155,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
async () => {
|
||||
// Fall asleep waiting for Snorlax
|
||||
// Full heal party
|
||||
globalScene.unshiftPhase(new PartyHealPhase(true));
|
||||
globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
|
||||
queueEncounterMessage(`${namespace}:option.2.rest_result`);
|
||||
leaveEncounterWithoutBattle();
|
||||
},
|
||||
@ -181,7 +181,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
|
||||
fillRemaining: false,
|
||||
});
|
||||
// Snorlax exp to Pokemon that did the stealing
|
||||
setEncounterExp(instance.primaryPokemon!.id, getPokemonSpecies(Species.SNORLAX).baseExp);
|
||||
setEncounterExp(instance.primaryPokemon!.id, getPokemonSpecies(SpeciesId.SNORLAX).baseExp);
|
||||
leaveEncounterWithoutBattle();
|
||||
})
|
||||
.build(),
|
||||
|
@ -20,14 +20,14 @@ import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-enco
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import { getBiomeKey } from "#app/field/arena";
|
||||
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 { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import {
|
||||
@ -39,7 +39,14 @@ import {
|
||||
const namespace = "mysteryEncounters/teleportingHijinks";
|
||||
|
||||
const MONEY_COST_MULTIPLIER = 1.75;
|
||||
const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO];
|
||||
const BIOME_CANDIDATES = [
|
||||
BiomeId.SPACE,
|
||||
BiomeId.FAIRY_CAVE,
|
||||
BiomeId.LABORATORY,
|
||||
BiomeId.ISLAND,
|
||||
BiomeId.WASTELAND,
|
||||
BiomeId.DOJO,
|
||||
];
|
||||
const MACHINE_INTERFACING_TYPES = [PokemonType.ELECTRIC, PokemonType.STEEL];
|
||||
|
||||
/**
|
||||
@ -220,7 +227,13 @@ async function doBiomeTransitionDialogueAndBattleInit() {
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:boss_enraged`);
|
||||
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
statChangesForBattle,
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -229,7 +242,7 @@ async function doBiomeTransitionDialogueAndBattleInit() {
|
||||
return config;
|
||||
}
|
||||
|
||||
async function animateBiomeChange(nextBiome: Biome) {
|
||||
async function animateBiomeChange(nextBiome: BiomeId) {
|
||||
return new Promise<void>(resolve => {
|
||||
globalScene.tweens.add({
|
||||
targets: [globalScene.arenaEnemy, globalScene.lastEnemyTrainer],
|
||||
|
@ -11,14 +11,14 @@ import { randSeedShuffle } from "#app/utils/common";
|
||||
import type MysteryEncounter from "../mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import i18next from "i18next";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import type { IEggOptions } from "#app/data/egg";
|
||||
@ -26,7 +26,7 @@ import { EggSourceType } from "#enums/egg-source-types";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { getPokeballTintColor } from "#app/data/pokeball";
|
||||
|
||||
@ -42,75 +42,75 @@ const FINAL_STAGE_EVOLUTION_WAVE = 75;
|
||||
const FRIENDSHIP_ADDED = 20;
|
||||
|
||||
class BreederSpeciesEvolution {
|
||||
species: Species;
|
||||
species: SpeciesId;
|
||||
evolution: number;
|
||||
|
||||
constructor(species: Species, evolution: number) {
|
||||
constructor(species: SpeciesId, evolution: number) {
|
||||
this.species = species;
|
||||
this.evolution = evolution;
|
||||
}
|
||||
}
|
||||
|
||||
const POOL_1_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
|
||||
[Species.MUNCHLAX, new BreederSpeciesEvolution(Species.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
const POOL_1_POKEMON: (SpeciesId | BreederSpeciesEvolution)[][] = [
|
||||
[SpeciesId.MUNCHLAX, new BreederSpeciesEvolution(SpeciesId.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[
|
||||
Species.HAPPINY,
|
||||
new BreederSpeciesEvolution(Species.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.HAPPINY,
|
||||
new BreederSpeciesEvolution(SpeciesId.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
[
|
||||
Species.MAGBY,
|
||||
new BreederSpeciesEvolution(Species.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.MAGBY,
|
||||
new BreederSpeciesEvolution(SpeciesId.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
[
|
||||
Species.ELEKID,
|
||||
new BreederSpeciesEvolution(Species.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.ELEKID,
|
||||
new BreederSpeciesEvolution(SpeciesId.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
[Species.RIOLU, new BreederSpeciesEvolution(Species.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.RIOLU, new BreederSpeciesEvolution(SpeciesId.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[
|
||||
Species.BUDEW,
|
||||
new BreederSpeciesEvolution(Species.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.BUDEW,
|
||||
new BreederSpeciesEvolution(SpeciesId.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
[Species.TOXEL, new BreederSpeciesEvolution(Species.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.TOXEL, new BreederSpeciesEvolution(SpeciesId.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[
|
||||
Species.MIME_JR,
|
||||
new BreederSpeciesEvolution(Species.GALAR_MR_MIME, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.MIME_JR,
|
||||
new BreederSpeciesEvolution(SpeciesId.GALAR_MR_MIME, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
];
|
||||
|
||||
const POOL_2_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
|
||||
const POOL_2_POKEMON: (SpeciesId | BreederSpeciesEvolution)[][] = [
|
||||
[
|
||||
Species.PICHU,
|
||||
new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.RAICHU, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.PICHU,
|
||||
new BreederSpeciesEvolution(SpeciesId.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.RAICHU, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
[
|
||||
Species.PICHU,
|
||||
new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.PICHU,
|
||||
new BreederSpeciesEvolution(SpeciesId.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
[Species.SMOOCHUM, new BreederSpeciesEvolution(Species.JYNX, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.SMOOCHUM, new BreederSpeciesEvolution(SpeciesId.JYNX, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[
|
||||
Species.IGGLYBUFF,
|
||||
new BreederSpeciesEvolution(Species.JIGGLYPUFF, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.WIGGLYTUFF, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.IGGLYBUFF,
|
||||
new BreederSpeciesEvolution(SpeciesId.JIGGLYPUFF, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.WIGGLYTUFF, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
[
|
||||
Species.AZURILL,
|
||||
new BreederSpeciesEvolution(Species.MARILL, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(Species.AZUMARILL, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
SpeciesId.AZURILL,
|
||||
new BreederSpeciesEvolution(SpeciesId.MARILL, FIRST_STAGE_EVOLUTION_WAVE),
|
||||
new BreederSpeciesEvolution(SpeciesId.AZUMARILL, FINAL_STAGE_EVOLUTION_WAVE),
|
||||
],
|
||||
[Species.WYNAUT, new BreederSpeciesEvolution(Species.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.CHINGLING, new BreederSpeciesEvolution(Species.CHIMECHO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.BONSLY, new BreederSpeciesEvolution(Species.SUDOWOODO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.MANTYKE, new BreederSpeciesEvolution(Species.MANTINE, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.WYNAUT, new BreederSpeciesEvolution(SpeciesId.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.CHINGLING, new BreederSpeciesEvolution(SpeciesId.CHIMECHO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.BONSLY, new BreederSpeciesEvolution(SpeciesId.SUDOWOODO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[SpeciesId.MANTYKE, new BreederSpeciesEvolution(SpeciesId.MANTINE, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
];
|
||||
|
||||
/**
|
||||
@ -144,10 +144,10 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
|
||||
|
||||
const cleffaSpecies =
|
||||
waveIndex < FIRST_STAGE_EVOLUTION_WAVE
|
||||
? Species.CLEFFA
|
||||
? SpeciesId.CLEFFA
|
||||
: waveIndex < FINAL_STAGE_EVOLUTION_WAVE
|
||||
? Species.CLEFAIRY
|
||||
: Species.CLEFABLE;
|
||||
? SpeciesId.CLEFAIRY
|
||||
: SpeciesId.CLEFABLE;
|
||||
encounter.spriteConfigs = [
|
||||
{
|
||||
spriteKey: cleffaSpecies.toString(),
|
||||
@ -466,10 +466,10 @@ function getPartyConfig(): EnemyPartyConfig {
|
||||
// First mon is *always* this special cleffa
|
||||
const cleffaSpecies =
|
||||
waveIndex < FIRST_STAGE_EVOLUTION_WAVE
|
||||
? Species.CLEFFA
|
||||
? SpeciesId.CLEFFA
|
||||
: waveIndex < FINAL_STAGE_EVOLUTION_WAVE
|
||||
? Species.CLEFAIRY
|
||||
: Species.CLEFABLE;
|
||||
? SpeciesId.CLEFAIRY
|
||||
: SpeciesId.CLEFABLE;
|
||||
const baseConfig: EnemyPartyConfig = {
|
||||
trainerType: TrainerType.EXPERT_POKEMON_BREEDER,
|
||||
pokemonConfigs: [
|
||||
@ -482,14 +482,14 @@ function getPartyConfig(): EnemyPartyConfig {
|
||||
abilityIndex: 1, // Magic Guard
|
||||
shiny: false,
|
||||
nature: Nature.ADAMANT,
|
||||
moveSet: [Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH, Moves.METEOR_MASH],
|
||||
moveSet: [MoveId.FIRE_PUNCH, MoveId.ICE_PUNCH, MoveId.THUNDER_PUNCH, MoveId.METEOR_MASH],
|
||||
ivs: [31, 31, 31, 31, 31, 31],
|
||||
tera: PokemonType.FAIRY,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
if (globalScene.arena.biomeType === Biome.SPACE) {
|
||||
if (globalScene.arena.biomeType === BiomeId.SPACE) {
|
||||
// All 3 members always Cleffa line, but different configs
|
||||
baseConfig.pokemonConfigs!.push(
|
||||
{
|
||||
@ -502,7 +502,7 @@ function getPartyConfig(): EnemyPartyConfig {
|
||||
shiny: true,
|
||||
variant: 1,
|
||||
nature: Nature.MODEST,
|
||||
moveSet: [Moves.MOONBLAST, Moves.MYSTICAL_FIRE, Moves.ICE_BEAM, Moves.THUNDERBOLT],
|
||||
moveSet: [MoveId.MOONBLAST, MoveId.MYSTICAL_FIRE, MoveId.ICE_BEAM, MoveId.THUNDERBOLT],
|
||||
ivs: [31, 31, 31, 31, 31, 31],
|
||||
},
|
||||
{
|
||||
@ -515,7 +515,7 @@ function getPartyConfig(): EnemyPartyConfig {
|
||||
shiny: true,
|
||||
variant: 2,
|
||||
nature: Nature.BOLD,
|
||||
moveSet: [Moves.TRI_ATTACK, Moves.STORED_POWER, Moves.TAKE_HEART, Moves.MOONLIGHT],
|
||||
moveSet: [MoveId.TRI_ATTACK, MoveId.STORED_POWER, MoveId.TAKE_HEART, MoveId.MOONLIGHT],
|
||||
ivs: [31, 31, 31, 31, 31, 31],
|
||||
},
|
||||
);
|
||||
@ -542,7 +542,7 @@ function getPartyConfig(): EnemyPartyConfig {
|
||||
return baseConfig;
|
||||
}
|
||||
|
||||
function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species {
|
||||
function getSpeciesFromPool(speciesPool: (SpeciesId | BreederSpeciesEvolution)[][], waveIndex: number): SpeciesId {
|
||||
const poolCopy = randSeedShuffle(speciesPool.slice(0));
|
||||
const speciesEvolutions = poolCopy.pop()!.slice(0);
|
||||
let speciesObject = speciesEvolutions.pop()!;
|
||||
@ -658,8 +658,8 @@ function onGameOver() {
|
||||
globalScene.playBgm(globalScene.arena.bgm);
|
||||
|
||||
// Clear any leftover battle phases
|
||||
globalScene.clearPhaseQueue();
|
||||
globalScene.clearPhaseQueueSplice();
|
||||
globalScene.phaseManager.clearPhaseQueue();
|
||||
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||
|
||||
// Return enemy Pokemon
|
||||
const pokemon = globalScene.getEnemyPokemon();
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
@ -27,7 +27,7 @@ import PokemonData from "#app/system/pokemon-data";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { NON_LEGEND_PARADOX_POKEMON, NON_LEGEND_ULTRA_BEASTS } from "#app/data/balance/special-species-groups";
|
||||
import { timedEventManager } from "#app/global-event-manager";
|
||||
|
||||
@ -81,7 +81,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
let tries = 0;
|
||||
|
||||
// Reroll any species that don't have HAs
|
||||
while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && tries < 5) {
|
||||
while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE) && tries < 5) {
|
||||
species = getSalesmanSpeciesOffer();
|
||||
tries++;
|
||||
}
|
||||
@ -110,15 +110,15 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
*/
|
||||
if (
|
||||
r === 0 ||
|
||||
((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) &&
|
||||
((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE) &&
|
||||
validEventEncounters.length === 0)
|
||||
) {
|
||||
// If you roll 1%, give shiny Magikarp with random variant
|
||||
species = getPokemonSpecies(Species.MAGIKARP);
|
||||
species = getPokemonSpecies(SpeciesId.MAGIKARP);
|
||||
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
|
||||
} else if (
|
||||
validEventEncounters.length > 0 &&
|
||||
(r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE)
|
||||
(r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === AbilityId.NONE)
|
||||
) {
|
||||
tries = 0;
|
||||
do {
|
||||
@ -128,7 +128,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
pokemon = new PlayerPokemon(
|
||||
species,
|
||||
5,
|
||||
species.abilityHidden === Abilities.NONE ? undefined : 2,
|
||||
species.abilityHidden === AbilityId.NONE ? undefined : 2,
|
||||
enc.formIndex,
|
||||
);
|
||||
pokemon.trySetShinySeed();
|
||||
@ -151,7 +151,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
|
||||
pokemon.trySetShinySeed();
|
||||
} else {
|
||||
// If there's, and this would never happen, no eligible event encounters with a hidden ability, just do Magikarp
|
||||
species = getPokemonSpecies(Species.MAGIKARP);
|
||||
species = getPokemonSpecies(SpeciesId.MAGIKARP);
|
||||
pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true);
|
||||
}
|
||||
}
|
||||
|
@ -8,26 +8,25 @@ import {
|
||||
generateModifierType,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Nature } from "#enums/nature";
|
||||
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 { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { CustomPokemonData } from "#app/data/pokemon/pokemon-data";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
@ -64,7 +63,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
disableAnimation: true,
|
||||
},
|
||||
{
|
||||
spriteKey: Species.SHUCKLE.toString(),
|
||||
spriteKey: SpeciesId.SHUCKLE.toString(),
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
@ -88,13 +87,13 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
disableSwitch: true,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.SHUCKLE),
|
||||
species: getPokemonSpecies(SpeciesId.SHUCKLE),
|
||||
isBoss: true,
|
||||
bossSegments: 5,
|
||||
shiny: false, // Shiny lock because shiny is rolled only if the battle option is picked
|
||||
customPokemonData: new CustomPokemonData({ spriteScale: 1.25 }),
|
||||
nature: Nature.HARDY,
|
||||
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
||||
moveSet: [MoveId.INFESTATION, MoveId.SALT_CURE, MoveId.GASTRO_ACID, MoveId.HEAL_ORDER],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType,
|
||||
@ -116,8 +115,12 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.2.stat_boost`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
[Stat.DEF, Stat.SPDEF],
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -126,9 +129,9 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
|
||||
encounter.enemyPartyConfigs = [config];
|
||||
|
||||
loadCustomMovesForEncounter([Moves.GASTRO_ACID, Moves.STEALTH_ROCK]);
|
||||
loadCustomMovesForEncounter([MoveId.GASTRO_ACID, MoveId.STEALTH_ROCK]);
|
||||
|
||||
encounter.setDialogueToken("shuckleName", getPokemonSpecies(Species.SHUCKLE).getName());
|
||||
encounter.setDialogueToken("shuckleName", getPokemonSpecies(SpeciesId.SHUCKLE).getName());
|
||||
|
||||
return true;
|
||||
})
|
||||
@ -210,13 +213,13 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
move: new PokemonMove(Moves.GASTRO_ACID),
|
||||
move: new PokemonMove(MoveId.GASTRO_ACID),
|
||||
ignorePp: true,
|
||||
},
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
move: new PokemonMove(Moves.STEALTH_ROCK),
|
||||
move: new PokemonMove(MoveId.STEALTH_ROCK),
|
||||
ignorePp: true,
|
||||
},
|
||||
);
|
||||
|
@ -8,30 +8,27 @@ import {
|
||||
transitionMysteryEncounterIntroVisuals,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms";
|
||||
import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/abilities/ability";
|
||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { applyPostBattleInitAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
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 { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { ModifierTier } from "#enums/modifier-tier";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
|
||||
@ -143,7 +140,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounter
|
||||
},
|
||||
async () => {
|
||||
// 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({
|
||||
guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY],
|
||||
fillRemaining: false,
|
||||
@ -209,14 +206,14 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
|
||||
for (const pokemon of playerField) {
|
||||
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()) {
|
||||
// Only trigger form change when Eiscue is in Noice form
|
||||
// Hardcoded Eiscue for now in case it is fused with another pokemon
|
||||
if (
|
||||
pokemon.species.speciesId === Species.EISCUE &&
|
||||
pokemon.hasAbility(Abilities.ICE_FACE) &&
|
||||
pokemon.species.speciesId === SpeciesId.EISCUE &&
|
||||
pokemon.hasAbility(AbilityId.ICE_FACE) &&
|
||||
pokemon.formIndex === 1
|
||||
) {
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger);
|
||||
@ -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
|
||||
pokemon.resetBattleAndWaveData();
|
||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
||||
applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon);
|
||||
}
|
||||
|
||||
globalScene.unshiftPhase(new ShowTrainerPhase());
|
||||
globalScene.phaseManager.unshiftNew("ShowTrainerPhase");
|
||||
// Hide the trainer and init next battle
|
||||
const trainer = globalScene.currentBattle.trainer;
|
||||
// Unassign previous trainer from battle so it isn't destroyed before animation completes
|
||||
@ -256,11 +253,11 @@ function getVictorTrainerConfig(): EnemyPartyConfig {
|
||||
trainerType: TrainerType.VICTOR,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.SWELLOW),
|
||||
species: getPokemonSpecies(SpeciesId.SWELLOW),
|
||||
isBoss: false,
|
||||
abilityIndex: 0, // Guts
|
||||
nature: Nature.ADAMANT,
|
||||
moveSet: [Moves.FACADE, Moves.BRAVE_BIRD, Moves.PROTECT, Moves.QUICK_ATTACK],
|
||||
moveSet: [MoveId.FACADE, MoveId.BRAVE_BIRD, MoveId.PROTECT, MoveId.QUICK_ATTACK],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
|
||||
@ -274,11 +271,11 @@ function getVictorTrainerConfig(): EnemyPartyConfig {
|
||||
],
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.OBSTAGOON),
|
||||
species: getPokemonSpecies(SpeciesId.OBSTAGOON),
|
||||
isBoss: false,
|
||||
abilityIndex: 1, // Guts
|
||||
nature: Nature.ADAMANT,
|
||||
moveSet: [Moves.FACADE, Moves.OBSTRUCT, Moves.NIGHT_SLASH, Moves.FIRE_PUNCH],
|
||||
moveSet: [MoveId.FACADE, MoveId.OBSTRUCT, MoveId.NIGHT_SLASH, MoveId.FIRE_PUNCH],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.FLAME_ORB) as PokemonHeldItemModifierType,
|
||||
@ -300,11 +297,11 @@ function getVictoriaTrainerConfig(): EnemyPartyConfig {
|
||||
trainerType: TrainerType.VICTORIA,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.ROSERADE),
|
||||
species: getPokemonSpecies(SpeciesId.ROSERADE),
|
||||
isBoss: false,
|
||||
abilityIndex: 0, // Natural Cure
|
||||
nature: Nature.CALM,
|
||||
moveSet: [Moves.SYNTHESIS, Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.SLEEP_POWDER],
|
||||
moveSet: [MoveId.SYNTHESIS, MoveId.SLUDGE_BOMB, MoveId.GIGA_DRAIN, MoveId.SLEEP_POWDER],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType,
|
||||
@ -318,11 +315,11 @@ function getVictoriaTrainerConfig(): EnemyPartyConfig {
|
||||
],
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.GARDEVOIR),
|
||||
species: getPokemonSpecies(SpeciesId.GARDEVOIR),
|
||||
isBoss: false,
|
||||
formIndex: 1,
|
||||
nature: Nature.TIMID,
|
||||
moveSet: [Moves.PSYSHOCK, Moves.MOONBLAST, Moves.SHADOW_BALL, Moves.WILL_O_WISP],
|
||||
moveSet: [MoveId.PSYSHOCK, MoveId.MOONBLAST, MoveId.SHADOW_BALL, MoveId.WILL_O_WISP],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [
|
||||
@ -349,11 +346,11 @@ function getViviTrainerConfig(): EnemyPartyConfig {
|
||||
trainerType: TrainerType.VIVI,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.SEAKING),
|
||||
species: getPokemonSpecies(SpeciesId.SEAKING),
|
||||
isBoss: false,
|
||||
abilityIndex: 3, // Lightning Rod
|
||||
nature: Nature.ADAMANT,
|
||||
moveSet: [Moves.WATERFALL, Moves.MEGAHORN, Moves.KNOCK_OFF, Moves.REST],
|
||||
moveSet: [MoveId.WATERFALL, MoveId.MEGAHORN, MoveId.KNOCK_OFF, MoveId.REST],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [BerryType.LUM]) as PokemonHeldItemModifierType,
|
||||
@ -368,11 +365,11 @@ function getViviTrainerConfig(): EnemyPartyConfig {
|
||||
],
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.BRELOOM),
|
||||
species: getPokemonSpecies(SpeciesId.BRELOOM),
|
||||
isBoss: false,
|
||||
abilityIndex: 1, // Poison Heal
|
||||
nature: Nature.JOLLY,
|
||||
moveSet: [Moves.SPORE, Moves.SWORDS_DANCE, Moves.SEED_BOMB, Moves.DRAIN_PUNCH],
|
||||
moveSet: [MoveId.SPORE, MoveId.SWORDS_DANCE, MoveId.SEED_BOMB, MoveId.DRAIN_PUNCH],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [Stat.HP]) as PokemonHeldItemModifierType,
|
||||
@ -386,11 +383,11 @@ function getViviTrainerConfig(): EnemyPartyConfig {
|
||||
],
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.CAMERUPT),
|
||||
species: getPokemonSpecies(SpeciesId.CAMERUPT),
|
||||
isBoss: false,
|
||||
formIndex: 1,
|
||||
nature: Nature.CALM,
|
||||
moveSet: [Moves.EARTH_POWER, Moves.FIRE_BLAST, Moves.YAWN, Moves.PROTECT],
|
||||
moveSet: [MoveId.EARTH_POWER, MoveId.FIRE_BLAST, MoveId.YAWN, MoveId.PROTECT],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||
@ -408,11 +405,11 @@ function getVickyTrainerConfig(): EnemyPartyConfig {
|
||||
trainerType: TrainerType.VICKY,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.MEDICHAM),
|
||||
species: getPokemonSpecies(SpeciesId.MEDICHAM),
|
||||
isBoss: false,
|
||||
formIndex: 1,
|
||||
nature: Nature.IMPISH,
|
||||
moveSet: [Moves.AXE_KICK, Moves.ICE_PUNCH, Moves.ZEN_HEADBUTT, Moves.BULLET_PUNCH],
|
||||
moveSet: [MoveId.AXE_KICK, MoveId.ICE_PUNCH, MoveId.ZEN_HEADBUTT, MoveId.BULLET_PUNCH],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType,
|
||||
@ -429,11 +426,11 @@ function getVitoTrainerConfig(): EnemyPartyConfig {
|
||||
trainerType: TrainerType.VITO,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.HISUI_ELECTRODE),
|
||||
species: getPokemonSpecies(SpeciesId.HISUI_ELECTRODE),
|
||||
isBoss: false,
|
||||
abilityIndex: 0, // Soundproof
|
||||
nature: Nature.MODEST,
|
||||
moveSet: [Moves.THUNDERBOLT, Moves.GIGA_DRAIN, Moves.FOUL_PLAY, Moves.THUNDER_WAVE],
|
||||
moveSet: [MoveId.THUNDERBOLT, MoveId.GIGA_DRAIN, MoveId.FOUL_PLAY, MoveId.THUNDER_WAVE],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [Stat.SPD]) as PokemonHeldItemModifierType,
|
||||
@ -443,11 +440,11 @@ function getVitoTrainerConfig(): EnemyPartyConfig {
|
||||
],
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.SWALOT),
|
||||
species: getPokemonSpecies(SpeciesId.SWALOT),
|
||||
isBoss: false,
|
||||
abilityIndex: 2, // Gluttony
|
||||
nature: Nature.QUIET,
|
||||
moveSet: [Moves.SLUDGE_BOMB, Moves.GIGA_DRAIN, Moves.ICE_BEAM, Moves.EARTHQUAKE],
|
||||
moveSet: [MoveId.SLUDGE_BOMB, MoveId.GIGA_DRAIN, MoveId.ICE_BEAM, MoveId.EARTHQUAKE],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType,
|
||||
@ -496,11 +493,11 @@ function getVitoTrainerConfig(): EnemyPartyConfig {
|
||||
],
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.DODRIO),
|
||||
species: getPokemonSpecies(SpeciesId.DODRIO),
|
||||
isBoss: false,
|
||||
abilityIndex: 2, // Tangled Feet
|
||||
nature: Nature.JOLLY,
|
||||
moveSet: [Moves.DRILL_PECK, Moves.QUICK_ATTACK, Moves.THRASH, Moves.KNOCK_OFF],
|
||||
moveSet: [MoveId.DRILL_PECK, MoveId.QUICK_ATTACK, MoveId.THRASH, MoveId.KNOCK_OFF],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.KINGS_ROCK) as PokemonHeldItemModifierType,
|
||||
@ -510,11 +507,11 @@ function getVitoTrainerConfig(): EnemyPartyConfig {
|
||||
],
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.ALAKAZAM),
|
||||
species: getPokemonSpecies(SpeciesId.ALAKAZAM),
|
||||
isBoss: false,
|
||||
formIndex: 1,
|
||||
nature: Nature.BOLD,
|
||||
moveSet: [Moves.PSYCHIC, Moves.SHADOW_BALL, Moves.FOCUS_BLAST, Moves.THUNDERBOLT],
|
||||
moveSet: [MoveId.PSYCHIC, MoveId.SHADOW_BALL, MoveId.FOCUS_BLAST, MoveId.THUNDERBOLT],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.WIDE_LENS) as PokemonHeldItemModifierType,
|
||||
@ -524,11 +521,11 @@ function getVitoTrainerConfig(): EnemyPartyConfig {
|
||||
],
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.DARMANITAN),
|
||||
species: getPokemonSpecies(SpeciesId.DARMANITAN),
|
||||
isBoss: false,
|
||||
abilityIndex: 0, // Sheer Force
|
||||
nature: Nature.IMPISH,
|
||||
moveSet: [Moves.EARTHQUAKE, Moves.U_TURN, Moves.FLARE_BLITZ, Moves.ROCK_SLIDE],
|
||||
moveSet: [MoveId.EARTHQUAKE, MoveId.U_TURN, MoveId.FLARE_BLITZ, MoveId.ROCK_SLIDE],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.QUICK_CLAW) as PokemonHeldItemModifierType,
|
||||
|
@ -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 type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import {
|
||||
@ -12,7 +12,7 @@ import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { AbilityAttr } from "#app/system/game-data";
|
||||
import { AbilityAttr } from "#enums/ability-attr";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
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 { getStatKey } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
transitionMysteryEncounterIntroVisuals,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { modifierTypes } from "#app/data/data-lists";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -16,16 +16,16 @@ import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-en
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { HitHealModifier, PokemonHeldItemModifier, TurnHealModifier } from "#app/modifier/modifier";
|
||||
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
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 { Moves } from "#enums/moves";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
import { randSeedInt } from "#app/utils/common";
|
||||
|
||||
@ -51,7 +51,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
.withFleeAllowed(false)
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: Species.GARBODOR.toString() + "-gigantamax",
|
||||
spriteKey: SpeciesId.GARBODOR.toString() + "-gigantamax",
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: false,
|
||||
disableAnimation: true,
|
||||
@ -74,14 +74,14 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||
|
||||
// Calculate boss mon (shiny locked)
|
||||
const bossSpecies = getPokemonSpecies(Species.GARBODOR);
|
||||
const bossSpecies = getPokemonSpecies(SpeciesId.GARBODOR);
|
||||
const pokemonConfig: EnemyPokemonConfig = {
|
||||
species: bossSpecies,
|
||||
isBoss: true,
|
||||
shiny: false, // Shiny lock because of custom intro sprite
|
||||
formIndex: 1, // Gmax
|
||||
bossSegmentModifier: 1, // +1 Segment from normal
|
||||
moveSet: [Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.HAMMER_ARM, Moves.PAYBACK],
|
||||
moveSet: [MoveId.GUNK_SHOT, MoveId.STOMPING_TANTRUM, MoveId.HAMMER_ARM, MoveId.PAYBACK],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||
@ -127,7 +127,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
encounter.enemyPartyConfigs = [config];
|
||||
|
||||
// Load animations/sfx for Garbodor fight start moves
|
||||
loadCustomMovesForEncounter([Moves.TOXIC, Moves.STOCKPILE]);
|
||||
loadCustomMovesForEncounter([MoveId.TOXIC, MoveId.STOCKPILE]);
|
||||
|
||||
globalScene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
|
||||
globalScene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
|
||||
@ -206,13 +206,13 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
move: new PokemonMove(Moves.TOXIC),
|
||||
move: new PokemonMove(MoveId.TOXIC),
|
||||
ignorePp: true,
|
||||
},
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.ENEMY],
|
||||
move: new PokemonMove(Moves.STOCKPILE),
|
||||
move: new PokemonMove(MoveId.STOCKPILE),
|
||||
ignorePp: true,
|
||||
},
|
||||
);
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||
import type Pokemon 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 { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -28,14 +28,12 @@ import {
|
||||
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { SelfStatusMove } from "#app/data/moves/move";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { BerryModifier } from "#app/modifier/modifier";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||
|
||||
@ -73,7 +71,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
const eggMoves = pokemon.getEggMoves();
|
||||
if (eggMoves) {
|
||||
const eggMoveIndex = randSeedInt(4);
|
||||
const randomEggMove: Moves = eggMoves[eggMoveIndex];
|
||||
const randomEggMove: MoveId = eggMoves[eggMoveIndex];
|
||||
encounter.misc = {
|
||||
eggMove: randomEggMove,
|
||||
pokemon: pokemon,
|
||||
@ -103,8 +101,12 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||
queueEncounterMessage(`${namespace}:option.1.stat_boost`);
|
||||
globalScene.unshiftPhase(
|
||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1),
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
pokemon.getBattlerIndex(),
|
||||
true,
|
||||
statChangesForBattle,
|
||||
1,
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -172,7 +174,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
// Check what type of move the egg move is to determine target
|
||||
const pokemonMove = new PokemonMove(eggMove);
|
||||
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({
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
@ -270,10 +272,10 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
||||
)
|
||||
.build();
|
||||
|
||||
function givePokemonExtraEggMove(pokemon: EnemyPokemon, previousEggMove: Moves) {
|
||||
function givePokemonExtraEggMove(pokemon: EnemyPokemon, previousEggMove: MoveId) {
|
||||
const eggMoves = pokemon.getEggMoves();
|
||||
if (eggMoves) {
|
||||
let randomEggMove: Moves = eggMoves[randSeedInt(4)];
|
||||
let randomEggMove: MoveId = eggMoves[randSeedInt(4)];
|
||||
while (randomEggMove === previousEggMove) {
|
||||
randomEggMove = eggMoves[randSeedInt(4)];
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
@ -16,7 +16,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import type { PlayerPokemon } 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 type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
@ -25,7 +25,7 @@ import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier } from
|
||||
import { achvs } from "#app/system/achv";
|
||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
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 {
|
||||
doPokemonTransformationSequence,
|
||||
@ -34,12 +34,12 @@ import {
|
||||
import { getLevelTotalExp } from "#app/data/exp";
|
||||
import { Stat } from "#enums/stat";
|
||||
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 { TrainerType } from "#enums/trainer-type";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
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 { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
@ -49,55 +49,55 @@ const namespace = "mysteryEncounters/weirdDream";
|
||||
|
||||
/** Exclude Ultra Beasts, Paradox, Eternatus, and all legendary/mythical/trio pokemon that are below 570 BST */
|
||||
const EXCLUDED_TRANSFORMATION_SPECIES = [
|
||||
Species.ETERNATUS,
|
||||
SpeciesId.ETERNATUS,
|
||||
/** UBs */
|
||||
Species.NIHILEGO,
|
||||
Species.BUZZWOLE,
|
||||
Species.PHEROMOSA,
|
||||
Species.XURKITREE,
|
||||
Species.CELESTEELA,
|
||||
Species.KARTANA,
|
||||
Species.GUZZLORD,
|
||||
Species.POIPOLE,
|
||||
Species.NAGANADEL,
|
||||
Species.STAKATAKA,
|
||||
Species.BLACEPHALON,
|
||||
SpeciesId.NIHILEGO,
|
||||
SpeciesId.BUZZWOLE,
|
||||
SpeciesId.PHEROMOSA,
|
||||
SpeciesId.XURKITREE,
|
||||
SpeciesId.CELESTEELA,
|
||||
SpeciesId.KARTANA,
|
||||
SpeciesId.GUZZLORD,
|
||||
SpeciesId.POIPOLE,
|
||||
SpeciesId.NAGANADEL,
|
||||
SpeciesId.STAKATAKA,
|
||||
SpeciesId.BLACEPHALON,
|
||||
/** Paradox */
|
||||
Species.GREAT_TUSK,
|
||||
Species.SCREAM_TAIL,
|
||||
Species.BRUTE_BONNET,
|
||||
Species.FLUTTER_MANE,
|
||||
Species.SLITHER_WING,
|
||||
Species.SANDY_SHOCKS,
|
||||
Species.ROARING_MOON,
|
||||
Species.WALKING_WAKE,
|
||||
Species.GOUGING_FIRE,
|
||||
Species.RAGING_BOLT,
|
||||
Species.IRON_TREADS,
|
||||
Species.IRON_BUNDLE,
|
||||
Species.IRON_HANDS,
|
||||
Species.IRON_JUGULIS,
|
||||
Species.IRON_MOTH,
|
||||
Species.IRON_THORNS,
|
||||
Species.IRON_VALIANT,
|
||||
Species.IRON_LEAVES,
|
||||
Species.IRON_BOULDER,
|
||||
Species.IRON_CROWN,
|
||||
SpeciesId.GREAT_TUSK,
|
||||
SpeciesId.SCREAM_TAIL,
|
||||
SpeciesId.BRUTE_BONNET,
|
||||
SpeciesId.FLUTTER_MANE,
|
||||
SpeciesId.SLITHER_WING,
|
||||
SpeciesId.SANDY_SHOCKS,
|
||||
SpeciesId.ROARING_MOON,
|
||||
SpeciesId.WALKING_WAKE,
|
||||
SpeciesId.GOUGING_FIRE,
|
||||
SpeciesId.RAGING_BOLT,
|
||||
SpeciesId.IRON_TREADS,
|
||||
SpeciesId.IRON_BUNDLE,
|
||||
SpeciesId.IRON_HANDS,
|
||||
SpeciesId.IRON_JUGULIS,
|
||||
SpeciesId.IRON_MOTH,
|
||||
SpeciesId.IRON_THORNS,
|
||||
SpeciesId.IRON_VALIANT,
|
||||
SpeciesId.IRON_LEAVES,
|
||||
SpeciesId.IRON_BOULDER,
|
||||
SpeciesId.IRON_CROWN,
|
||||
/** These are banned so they don't appear in the < 570 BST pool */
|
||||
Species.COSMOG,
|
||||
Species.MELTAN,
|
||||
Species.KUBFU,
|
||||
Species.COSMOEM,
|
||||
Species.POIPOLE,
|
||||
Species.TERAPAGOS,
|
||||
Species.TYPE_NULL,
|
||||
Species.CALYREX,
|
||||
Species.NAGANADEL,
|
||||
Species.URSHIFU,
|
||||
Species.OGERPON,
|
||||
Species.OKIDOGI,
|
||||
Species.MUNKIDORI,
|
||||
Species.FEZANDIPITI,
|
||||
SpeciesId.COSMOG,
|
||||
SpeciesId.MELTAN,
|
||||
SpeciesId.KUBFU,
|
||||
SpeciesId.COSMOEM,
|
||||
SpeciesId.POIPOLE,
|
||||
SpeciesId.TERAPAGOS,
|
||||
SpeciesId.TYPE_NULL,
|
||||
SpeciesId.CALYREX,
|
||||
SpeciesId.NAGANADEL,
|
||||
SpeciesId.URSHIFU,
|
||||
SpeciesId.OGERPON,
|
||||
SpeciesId.OKIDOGI,
|
||||
SpeciesId.MUNKIDORI,
|
||||
SpeciesId.FEZANDIPITI,
|
||||
];
|
||||
|
||||
const SUPER_LEGENDARY_BST_THRESHOLD = 600;
|
||||
@ -500,7 +500,7 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) {
|
||||
async function postProcessTransformedPokemon(
|
||||
previousPokemon: PlayerPokemon,
|
||||
newPokemon: PlayerPokemon,
|
||||
speciesRootForm: Species,
|
||||
speciesRootForm: SpeciesId,
|
||||
forBattle = false,
|
||||
): Promise<boolean> {
|
||||
let isNewStarter = false;
|
||||
@ -768,7 +768,7 @@ function doSideBySideTransformations(transformations: PokemonTransformation[]) {
|
||||
*/
|
||||
async function addEggMoveToNewPokemonMoveset(
|
||||
newPokemon: PlayerPokemon,
|
||||
speciesRootForm: Species,
|
||||
speciesRootForm: SpeciesId,
|
||||
forBattle = false,
|
||||
): Promise<number | null> {
|
||||
let eggMoveIndex: null | number = null;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { OptionTextDisplay } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
||||
import type { Moves } from "#app/enums/moves";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
@ -300,7 +300,7 @@ export class MysteryEncounterOptionBuilder implements Partial<IMysteryEncounterO
|
||||
* @param options see {@linkcode CanLearnMoveRequirementOptions}
|
||||
* @returns
|
||||
*/
|
||||
withPokemonCanLearnMoveRequirement(move: Moves | Moves[], options?: CanLearnMoveRequirementOptions) {
|
||||
withPokemonCanLearnMoveRequirement(move: MoveId | MoveId[], options?: CanLearnMoveRequirementOptions) {
|
||||
return this.withPrimaryPokemonRequirement(new CanLearnMoveRequirement(move, options));
|
||||
}
|
||||
|
||||
|
@ -2,18 +2,20 @@ import { globalScene } from "#app/global-scene";
|
||||
import { allAbilities } from "../data-lists";
|
||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
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 { PokemonType } from "#enums/pokemon-type";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
||||
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||
import { isNullOrUndefined } from "#app/utils/common";
|
||||
import type { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { coerceArray, isNullOrUndefined } from "#app/utils/common";
|
||||
import type { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { Species } from "#enums/species";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
|
||||
@ -270,20 +272,16 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
||||
|
||||
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
||||
super();
|
||||
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [timeOfDay];
|
||||
this.requiredTimeOfDay = coerceArray(timeOfDay);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
const timeOfDay = globalScene.arena?.getTimeOfDay();
|
||||
if (
|
||||
return !(
|
||||
!isNullOrUndefined(timeOfDay) &&
|
||||
this.requiredTimeOfDay?.length > 0 &&
|
||||
!this.requiredTimeOfDay.includes(timeOfDay)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
);
|
||||
}
|
||||
|
||||
override getDialogueToken(_pokemon?: PlayerPokemon): [string, string] {
|
||||
@ -296,20 +294,16 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
||||
|
||||
constructor(weather: WeatherType | WeatherType[]) {
|
||||
super();
|
||||
this.requiredWeather = Array.isArray(weather) ? weather : [weather];
|
||||
this.requiredWeather = coerceArray(weather);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
const currentWeather = globalScene.arena.weather?.weatherType;
|
||||
if (
|
||||
return !(
|
||||
!isNullOrUndefined(currentWeather) &&
|
||||
this.requiredWeather?.length > 0 &&
|
||||
!this.requiredWeather.includes(currentWeather!)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
);
|
||||
}
|
||||
|
||||
override getDialogueToken(_pokemon?: PlayerPokemon): [string, string] {
|
||||
@ -366,7 +360,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
|
||||
constructor(heldItem: string | string[], minNumberOfItems = 1) {
|
||||
super();
|
||||
this.minNumberOfItems = minNumberOfItems;
|
||||
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
||||
this.requiredHeldItemModifiers = coerceArray(heldItem);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -424,15 +418,15 @@ export class MoneyRequirement extends EncounterSceneRequirement {
|
||||
}
|
||||
|
||||
export class SpeciesRequirement extends EncounterPokemonRequirement {
|
||||
requiredSpecies: Species[];
|
||||
requiredSpecies: SpeciesId[];
|
||||
minNumberOfPokemon: number;
|
||||
invertQuery: boolean;
|
||||
|
||||
constructor(species: Species | Species[], minNumberOfPokemon = 1, invertQuery = false) {
|
||||
constructor(species: SpeciesId | SpeciesId[], minNumberOfPokemon = 1, invertQuery = false) {
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredSpecies = Array.isArray(species) ? species : [species];
|
||||
this.requiredSpecies = coerceArray(species);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -457,7 +451,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
|
||||
|
||||
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
|
||||
if (pokemon?.species.speciesId && this.requiredSpecies.includes(pokemon.species.speciesId)) {
|
||||
return ["species", Species[pokemon.species.speciesId]];
|
||||
return ["species", SpeciesId[pokemon.species.speciesId]];
|
||||
}
|
||||
return ["species", ""];
|
||||
}
|
||||
@ -472,7 +466,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredNature = Array.isArray(nature) ? nature : [nature];
|
||||
this.requiredNature = coerceArray(nature);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -510,7 +504,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
||||
this.excludeFainted = excludeFainted;
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredType = Array.isArray(type) ? type : [type];
|
||||
this.requiredType = coerceArray(type);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -549,17 +543,22 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
||||
}
|
||||
|
||||
export class MoveRequirement extends EncounterPokemonRequirement {
|
||||
requiredMoves: Moves[] = [];
|
||||
requiredMoves: MoveId[] = [];
|
||||
minNumberOfPokemon: number;
|
||||
invertQuery: boolean;
|
||||
excludeDisallowedPokemon: boolean;
|
||||
|
||||
constructor(moves: Moves | Moves[], excludeDisallowedPokemon: boolean, minNumberOfPokemon = 1, invertQuery = false) {
|
||||
constructor(
|
||||
moves: MoveId | MoveId[],
|
||||
excludeDisallowedPokemon: boolean,
|
||||
minNumberOfPokemon = 1,
|
||||
invertQuery = false,
|
||||
) {
|
||||
super();
|
||||
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredMoves = Array.isArray(moves) ? moves : [moves];
|
||||
this.requiredMoves = coerceArray(moves);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -602,15 +601,15 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
||||
* NOTE: If the Pokemon already knows the move, this requirement will fail, since it's not technically learnable.
|
||||
*/
|
||||
export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
||||
requiredMoves: Moves[];
|
||||
requiredMoves: MoveId[];
|
||||
minNumberOfPokemon: number;
|
||||
invertQuery: boolean;
|
||||
|
||||
constructor(learnableMove: Moves | Moves[], minNumberOfPokemon = 1, invertQuery = false) {
|
||||
constructor(learnableMove: MoveId | MoveId[], minNumberOfPokemon = 1, invertQuery = false) {
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [learnableMove];
|
||||
this.requiredMoves = coerceArray(learnableMove);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -644,20 +643,20 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
||||
pokemon?.compatibleTms.filter(tm => !pokemon.moveset.find(m => m.moveId === tm)).includes(reqMove),
|
||||
);
|
||||
if (includedCompatMoves.length > 0) {
|
||||
return ["compatibleMove", Moves[includedCompatMoves[0]]];
|
||||
return ["compatibleMove", MoveId[includedCompatMoves[0]]];
|
||||
}
|
||||
return ["compatibleMove", ""];
|
||||
}
|
||||
}
|
||||
|
||||
export class AbilityRequirement extends EncounterPokemonRequirement {
|
||||
requiredAbilities: Abilities[];
|
||||
requiredAbilities: AbilityId[];
|
||||
minNumberOfPokemon: number;
|
||||
invertQuery: boolean;
|
||||
excludeDisallowedPokemon: boolean;
|
||||
|
||||
constructor(
|
||||
abilities: Abilities | Abilities[],
|
||||
abilities: AbilityId | AbilityId[],
|
||||
excludeDisallowedPokemon: boolean,
|
||||
minNumberOfPokemon = 1,
|
||||
invertQuery = false,
|
||||
@ -666,7 +665,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
||||
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredAbilities = Array.isArray(abilities) ? abilities : [abilities];
|
||||
this.requiredAbilities = coerceArray(abilities);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -711,7 +710,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [statusEffect];
|
||||
this.requiredStatusEffect = coerceArray(statusEffect);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -786,7 +785,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [formChangeItem];
|
||||
this.requiredFormChangeItem = coerceArray(formChangeItem);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -798,7 +797,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
||||
}
|
||||
|
||||
filterByForm(pokemon, formChangeItem) {
|
||||
if (
|
||||
return (
|
||||
pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) &&
|
||||
// Get all form changes for this species with an item trigger, including any compound triggers
|
||||
pokemonFormChanges[pokemon.species.speciesId]
|
||||
@ -807,10 +806,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
||||
.flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
|
||||
.flatMap(fc => fc.item)
|
||||
.includes(formChangeItem)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
);
|
||||
}
|
||||
|
||||
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
||||
@ -847,7 +843,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [evolutionItems];
|
||||
this.requiredEvolutionItem = coerceArray(evolutionItems);
|
||||
}
|
||||
|
||||
override meetsRequirement(): boolean {
|
||||
@ -868,17 +864,15 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
|
||||
return (
|
||||
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[] {
|
||||
@ -914,7 +908,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
||||
this.requiredHeldItemModifiers = coerceArray(heldItem);
|
||||
this.requireTransferable = requireTransferable;
|
||||
}
|
||||
|
||||
@ -978,7 +972,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
||||
super();
|
||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||
this.invertQuery = invertQuery;
|
||||
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [heldItemTypes];
|
||||
this.requiredHeldItemTypes = coerceArray(heldItemTypes);
|
||||
this.requireTransferable = requireTransferable;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 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 { 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 { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils/common";
|
||||
import { capitalizeFirstLetter, coerceArray, isNullOrUndefined } from "#app/utils/common";
|
||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
||||
import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro";
|
||||
@ -20,11 +21,11 @@ import {
|
||||
StatusEffectRequirement,
|
||||
WaveRangeRequirement,
|
||||
} 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 { MysteryEncounterMode } from "#enums/mystery-encounter-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 { Challenges } from "#enums/challenges";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
@ -716,7 +717,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
withAnimations(
|
||||
...encounterAnimations: EncounterAnim[]
|
||||
): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
|
||||
const animations = Array.isArray(encounterAnimations) ? encounterAnimations : [encounterAnimations];
|
||||
const animations = coerceArray(encounterAnimations);
|
||||
return Object.assign(this, { encounterAnimations: animations });
|
||||
}
|
||||
|
||||
@ -728,7 +729,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
withDisallowedGameModes(
|
||||
...disallowedGameModes: GameModes[]
|
||||
): this & Required<Pick<IMysteryEncounter, "disallowedGameModes">> {
|
||||
const gameModes = Array.isArray(disallowedGameModes) ? disallowedGameModes : [disallowedGameModes];
|
||||
const gameModes = coerceArray(disallowedGameModes);
|
||||
return Object.assign(this, { disallowedGameModes: gameModes });
|
||||
}
|
||||
|
||||
@ -740,7 +741,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||
withDisallowedChallenges(
|
||||
...disallowedChallenges: Challenges[]
|
||||
): this & Required<Pick<IMysteryEncounter, "disallowedChallenges">> {
|
||||
const challenges = Array.isArray(disallowedChallenges) ? disallowedChallenges : [disallowedChallenges];
|
||||
const challenges = coerceArray(disallowedChallenges);
|
||||
return Object.assign(this, { disallowedChallenges: challenges });
|
||||
}
|
||||
|
||||
|