mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-21 17:12:44 +02:00
Merge remote-tracking branch 'upstream/beta' into modifier-fixes
This commit is contained in:
commit
2404bd80e7
@ -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();
|
|
@ -13,6 +13,7 @@
|
|||||||
"test:cov": "vitest run --coverage --no-isolate",
|
"test:cov": "vitest run --coverage --no-isolate",
|
||||||
"test:watch": "vitest watch --coverage --no-isolate",
|
"test:watch": "vitest watch --coverage --no-isolate",
|
||||||
"test:silent": "vitest run --silent --no-isolate",
|
"test:silent": "vitest run --silent --no-isolate",
|
||||||
|
"test:create": "node scripts/create-test/create-test.js",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"eslint": "eslint --fix .",
|
"eslint": "eslint --fix .",
|
||||||
"eslint-ci": "eslint .",
|
"eslint-ci": "eslint .",
|
||||||
@ -21,7 +22,6 @@
|
|||||||
"docs": "typedoc",
|
"docs": "typedoc",
|
||||||
"depcruise": "depcruise src",
|
"depcruise": "depcruise src",
|
||||||
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",
|
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",
|
||||||
"create-test": "node ./create-test-boilerplate.js",
|
|
||||||
"postinstall": "npx lefthook install && npx lefthook run post-merge",
|
"postinstall": "npx lefthook install && npx lefthook run post-merge",
|
||||||
"update-version:patch": "npm version patch --force --no-git-tag-version",
|
"update-version:patch": "npm version patch --force --no-git-tag-version",
|
||||||
"update-version:minor": "npm version minor --force --no-git-tag-version",
|
"update-version:minor": "npm version minor --force --no-git-tag-version",
|
||||||
|
147
scripts/create-test/create-test.js
Normal file
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
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);
|
||||||
|
});
|
||||||
|
});
|
56
src/@types/move-types.ts
Normal file
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;
|
@ -80,13 +80,15 @@ import type { FixedBattleConfig } from "#app/battle";
|
|||||||
import Battle from "#app/battle";
|
import Battle from "#app/battle";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import type { GameMode } from "#app/game-mode";
|
import type { GameMode } from "#app/game-mode";
|
||||||
import { GameModes, getGameMode } from "#app/game-mode";
|
import { getGameMode } from "#app/game-mode";
|
||||||
|
import { GameModes } from "#enums/game-modes";
|
||||||
import FieldSpritePipeline from "#app/pipelines/field-sprite";
|
import FieldSpritePipeline from "#app/pipelines/field-sprite";
|
||||||
import SpritePipeline from "#app/pipelines/sprite";
|
import SpritePipeline from "#app/pipelines/sprite";
|
||||||
import PartyExpBar from "#app/ui/party-exp-bar";
|
import PartyExpBar from "#app/ui/party-exp-bar";
|
||||||
import type { TrainerSlot } from "./enums/trainer-slot";
|
import type { TrainerSlot } from "./enums/trainer-slot";
|
||||||
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
import { trainerConfigs } from "#app/data/trainers/trainer-config";
|
||||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import type TrainerData from "#app/system/trainer-data";
|
import type TrainerData from "#app/system/trainer-data";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
@ -101,13 +103,12 @@ import type UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
|||||||
import { addUiThemeOverrides } from "#app/ui/ui-theme";
|
import { addUiThemeOverrides } from "#app/ui/ui-theme";
|
||||||
import type PokemonData from "#app/system/pokemon-data";
|
import type PokemonData from "#app/system/pokemon-data";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import type { SpeciesFormChange, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms";
|
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||||
import {
|
import type { SpeciesFormChangeTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||||
FormChangeItem,
|
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||||
pokemonFormChanges,
|
import { SpeciesFormChangeTimeOfDayTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||||
SpeciesFormChangeManualTrigger,
|
import { SpeciesFormChangeManualTrigger } from "./data/pokemon-forms/form-change-triggers";
|
||||||
SpeciesFormChangeTimeOfDayTrigger,
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
} from "#app/data/pokemon-forms";
|
|
||||||
import { getTypeRgb } from "#app/data/type";
|
import { getTypeRgb } from "#app/data/type";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
|
import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { Command } from "./ui/command-ui-handler";
|
import type { Command } from "#enums/command";
|
||||||
import {
|
import {
|
||||||
randomString,
|
randomString,
|
||||||
getEnumValues,
|
getEnumValues,
|
||||||
@ -10,7 +10,8 @@ import {
|
|||||||
randInt,
|
randInt,
|
||||||
randSeedFloat,
|
randSeedFloat,
|
||||||
} from "#app/utils/common";
|
} from "#app/utils/common";
|
||||||
import Trainer, { TrainerVariant } from "./field/trainer";
|
import Trainer from "./field/trainer";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import type { GameMode } from "./game-mode";
|
import type { GameMode } from "./game-mode";
|
||||||
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
||||||
import type { PokeballType } from "#enums/pokeball";
|
import type { PokeballType } from "#enums/pokeball";
|
||||||
@ -33,14 +34,7 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
|
|||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||||
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
export enum BattlerIndex {
|
|
||||||
ATTACKER = -1,
|
|
||||||
PLAYER,
|
|
||||||
PLAYER_2,
|
|
||||||
ENEMY,
|
|
||||||
ENEMY_2,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TurnCommand {
|
export interface TurnCommand {
|
||||||
command: Command;
|
command: Command;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
|
||||||
/** The maximum size of the player's party */
|
/** The maximum size of the player's party */
|
||||||
export const PLAYER_PARTY_MAX_SIZE: number = 6;
|
export const PLAYER_PARTY_MAX_SIZE: number = 6;
|
||||||
|
|
||||||
@ -17,3 +19,38 @@ export const CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180
|
|||||||
|
|
||||||
/** The raw percentage power boost for type boost items*/
|
/** The raw percentage power boost for type boost items*/
|
||||||
export const TYPE_BOOST_ITEM_BOOST_PERCENT = 20;
|
export const TYPE_BOOST_ITEM_BOOST_PERCENT = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default species that a new player can choose from
|
||||||
|
*/
|
||||||
|
export const defaultStarterSpecies: SpeciesId[] = [
|
||||||
|
SpeciesId.BULBASAUR,
|
||||||
|
SpeciesId.CHARMANDER,
|
||||||
|
SpeciesId.SQUIRTLE,
|
||||||
|
SpeciesId.CHIKORITA,
|
||||||
|
SpeciesId.CYNDAQUIL,
|
||||||
|
SpeciesId.TOTODILE,
|
||||||
|
SpeciesId.TREECKO,
|
||||||
|
SpeciesId.TORCHIC,
|
||||||
|
SpeciesId.MUDKIP,
|
||||||
|
SpeciesId.TURTWIG,
|
||||||
|
SpeciesId.CHIMCHAR,
|
||||||
|
SpeciesId.PIPLUP,
|
||||||
|
SpeciesId.SNIVY,
|
||||||
|
SpeciesId.TEPIG,
|
||||||
|
SpeciesId.OSHAWOTT,
|
||||||
|
SpeciesId.CHESPIN,
|
||||||
|
SpeciesId.FENNEKIN,
|
||||||
|
SpeciesId.FROAKIE,
|
||||||
|
SpeciesId.ROWLET,
|
||||||
|
SpeciesId.LITTEN,
|
||||||
|
SpeciesId.POPPLIO,
|
||||||
|
SpeciesId.GROOKEY,
|
||||||
|
SpeciesId.SCORBUNNY,
|
||||||
|
SpeciesId.SOBBLE,
|
||||||
|
SpeciesId.SPRIGATITO,
|
||||||
|
SpeciesId.FUECOCO,
|
||||||
|
SpeciesId.QUAXLY,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { HitResult, MoveResult } from "#app/field/pokemon";
|
import { MoveResult } from "#enums/move-result";
|
||||||
|
import { HitResult } from "#enums/hit-result";
|
||||||
import {
|
import {
|
||||||
BooleanHolder,
|
BooleanHolder,
|
||||||
NumberHolder,
|
NumberHolder,
|
||||||
@ -10,40 +11,26 @@ import {
|
|||||||
randSeedFloat,
|
randSeedFloat,
|
||||||
} from "#app/utils/common";
|
} from "#app/utils/common";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { BattlerTagLapseType, GroundedTag } from "#app/data/battler-tags";
|
import { GroundedTag } from "#app/data/battler-tags";
|
||||||
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import {
|
import {
|
||||||
getNonVolatileStatusEffects,
|
getNonVolatileStatusEffects,
|
||||||
getStatusEffectDescriptor,
|
getStatusEffectDescriptor,
|
||||||
getStatusEffectHealText,
|
getStatusEffectHealText,
|
||||||
} from "#app/data/status-effect";
|
} from "#app/data/status-effect";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import {
|
import { applyMoveAttrs } from "../moves/apply-attrs";
|
||||||
AttackMove,
|
|
||||||
FlinchAttr,
|
|
||||||
OneHitKOAttr,
|
|
||||||
HitHealAttr,
|
|
||||||
StatusMove,
|
|
||||||
SelfStatusMove,
|
|
||||||
VariablePowerAttr,
|
|
||||||
applyMoveAttrs,
|
|
||||||
RandomMovesetMoveAttr,
|
|
||||||
RandomMoveAttr,
|
|
||||||
NaturePowerAttr,
|
|
||||||
CopyMoveAttr,
|
|
||||||
NeutralDamageAgainstFlyingTypeMultiplierAttr,
|
|
||||||
FixedDamageAttr,
|
|
||||||
} from "#app/data/moves/move";
|
|
||||||
import { allMoves } from "../data-lists";
|
import { allMoves } from "../data-lists";
|
||||||
import { ArenaTagSide } from "#app/data/arena-tag";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { TerrainType } from "#app/data/terrain";
|
import { TerrainType } from "#app/data/terrain";
|
||||||
import {
|
import {
|
||||||
SpeciesFormChangeAbilityTrigger,
|
|
||||||
SpeciesFormChangeRevertWeatherFormTrigger,
|
SpeciesFormChangeRevertWeatherFormTrigger,
|
||||||
SpeciesFormChangeWeatherTrigger,
|
SpeciesFormChangeWeatherTrigger,
|
||||||
} from "#app/data/pokemon-forms";
|
} from "../pokemon-forms/form-change-triggers";
|
||||||
|
import { SpeciesFormChangeAbilityTrigger } from "../pokemon-forms/form-change-triggers";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#enums/command";
|
||||||
import { BerryModifierType } from "#app/modifier/modifier-type";
|
import { BerryModifierType } from "#app/modifier/modifier-type";
|
||||||
import { getPokeballName } from "#app/data/pokeball";
|
import { getPokeballName } from "#app/data/pokeball";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
@ -69,12 +56,13 @@ import { MoveFlags } from "#enums/MoveFlags";
|
|||||||
import { MoveTarget } from "#enums/MoveTarget";
|
import { MoveTarget } from "#enums/MoveTarget";
|
||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import type { BerryType } from "#enums/berry-type";
|
import type { BerryType } from "#enums/berry-type";
|
||||||
import { CommonAnim } from "../battle-anims";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { getBerryEffectFunc } from "../berry";
|
import { getBerryEffectFunc } from "../berry";
|
||||||
import { BerryUsedEvent } from "#app/events/battle-scene";
|
import { BerryUsedEvent } from "#app/events/battle-scene";
|
||||||
|
|
||||||
// Type imports
|
// Type imports
|
||||||
import type { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import type { PokemonMove } from "../moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { Weather } from "#app/data/weather";
|
import type { Weather } from "#app/data/weather";
|
||||||
import type { BattlerTag } from "#app/data/battler-tags";
|
import type { BattlerTag } from "#app/data/battler-tags";
|
||||||
@ -86,7 +74,7 @@ import type {
|
|||||||
AbAttrApplyFunc,
|
AbAttrApplyFunc,
|
||||||
AbAttrSuccessFunc,
|
AbAttrSuccessFunc,
|
||||||
} from "#app/@types/ability-types";
|
} from "#app/@types/ability-types";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import type Move from "#app/data/moves/move";
|
import type Move from "#app/data/moves/move";
|
||||||
import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag";
|
import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag";
|
||||||
import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves";
|
import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves";
|
||||||
@ -520,7 +508,7 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr {
|
|||||||
): boolean {
|
): boolean {
|
||||||
return (
|
return (
|
||||||
move.category !== MoveCategory.STATUS &&
|
move.category !== MoveCategory.STATUS &&
|
||||||
!move.hasAttr(NeutralDamageAgainstFlyingTypeMultiplierAttr) &&
|
!move.hasAttr("NeutralDamageAgainstFlyingTypeMultiplierAttr") &&
|
||||||
super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args)
|
super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -693,7 +681,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
|
|||||||
args.length > 0
|
args.length > 0
|
||||||
? (args[0] as NumberHolder).value
|
? (args[0] as NumberHolder).value
|
||||||
: pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker, undefined, undefined, move);
|
: pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker, undefined, undefined, move);
|
||||||
return move instanceof AttackMove && modifierValue < 2;
|
return move.is("AttackMove") && modifierValue < 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
override applyPreDefend(
|
override applyPreDefend(
|
||||||
@ -735,7 +723,7 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr {
|
|||||||
const typeMultiplier = args[0];
|
const typeMultiplier = args[0];
|
||||||
return (
|
return (
|
||||||
typeMultiplier instanceof NumberHolder &&
|
typeMultiplier instanceof NumberHolder &&
|
||||||
!move?.hasAttr(FixedDamageAttr) &&
|
!move?.hasAttr("FixedDamageAttr") &&
|
||||||
pokemon.isFullHp() &&
|
pokemon.isFullHp() &&
|
||||||
typeMultiplier.value > 0.5
|
typeMultiplier.value > 0.5
|
||||||
);
|
);
|
||||||
@ -980,7 +968,7 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr {
|
|||||||
_hitResult: HitResult | null,
|
_hitResult: HitResult | null,
|
||||||
_args: any[],
|
_args: any[],
|
||||||
): boolean {
|
): boolean {
|
||||||
return move.hasAttr(HitHealAttr);
|
return move.hasAttr("HitHealAttr");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2061,10 +2049,10 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr {
|
|||||||
*/
|
*/
|
||||||
!move.findAttr(
|
!move.findAttr(
|
||||||
attr =>
|
attr =>
|
||||||
attr instanceof RandomMovesetMoveAttr ||
|
attr.is("RandomMovesetMoveAttr") ||
|
||||||
attr instanceof RandomMoveAttr ||
|
attr.is("RandomMoveAttr") ||
|
||||||
attr instanceof NaturePowerAttr ||
|
attr.is("NaturePowerAttr") ||
|
||||||
attr instanceof CopyMoveAttr,
|
attr.is("CopyMoveAttr"),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
const moveType = pokemon.getMoveType(move);
|
const moveType = pokemon.getMoveType(move);
|
||||||
@ -4920,13 +4908,13 @@ function getAnticipationCondition(): AbAttrCondition {
|
|||||||
}
|
}
|
||||||
// the move's base type (not accounting for variable type changes) is super effective
|
// the move's base type (not accounting for variable type changes) is super effective
|
||||||
if (
|
if (
|
||||||
move.getMove() instanceof AttackMove &&
|
move.getMove().is("AttackMove") &&
|
||||||
pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2
|
pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// move is a OHKO
|
// move is a OHKO
|
||||||
if (move.getMove().hasAttr(OneHitKOAttr)) {
|
if (move.getMove().hasAttr("OneHitKOAttr")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// edge case for hidden power, type is computed
|
// edge case for hidden power, type is computed
|
||||||
@ -4995,9 +4983,9 @@ export class ForewarnAbAttr extends PostSummonAbAttr {
|
|||||||
let movePower = 0;
|
let movePower = 0;
|
||||||
for (const opponent of pokemon.getOpponents()) {
|
for (const opponent of pokemon.getOpponents()) {
|
||||||
for (const move of opponent.moveset) {
|
for (const move of opponent.moveset) {
|
||||||
if (move?.getMove() instanceof StatusMove) {
|
if (move?.getMove().is("StatusMove")) {
|
||||||
movePower = 1;
|
movePower = 1;
|
||||||
} else if (move?.getMove().hasAttr(OneHitKOAttr)) {
|
} else if (move?.getMove().hasAttr("OneHitKOAttr")) {
|
||||||
movePower = 150;
|
movePower = 150;
|
||||||
} else if (
|
} else if (
|
||||||
move?.getMove().id === MoveId.COUNTER ||
|
move?.getMove().id === MoveId.COUNTER ||
|
||||||
@ -5868,10 +5856,10 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
|
|||||||
dancer.turnData.extraTurns++;
|
dancer.turnData.extraTurns++;
|
||||||
const phaseManager = globalScene.phaseManager;
|
const phaseManager = globalScene.phaseManager;
|
||||||
// If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance
|
// If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance
|
||||||
if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) {
|
if (move.getMove().is("AttackMove") || move.getMove().is("StatusMove")) {
|
||||||
const target = this.getTarget(dancer, source, targets);
|
const target = this.getTarget(dancer, source, targets);
|
||||||
phaseManager.unshiftNew("MovePhase", dancer, target, move, true, true);
|
phaseManager.unshiftNew("MovePhase", dancer, target, move, true, true);
|
||||||
} else if (move.getMove() instanceof SelfStatusMove) {
|
} else if (move.getMove().is("SelfStatusMove")) {
|
||||||
// If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself
|
// If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself
|
||||||
phaseManager.unshiftNew("MovePhase", dancer, [dancer.getBattlerIndex()], move, true, true);
|
phaseManager.unshiftNew("MovePhase", dancer, [dancer.getBattlerIndex()], move, true, true);
|
||||||
}
|
}
|
||||||
@ -8201,7 +8189,7 @@ export function initAbilities() {
|
|||||||
allAbilities.push(
|
allAbilities.push(
|
||||||
new Ability(AbilityId.NONE, 3),
|
new Ability(AbilityId.NONE, 3),
|
||||||
new Ability(AbilityId.STENCH, 3)
|
new Ability(AbilityId.STENCH, 3)
|
||||||
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => !move.hasAttr(FlinchAttr) && !move.hitsSubstitute(user, target) ? 10 : 0, BattlerTagType.FLINCHED),
|
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => !move.hasAttr("FlinchAttr") && !move.hitsSubstitute(user, target) ? 10 : 0, BattlerTagType.FLINCHED),
|
||||||
new Ability(AbilityId.DRIZZLE, 3)
|
new Ability(AbilityId.DRIZZLE, 3)
|
||||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN)
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN)
|
||||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN),
|
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN),
|
||||||
@ -8508,7 +8496,7 @@ export function initAbilities() {
|
|||||||
new Ability(AbilityId.TECHNICIAN, 4)
|
new Ability(AbilityId.TECHNICIAN, 4)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, move) => {
|
.attr(MovePowerBoostAbAttr, (user, target, move) => {
|
||||||
const power = new NumberHolder(move.power);
|
const power = new NumberHolder(move.power);
|
||||||
applyMoveAttrs(VariablePowerAttr, user, target, move, power);
|
applyMoveAttrs("VariablePowerAttr", user, target, move, power);
|
||||||
return power.value <= 60;
|
return power.value <= 60;
|
||||||
}, 1.5),
|
}, 1.5),
|
||||||
new Ability(AbilityId.LEAF_GUARD, 4)
|
new Ability(AbilityId.LEAF_GUARD, 4)
|
||||||
@ -8629,7 +8617,7 @@ export function initAbilities() {
|
|||||||
)
|
)
|
||||||
.edgeCase(), // Cannot recover berries used up by fling or natural gift (unimplemented)
|
.edgeCase(), // Cannot recover berries used up by fling or natural gift (unimplemented)
|
||||||
new Ability(AbilityId.TELEPATHY, 5)
|
new Ability(AbilityId.TELEPATHY, 5)
|
||||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove)
|
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move.is("AttackMove"))
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(AbilityId.MOODY, 5)
|
new Ability(AbilityId.MOODY, 5)
|
||||||
.attr(MoodyAbAttr),
|
.attr(MoodyAbAttr),
|
||||||
|
@ -7,9 +7,9 @@ import { MoveTarget } from "#enums/MoveTarget";
|
|||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { HitResult } from "#app/field/pokemon";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import {
|
import {
|
||||||
BlockNonDirectDamageAbAttr,
|
BlockNonDirectDamageAbAttr,
|
||||||
InfiltratorAbAttr,
|
InfiltratorAbAttr,
|
||||||
@ -20,18 +20,14 @@ import {
|
|||||||
applyOnLoseAbAttrs,
|
applyOnLoseAbAttrs,
|
||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim } from "#app/data/battle-anims";
|
||||||
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
export enum ArenaTagSide {
|
|
||||||
BOTH,
|
|
||||||
PLAYER,
|
|
||||||
ENEMY,
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class ArenaTag {
|
export abstract class ArenaTag {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -1,111 +1,15 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove } from "./moves/move";
|
|
||||||
import { allMoves } from "./data-lists";
|
import { allMoves } from "./data-lists";
|
||||||
import { MoveFlags } from "#enums/MoveFlags";
|
import { MoveFlags } from "#enums/MoveFlags";
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils/common";
|
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils/common";
|
||||||
import type { BattlerIndex } from "../battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SubstituteTag } from "./battler-tags";
|
import { SubstituteTag } from "./battler-tags";
|
||||||
import { isNullOrUndefined } from "../utils/common";
|
import { isNullOrUndefined } from "../utils/common";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { EncounterAnim } from "#enums/encounter-anims";
|
import { EncounterAnim } from "#enums/encounter-anims";
|
||||||
|
import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
||||||
export enum AnimFrameTarget {
|
|
||||||
USER,
|
|
||||||
TARGET,
|
|
||||||
GRAPHIC,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AnimFocus {
|
|
||||||
TARGET = 1,
|
|
||||||
USER,
|
|
||||||
USER_TARGET,
|
|
||||||
SCREEN,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AnimBlendType {
|
|
||||||
NORMAL,
|
|
||||||
ADD,
|
|
||||||
SUBTRACT,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ChargeAnim {
|
|
||||||
FLY_CHARGING = 1000,
|
|
||||||
BOUNCE_CHARGING,
|
|
||||||
DIG_CHARGING,
|
|
||||||
FUTURE_SIGHT_CHARGING,
|
|
||||||
DIVE_CHARGING,
|
|
||||||
SOLAR_BEAM_CHARGING,
|
|
||||||
SHADOW_FORCE_CHARGING,
|
|
||||||
SKULL_BASH_CHARGING,
|
|
||||||
FREEZE_SHOCK_CHARGING,
|
|
||||||
SKY_DROP_CHARGING,
|
|
||||||
SKY_ATTACK_CHARGING,
|
|
||||||
ICE_BURN_CHARGING,
|
|
||||||
DOOM_DESIRE_CHARGING,
|
|
||||||
RAZOR_WIND_CHARGING,
|
|
||||||
PHANTOM_FORCE_CHARGING,
|
|
||||||
GEOMANCY_CHARGING,
|
|
||||||
SHADOW_BLADE_CHARGING,
|
|
||||||
SOLAR_BLADE_CHARGING,
|
|
||||||
BEAK_BLAST_CHARGING,
|
|
||||||
METEOR_BEAM_CHARGING,
|
|
||||||
ELECTRO_SHOT_CHARGING,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CommonAnim {
|
|
||||||
USE_ITEM = 2000,
|
|
||||||
HEALTH_UP,
|
|
||||||
TERASTALLIZE,
|
|
||||||
POISON = 2010,
|
|
||||||
TOXIC,
|
|
||||||
PARALYSIS,
|
|
||||||
SLEEP,
|
|
||||||
FROZEN,
|
|
||||||
BURN,
|
|
||||||
CONFUSION,
|
|
||||||
ATTRACT,
|
|
||||||
BIND,
|
|
||||||
WRAP,
|
|
||||||
CURSE_NO_GHOST,
|
|
||||||
LEECH_SEED,
|
|
||||||
FIRE_SPIN,
|
|
||||||
PROTECT,
|
|
||||||
COVET,
|
|
||||||
WHIRLPOOL,
|
|
||||||
BIDE,
|
|
||||||
SAND_TOMB,
|
|
||||||
QUICK_GUARD,
|
|
||||||
WIDE_GUARD,
|
|
||||||
CURSE,
|
|
||||||
MAGMA_STORM,
|
|
||||||
CLAMP,
|
|
||||||
SNAP_TRAP,
|
|
||||||
THUNDER_CAGE,
|
|
||||||
INFESTATION,
|
|
||||||
ORDER_UP_CURLY,
|
|
||||||
ORDER_UP_DROOPY,
|
|
||||||
ORDER_UP_STRETCHY,
|
|
||||||
RAGING_BULL_FIRE,
|
|
||||||
RAGING_BULL_WATER,
|
|
||||||
SALT_CURE,
|
|
||||||
POWDER,
|
|
||||||
SUNNY = 2100,
|
|
||||||
RAIN,
|
|
||||||
SANDSTORM,
|
|
||||||
HAIL,
|
|
||||||
SNOW,
|
|
||||||
WIND,
|
|
||||||
HEAVY_RAIN,
|
|
||||||
HARSH_SUN,
|
|
||||||
STRONG_WINDS,
|
|
||||||
MISTY_TERRAIN = 2110,
|
|
||||||
ELECTRIC_TERRAIN,
|
|
||||||
GRASSY_TERRAIN,
|
|
||||||
PSYCHIC_TERRAIN,
|
|
||||||
LOCK_ON = 2120,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AnimConfig {
|
export class AnimConfig {
|
||||||
public id: number;
|
public id: number;
|
||||||
@ -531,7 +435,7 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
|||||||
if (moveAnims.get(move) !== null) {
|
if (moveAnims.get(move) !== null) {
|
||||||
const chargeAnimSource = allMoves[move].isChargingMove()
|
const chargeAnimSource = allMoves[move].isChargingMove()
|
||||||
? allMoves[move]
|
? allMoves[move]
|
||||||
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
|
: (allMoves[move].getAttrs("DelayedAttackAttr")[0] ?? allMoves[move].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||||
if (chargeAnimSource && chargeAnims.get(chargeAnimSource.chargeAnim) === null) {
|
if (chargeAnimSource && chargeAnims.get(chargeAnimSource.chargeAnim) === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -542,10 +446,9 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
moveAnims.set(move, null);
|
moveAnims.set(move, null);
|
||||||
const defaultMoveAnim =
|
const defaultMoveAnim = allMoves[move].is("AttackMove")
|
||||||
allMoves[move] instanceof AttackMove
|
|
||||||
? MoveId.TACKLE
|
? MoveId.TACKLE
|
||||||
: allMoves[move] instanceof SelfStatusMove
|
: allMoves[move].is("SelfStatusMove")
|
||||||
? MoveId.FOCUS_ENERGY
|
? MoveId.FOCUS_ENERGY
|
||||||
: MoveId.TAIL_WHIP;
|
: MoveId.TAIL_WHIP;
|
||||||
|
|
||||||
@ -570,7 +473,7 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
|||||||
}
|
}
|
||||||
const chargeAnimSource = allMoves[move].isChargingMove()
|
const chargeAnimSource = allMoves[move].isChargingMove()
|
||||||
? allMoves[move]
|
? allMoves[move]
|
||||||
: (allMoves[move].getAttrs(DelayedAttackAttr)[0] ?? allMoves[move].getAttrs(BeakBlastHeaderAttr)[0]);
|
: (allMoves[move].getAttrs("DelayedAttackAttr")[0] ?? allMoves[move].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||||
if (chargeAnimSource) {
|
if (chargeAnimSource) {
|
||||||
initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
|
initMoveChargeAnim(chargeAnimSource.chargeAnim).then(() => resolve());
|
||||||
} else {
|
} else {
|
||||||
@ -703,7 +606,7 @@ export function loadMoveAnimAssets(moveIds: MoveId[], startLoad?: boolean): Prom
|
|||||||
for (const moveId of moveIds) {
|
for (const moveId of moveIds) {
|
||||||
const chargeAnimSource = allMoves[moveId].isChargingMove()
|
const chargeAnimSource = allMoves[moveId].isChargingMove()
|
||||||
? allMoves[moveId]
|
? allMoves[moveId]
|
||||||
: (allMoves[moveId].getAttrs(DelayedAttackAttr)[0] ?? allMoves[moveId].getAttrs(BeakBlastHeaderAttr)[0]);
|
: (allMoves[moveId].getAttrs("DelayedAttackAttr")[0] ?? allMoves[moveId].getAttrs("BeakBlastHeaderAttr")[0]);
|
||||||
if (chargeAnimSource) {
|
if (chargeAnimSource) {
|
||||||
const moveChargeAnims = chargeAnims.get(chargeAnimSource.chargeAnim);
|
const moveChargeAnims = chargeAnims.get(chargeAnimSource.chargeAnim);
|
||||||
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims![0]); // TODO: is the bang correct?
|
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims![0]); // TODO: is the bang correct?
|
||||||
|
@ -9,23 +9,20 @@ import {
|
|||||||
ReverseDrainAbAttr,
|
ReverseDrainAbAttr,
|
||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
import { allAbilities } from "./data-lists";
|
import { allAbilities } from "./data-lists";
|
||||||
import { ChargeAnim, CommonAnim, CommonBattleAnim, MoveChargeAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim, MoveChargeAnim } from "#app/data/battle-anims";
|
||||||
|
import { ChargeAnim, CommonAnim } from "#enums/move-anims-common";
|
||||||
import type Move from "#app/data/moves/move";
|
import type Move from "#app/data/moves/move";
|
||||||
import {
|
import { applyMoveAttrs } from "./moves/apply-attrs";
|
||||||
applyMoveAttrs,
|
|
||||||
ConsecutiveUseDoublePowerAttr,
|
|
||||||
HealOnAllyAttr,
|
|
||||||
StatusCategoryOnAllyAttr,
|
|
||||||
} from "#app/data/moves/move";
|
|
||||||
import { allMoves } from "./data-lists";
|
import { allMoves } from "./data-lists";
|
||||||
import { MoveFlags } from "#enums/MoveFlags";
|
import { MoveFlags } from "#enums/MoveFlags";
|
||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeAbilityTrigger } from "./pokemon-forms/form-change-triggers";
|
||||||
import { getStatusEffectHealText } from "#app/data/status-effect";
|
import { getStatusEffectHealText } from "#app/data/status-effect";
|
||||||
import { TerrainType } from "#app/data/terrain";
|
import { TerrainType } from "#app/data/terrain";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { HitResult, MoveResult } from "#app/field/pokemon";
|
import { MoveResult } from "#enums/move-result";
|
||||||
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import type { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
import type { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
import type { MovePhase } from "#app/phases/move-phase";
|
import type { MovePhase } from "#app/phases/move-phase";
|
||||||
@ -41,19 +38,7 @@ import { EFFECTIVE_STATS, getStatKey, Stat, type BattleStat, type EffectiveStat
|
|||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
export enum BattlerTagLapseType {
|
|
||||||
FAINT,
|
|
||||||
MOVE,
|
|
||||||
PRE_MOVE,
|
|
||||||
AFTER_MOVE,
|
|
||||||
MOVE_EFFECT,
|
|
||||||
TURN_END,
|
|
||||||
HIT,
|
|
||||||
/** Tag lapses AFTER_HIT, applying its effects even if the user faints */
|
|
||||||
AFTER_HIT,
|
|
||||||
CUSTOM,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BattlerTag {
|
export class BattlerTag {
|
||||||
public tagType: BattlerTagType;
|
public tagType: BattlerTagType;
|
||||||
@ -2878,8 +2863,8 @@ export class HealBlockTag extends MoveRestrictionBattlerTag {
|
|||||||
*/
|
*/
|
||||||
override isMoveTargetRestricted(move: MoveId, user: Pokemon, target: Pokemon) {
|
override isMoveTargetRestricted(move: MoveId, user: Pokemon, target: Pokemon) {
|
||||||
const moveCategory = new NumberHolder(allMoves[move].category);
|
const moveCategory = new NumberHolder(allMoves[move].category);
|
||||||
applyMoveAttrs(StatusCategoryOnAllyAttr, user, target, allMoves[move], moveCategory);
|
applyMoveAttrs("StatusCategoryOnAllyAttr", user, target, allMoves[move], moveCategory);
|
||||||
return allMoves[move].hasAttr(HealOnAllyAttr) && moveCategory.value === MoveCategory.STATUS;
|
return allMoves[move].hasAttr("HealOnAllyAttr") && moveCategory.value === MoveCategory.STATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3221,7 +3206,7 @@ export class TormentTag extends MoveRestrictionBattlerTag {
|
|||||||
// This checks for locking / momentum moves like Rollout and Hydro Cannon + if the user is under the influence of BattlerTagType.FRENZY
|
// This checks for locking / momentum moves like Rollout and Hydro Cannon + if the user is under the influence of BattlerTagType.FRENZY
|
||||||
// Because Uproar's unique behavior is not implemented, it does not check for Uproar. Torment has been marked as partial in moves.ts
|
// Because Uproar's unique behavior is not implemented, it does not check for Uproar. Torment has been marked as partial in moves.ts
|
||||||
const moveObj = allMoves[lastMove.move];
|
const moveObj = allMoves[lastMove.move];
|
||||||
const isUnaffected = moveObj.hasAttr(ConsecutiveUseDoublePowerAttr) || user.getTag(BattlerTagType.FRENZY);
|
const isUnaffected = moveObj.hasAttr("ConsecutiveUseDoublePowerAttr") || user.getTag(BattlerTagType.FRENZY);
|
||||||
const validLastMoveResult = lastMove.result === MoveResult.SUCCESS || lastMove.result === MoveResult.MISS;
|
const validLastMoveResult = lastMove.result === MoveResult.SUCCESS || lastMove.result === MoveResult.MISS;
|
||||||
return lastMove.move === move && validLastMoveResult && lastMove.move !== MoveId.STRUGGLE && !isUnaffected;
|
return lastMove.move === move && validLastMoveResult && lastMove.move !== MoveId.STRUGGLE && !isUnaffected;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { getPokemonNameWithAffix } from "../messages";
|
import { getPokemonNameWithAffix } from "../messages";
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import { HitResult } from "../field/pokemon";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { getStatusEffectHealText } from "./status-effect";
|
import { getStatusEffectHealText } from "./status-effect";
|
||||||
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common";
|
import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common";
|
||||||
import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } from "./abilities/ability";
|
import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } from "./abilities/ability";
|
||||||
|
@ -2,17 +2,18 @@ import { BooleanHolder, type NumberHolder, randSeedItem } from "#app/utils/commo
|
|||||||
import { deepCopy } from "#app/utils/data";
|
import { deepCopy } from "#app/utils/data";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import type { DexAttrProps, GameData } from "#app/system/game-data";
|
import type { DexAttrProps, GameData } from "#app/system/game-data";
|
||||||
import { defaultStarterSpecies } from "#app/system/game-data";
|
import { defaultStarterSpecies } from "#app/constants";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "./moves/pokemon-move";
|
||||||
import type { FixedBattleConfig } from "#app/battle";
|
import type { FixedBattleConfig } from "#app/battle";
|
||||||
import { getRandomTrainerFunc } from "#app/battle";
|
import { getRandomTrainerFunc } from "#app/battle";
|
||||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { Challenges } from "#enums/challenges";
|
import { Challenges } from "#enums/challenges";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
@ -24,93 +25,12 @@ import { ModifierTier } from "#app/modifier/modifier-tier";
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { pokemonFormChanges } from "./pokemon-forms";
|
import { pokemonFormChanges } from "./pokemon-forms";
|
||||||
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
||||||
|
import { ChallengeType } from "#enums/challenge-type";
|
||||||
|
import type { MoveSourceType } from "#enums/move-source-type";
|
||||||
|
|
||||||
/** A constant for the default max cost of the starting party before a run */
|
/** A constant for the default max cost of the starting party before a run */
|
||||||
const DEFAULT_PARTY_MAX_COST = 10;
|
const DEFAULT_PARTY_MAX_COST = 10;
|
||||||
|
|
||||||
/**
|
|
||||||
* An enum for all the challenge types. The parameter entries on these describe the
|
|
||||||
* parameters to use when calling the applyChallenges function.
|
|
||||||
*/
|
|
||||||
export enum ChallengeType {
|
|
||||||
/**
|
|
||||||
* Challenges which modify what starters you can choose
|
|
||||||
* @see {@link Challenge.applyStarterChoice}
|
|
||||||
*/
|
|
||||||
STARTER_CHOICE,
|
|
||||||
/**
|
|
||||||
* Challenges which modify how many starter points you have
|
|
||||||
* @see {@link Challenge.applyStarterPoints}
|
|
||||||
*/
|
|
||||||
STARTER_POINTS,
|
|
||||||
/**
|
|
||||||
* Challenges which modify how many starter points you have
|
|
||||||
* @see {@link Challenge.applyStarterPointCost}
|
|
||||||
*/
|
|
||||||
STARTER_COST,
|
|
||||||
/**
|
|
||||||
* Challenges which modify your starters in some way
|
|
||||||
* @see {@link Challenge.applyStarterModify}
|
|
||||||
*/
|
|
||||||
STARTER_MODIFY,
|
|
||||||
/**
|
|
||||||
* Challenges which limit which pokemon you can have in battle.
|
|
||||||
* @see {@link Challenge.applyPokemonInBattle}
|
|
||||||
*/
|
|
||||||
POKEMON_IN_BATTLE,
|
|
||||||
/**
|
|
||||||
* Adds or modifies the fixed battles in a run
|
|
||||||
* @see {@link Challenge.applyFixedBattle}
|
|
||||||
*/
|
|
||||||
FIXED_BATTLES,
|
|
||||||
/**
|
|
||||||
* Modifies the effectiveness of Type matchups in battle
|
|
||||||
* @see {@linkcode Challenge.applyTypeEffectiveness}
|
|
||||||
*/
|
|
||||||
TYPE_EFFECTIVENESS,
|
|
||||||
/**
|
|
||||||
* Modifies what level the AI pokemon are. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
AI_LEVEL,
|
|
||||||
/**
|
|
||||||
* Modifies how many move slots the AI has. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
AI_MOVE_SLOTS,
|
|
||||||
/**
|
|
||||||
* Modifies if a pokemon has its passive. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
PASSIVE_ACCESS,
|
|
||||||
/**
|
|
||||||
* Modifies the game mode settings in some way. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
GAME_MODE_MODIFY,
|
|
||||||
/**
|
|
||||||
* Modifies what level AI pokemon can access a move. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
MOVE_ACCESS,
|
|
||||||
/**
|
|
||||||
* Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED.
|
|
||||||
*/
|
|
||||||
MOVE_WEIGHT,
|
|
||||||
/**
|
|
||||||
* Modifies what the pokemon stats for Flip Stat Mode.
|
|
||||||
*/
|
|
||||||
FLIP_STAT,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for challenge types that modify movesets, these denote the various sources of moves for pokemon.
|
|
||||||
*/
|
|
||||||
export enum MoveSourceType {
|
|
||||||
LEVEL_UP, // Currently unimplemented for move access
|
|
||||||
RELEARNER, // Relearner moves currently unimplemented
|
|
||||||
COMMON_TM,
|
|
||||||
GREAT_TM,
|
|
||||||
ULTRA_TM,
|
|
||||||
COMMON_EGG,
|
|
||||||
RARE_EGG,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A challenge object. Exists only to serve as a base class.
|
* A challenge object. Exists only to serve as a base class.
|
||||||
*/
|
*/
|
||||||
|
58
src/data/moves/apply-attrs.ts
Normal file
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,5 +1,14 @@
|
|||||||
import { MoveTarget } from "#enums/MoveTarget";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
|
import type { MoveId } from "#enums/move-id";
|
||||||
|
import type { MoveTargetSet, UserMoveConditionFunc } from "./move";
|
||||||
import type Move from "./move";
|
import type Move from "./move";
|
||||||
|
import { NumberHolder, isNullOrUndefined } from "#app/utils/common";
|
||||||
|
import { MoveTarget } from "#enums/MoveTarget";
|
||||||
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
|
import { allMoves } from "#app/data/data-lists";
|
||||||
|
import { applyMoveAttrs } from "./apply-attrs";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the move targets the field
|
* Return whether the move targets the field
|
||||||
@ -18,3 +27,88 @@ export function isFieldTargeted(move: Move): boolean {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMoveTargets(user: Pokemon, move: MoveId, replaceTarget?: MoveTarget): MoveTargetSet {
|
||||||
|
const variableTarget = new NumberHolder(0);
|
||||||
|
user.getOpponents(false).forEach(p => applyMoveAttrs("VariableTargetAttr", user, p, allMoves[move], variableTarget));
|
||||||
|
|
||||||
|
let moveTarget: MoveTarget | undefined;
|
||||||
|
if (allMoves[move].hasAttr("VariableTargetAttr")) {
|
||||||
|
moveTarget = variableTarget.value;
|
||||||
|
} else if (replaceTarget !== undefined) {
|
||||||
|
moveTarget = replaceTarget;
|
||||||
|
} else if (move) {
|
||||||
|
moveTarget = allMoves[move].moveTarget;
|
||||||
|
} else if (move === undefined) {
|
||||||
|
moveTarget = MoveTarget.NEAR_ENEMY;
|
||||||
|
}
|
||||||
|
const opponents = user.getOpponents(false);
|
||||||
|
|
||||||
|
let set: Pokemon[] = [];
|
||||||
|
let multiple = false;
|
||||||
|
const ally: Pokemon | undefined = user.getAlly();
|
||||||
|
|
||||||
|
switch (moveTarget) {
|
||||||
|
case MoveTarget.USER:
|
||||||
|
case MoveTarget.PARTY:
|
||||||
|
set = [user];
|
||||||
|
break;
|
||||||
|
case MoveTarget.NEAR_OTHER:
|
||||||
|
case MoveTarget.OTHER:
|
||||||
|
case MoveTarget.ALL_NEAR_OTHERS:
|
||||||
|
case MoveTarget.ALL_OTHERS:
|
||||||
|
set = !isNullOrUndefined(ally) ? opponents.concat([ally]) : opponents;
|
||||||
|
multiple = moveTarget === MoveTarget.ALL_NEAR_OTHERS || moveTarget === MoveTarget.ALL_OTHERS;
|
||||||
|
break;
|
||||||
|
case MoveTarget.NEAR_ENEMY:
|
||||||
|
case MoveTarget.ALL_NEAR_ENEMIES:
|
||||||
|
case MoveTarget.ALL_ENEMIES:
|
||||||
|
case MoveTarget.ENEMY_SIDE:
|
||||||
|
set = opponents;
|
||||||
|
multiple = moveTarget !== MoveTarget.NEAR_ENEMY;
|
||||||
|
break;
|
||||||
|
case MoveTarget.RANDOM_NEAR_ENEMY:
|
||||||
|
set = [opponents[user.randBattleSeedInt(opponents.length)]];
|
||||||
|
break;
|
||||||
|
case MoveTarget.ATTACKER:
|
||||||
|
return { targets: [-1 as BattlerIndex], multiple: false };
|
||||||
|
case MoveTarget.NEAR_ALLY:
|
||||||
|
case MoveTarget.ALLY:
|
||||||
|
set = !isNullOrUndefined(ally) ? [ally] : [];
|
||||||
|
break;
|
||||||
|
case MoveTarget.USER_OR_NEAR_ALLY:
|
||||||
|
case MoveTarget.USER_AND_ALLIES:
|
||||||
|
case MoveTarget.USER_SIDE:
|
||||||
|
set = !isNullOrUndefined(ally) ? [user, ally] : [user];
|
||||||
|
multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY;
|
||||||
|
break;
|
||||||
|
case MoveTarget.ALL:
|
||||||
|
case MoveTarget.BOTH_SIDES:
|
||||||
|
set = (!isNullOrUndefined(ally) ? [user, ally] : [user]).concat(opponents);
|
||||||
|
multiple = true;
|
||||||
|
break;
|
||||||
|
case MoveTarget.CURSE:
|
||||||
|
{
|
||||||
|
const extraTargets = !isNullOrUndefined(ally) ? [ally] : [];
|
||||||
|
set = user.getTypes(true).includes(PokemonType.GHOST) ? opponents.concat(extraTargets) : [user];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
targets: set
|
||||||
|
.filter(p => p?.isActive(true))
|
||||||
|
.map(p => p.getBattlerIndex())
|
||||||
|
.filter(t => t !== undefined),
|
||||||
|
multiple,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const frenzyMissFunc: UserMoveConditionFunc = (user: Pokemon, move: Move) => {
|
||||||
|
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id) {
|
||||||
|
user.getMoveQueue().shift();
|
||||||
|
}
|
||||||
|
user.removeTag(BattlerTagType.FRENZY); // FRENZY tag should be disrupted on miss/no effect
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ChargeAnim, MoveChargeAnim } from "../battle-anims";
|
import { MoveChargeAnim } from "../battle-anims";
|
||||||
|
import { ChargeAnim } from "#enums/move-anims-common";
|
||||||
import {
|
import {
|
||||||
CommandedTag,
|
CommandedTag,
|
||||||
EncoreTag,
|
EncoreTag,
|
||||||
@ -14,14 +15,11 @@ import {
|
|||||||
import { getPokemonNameWithAffix } from "../../messages";
|
import { getPokemonNameWithAffix } from "../../messages";
|
||||||
import type { AttackMoveResult, TurnMove } from "../../field/pokemon";
|
import type { AttackMoveResult, TurnMove } from "../../field/pokemon";
|
||||||
import type Pokemon from "../../field/pokemon";
|
import type Pokemon from "../../field/pokemon";
|
||||||
import {
|
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||||
EnemyPokemon,
|
import { PokemonMove } from "./pokemon-move";
|
||||||
FieldPosition,
|
import { MoveResult } from "#enums/move-result";
|
||||||
HitResult,
|
import { HitResult } from "#enums/hit-result";
|
||||||
MoveResult,
|
import { FieldPosition } from "#enums/field-position";
|
||||||
PlayerPokemon,
|
|
||||||
PokemonMove,
|
|
||||||
} from "../../field/pokemon";
|
|
||||||
import {
|
import {
|
||||||
getNonVolatileStatusEffects,
|
getNonVolatileStatusEffects,
|
||||||
getStatusEffectHealText,
|
getStatusEffectHealText,
|
||||||
@ -32,7 +30,8 @@ import { PokemonType } from "#enums/pokemon-type";
|
|||||||
import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor, randSeedFloat } from "#app/utils/common";
|
import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor, randSeedFloat } from "#app/utils/common";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import type { ArenaTrapTag } from "../arena-tag";
|
import type { ArenaTrapTag } from "../arena-tag";
|
||||||
import { ArenaTagSide, WeakenMoveTypeTag } from "../arena-tag";
|
import { WeakenMoveTypeTag } from "../arena-tag";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import {
|
import {
|
||||||
AllyMoveCategoryPowerBoostAbAttr,
|
AllyMoveCategoryPowerBoostAbAttr,
|
||||||
applyAbAttrs,
|
applyAbAttrs,
|
||||||
@ -75,11 +74,11 @@ import {
|
|||||||
PokemonMultiHitModifier,
|
PokemonMultiHitModifier,
|
||||||
PreserveBerryModifier,
|
PreserveBerryModifier,
|
||||||
} from "../../modifier/modifier";
|
} from "../../modifier/modifier";
|
||||||
import type { BattlerIndex } from "../../battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { TerrainType } from "../terrain";
|
import { TerrainType } from "../terrain";
|
||||||
import { ModifierPoolType } from "#app/modifier/modifier-type";
|
import { ModifierPoolType } from "#app/modifier/modifier-type";
|
||||||
import { Command } from "../../ui/command-ui-handler";
|
import { Command } from "#enums/command";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import type { Localizable } from "#app/@types/locales";
|
import type { Localizable } from "#app/@types/locales";
|
||||||
import { getBerryEffectFunc } from "../berry";
|
import { getBerryEffectFunc } from "../berry";
|
||||||
@ -105,9 +104,10 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
|||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||||
import { SpeciesFormChangeRevertWeatherFormTrigger } from "../pokemon-forms";
|
import { SpeciesFormChangeRevertWeatherFormTrigger } from "../pokemon-forms/form-change-triggers";
|
||||||
import type { GameMode } from "#app/game-mode";
|
import type { GameMode } from "#app/game-mode";
|
||||||
import { applyChallenges, ChallengeType } from "../challenge";
|
import { applyChallenges } from "../challenge";
|
||||||
|
import { ChallengeType } from "#enums/challenge-type";
|
||||||
import { SwitchType } from "#enums/switch-type";
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -123,11 +123,14 @@ import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
|
|||||||
import { MultiHitType } from "#enums/MultiHitType";
|
import { MultiHitType } from "#enums/MultiHitType";
|
||||||
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves";
|
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves";
|
||||||
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
||||||
|
import { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types";
|
||||||
|
import { applyMoveAttrs } from "./apply-attrs";
|
||||||
|
import { frenzyMissFunc, getMoveTargets } from "./move-utils";
|
||||||
|
|
||||||
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||||
type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
|
export type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
|
||||||
|
|
||||||
export default class Move implements Localizable {
|
export default abstract class Move implements Localizable {
|
||||||
public id: MoveId;
|
public id: MoveId;
|
||||||
public name: string;
|
public name: string;
|
||||||
private _type: PokemonType;
|
private _type: PokemonType;
|
||||||
@ -147,6 +150,17 @@ export default class Move implements Localizable {
|
|||||||
private flags: number = 0;
|
private flags: number = 0;
|
||||||
private nameAppend: string = "";
|
private nameAppend: string = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the move is of the given subclass without requiring `instanceof`.
|
||||||
|
*
|
||||||
|
* ⚠️ Does _not_ work for {@linkcode ChargingAttackMove} and {@linkcode ChargingSelfStatusMove} subclasses. For those,
|
||||||
|
* use {@linkcode isChargingMove} instead.
|
||||||
|
*
|
||||||
|
* @param moveKind - The string name of the move to check against
|
||||||
|
* @returns Whether this move is of the provided type.
|
||||||
|
*/
|
||||||
|
public abstract is<K extends MoveKindString>(moveKind: K): this is MoveClassMap[K];
|
||||||
|
|
||||||
constructor(id: MoveId, type: PokemonType, category: MoveCategory, defaultMoveTarget: MoveTarget, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
constructor(id: MoveId, type: PokemonType, category: MoveCategory, defaultMoveTarget: MoveTarget, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this._type = type;
|
this._type = type;
|
||||||
@ -188,8 +202,12 @@ export default class Move implements Localizable {
|
|||||||
* @param attrType any attribute that extends {@linkcode MoveAttr}
|
* @param attrType any attribute that extends {@linkcode MoveAttr}
|
||||||
* @returns Array of attributes that match `attrType`, Empty Array if none match.
|
* @returns Array of attributes that match `attrType`, Empty Array if none match.
|
||||||
*/
|
*/
|
||||||
getAttrs<T extends MoveAttr>(attrType: Constructor<T>): T[] {
|
getAttrs<T extends MoveAttrString>(attrType: T): (MoveAttrMap[T])[] {
|
||||||
return this.attrs.filter((a): a is T => a instanceof attrType);
|
const targetAttr = MoveAttrs[attrType];
|
||||||
|
if (!targetAttr) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.attrs.filter((a): a is MoveAttrMap[T] => a instanceof targetAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,8 +215,13 @@ export default class Move implements Localizable {
|
|||||||
* @param attrType any attribute that extends {@linkcode MoveAttr}
|
* @param attrType any attribute that extends {@linkcode MoveAttr}
|
||||||
* @returns true if the move has attribute `attrType`
|
* @returns true if the move has attribute `attrType`
|
||||||
*/
|
*/
|
||||||
hasAttr<T extends MoveAttr>(attrType: Constructor<T>): boolean {
|
hasAttr(attrType: MoveAttrString): boolean {
|
||||||
return this.attrs.some((attr) => attr instanceof attrType);
|
const targetAttr = MoveAttrs[attrType];
|
||||||
|
// Guard against invalid attrType
|
||||||
|
if (!targetAttr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.attrs.some((attr) => attr instanceof targetAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -768,14 +791,14 @@ export default class Move implements Localizable {
|
|||||||
calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated: boolean = false) {
|
calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated: boolean = false) {
|
||||||
const moveAccuracy = new NumberHolder(this.accuracy);
|
const moveAccuracy = new NumberHolder(this.accuracy);
|
||||||
|
|
||||||
applyMoveAttrs(VariableAccuracyAttr, user, target, this, moveAccuracy);
|
applyMoveAttrs("VariableAccuracyAttr", user, target, this, moveAccuracy);
|
||||||
applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, simulated, moveAccuracy);
|
applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, simulated, moveAccuracy);
|
||||||
|
|
||||||
if (moveAccuracy.value === -1) {
|
if (moveAccuracy.value === -1) {
|
||||||
return moveAccuracy.value;
|
return moveAccuracy.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOhko = this.hasAttr(OneHitKOAccuracyAttr);
|
const isOhko = this.hasAttr("OneHitKOAccuracyAttr");
|
||||||
|
|
||||||
if (!isOhko) {
|
if (!isOhko) {
|
||||||
globalScene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
|
globalScene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
|
||||||
@ -815,7 +838,7 @@ export default class Move implements Localizable {
|
|||||||
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier);
|
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier);
|
||||||
|
|
||||||
const sourceTeraType = source.getTeraType();
|
const sourceTeraType = source.getTeraType();
|
||||||
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
||||||
power.value = 60;
|
power.value = 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,9 +870,9 @@ export default class Move implements Localizable {
|
|||||||
power.value *= typeBoost.boostValue;
|
power.value *= typeBoost.boostValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyMoveAttrs(VariablePowerAttr, source, target, this, power);
|
applyMoveAttrs("VariablePowerAttr", source, target, this, power);
|
||||||
|
|
||||||
if (!this.hasAttr(TypelessAttr)) {
|
if (!this.hasAttr("TypelessAttr")) {
|
||||||
globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power);
|
globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power);
|
||||||
globalScene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, typeChangeHolder.value, power);
|
globalScene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, typeChangeHolder.value, power);
|
||||||
}
|
}
|
||||||
@ -864,7 +887,7 @@ export default class Move implements Localizable {
|
|||||||
getPriority(user: Pokemon, simulated: boolean = true) {
|
getPriority(user: Pokemon, simulated: boolean = true) {
|
||||||
const priority = new NumberHolder(this.priority);
|
const priority = new NumberHolder(this.priority);
|
||||||
|
|
||||||
applyMoveAttrs(IncrementMovePriorityAttr, user, null, this, priority);
|
applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority);
|
||||||
applyAbAttrs(ChangeMovePriorityAbAttr, user, null, simulated, this, priority);
|
applyAbAttrs(ChangeMovePriorityAbAttr, user, null, simulated, this, priority);
|
||||||
|
|
||||||
return priority.value;
|
return priority.value;
|
||||||
@ -885,7 +908,7 @@ export default class Move implements Localizable {
|
|||||||
} else if (this.id === MoveId.TRIPLE_KICK) {
|
} else if (this.id === MoveId.TRIPLE_KICK) {
|
||||||
effectivePower = 47.07;
|
effectivePower = 47.07;
|
||||||
} else {
|
} else {
|
||||||
const multiHitAttr = this.getAttrs(MultiHitAttr)[0];
|
const multiHitAttr = this.getAttrs("MultiHitAttr")[0];
|
||||||
if (multiHitAttr) {
|
if (multiHitAttr) {
|
||||||
effectivePower = multiHitAttr.calculateExpectedHitCount(this) * this.power;
|
effectivePower = multiHitAttr.calculateExpectedHitCount(this) * this.power;
|
||||||
} else {
|
} else {
|
||||||
@ -898,10 +921,10 @@ export default class Move implements Localizable {
|
|||||||
// These are intentionally not else-if statements even though there are no
|
// These are intentionally not else-if statements even though there are no
|
||||||
// pokemon moves that have more than one of these attributes. This allows
|
// pokemon moves that have more than one of these attributes. This allows
|
||||||
// the function to future proof new moves / custom move behaviors.
|
// the function to future proof new moves / custom move behaviors.
|
||||||
if (this.hasAttr(DelayedAttackAttr)) {
|
if (this.hasAttr("DelayedAttackAttr")) {
|
||||||
numTurns += 2;
|
numTurns += 2;
|
||||||
}
|
}
|
||||||
if (this.hasAttr(RechargeAttr)) {
|
if (this.hasAttr("RechargeAttr")) {
|
||||||
numTurns += 1;
|
numTurns += 1;
|
||||||
}
|
}
|
||||||
if (this.isChargingMove()) {
|
if (this.isChargingMove()) {
|
||||||
@ -927,10 +950,10 @@ export default class Move implements Localizable {
|
|||||||
const isMultiTarget = multiple && targets.length > 1;
|
const isMultiTarget = multiple && targets.length > 1;
|
||||||
|
|
||||||
// ...cannot enhance multi-hit or sacrificial moves
|
// ...cannot enhance multi-hit or sacrificial moves
|
||||||
const exceptAttrs: Constructor<MoveAttr>[] = [
|
const exceptAttrs: MoveAttrString[] = [
|
||||||
MultiHitAttr,
|
"MultiHitAttr",
|
||||||
SacrificialAttr,
|
"SacrificialAttr",
|
||||||
SacrificialAttrOnHit
|
"SacrificialAttrOnHit"
|
||||||
];
|
];
|
||||||
|
|
||||||
// ...and cannot enhance these specific moves
|
// ...and cannot enhance these specific moves
|
||||||
@ -956,6 +979,13 @@ export default class Move implements Localizable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AttackMove extends Move {
|
export class AttackMove extends Move {
|
||||||
|
/** This field does not exist at runtime and must not be used.
|
||||||
|
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||||
|
*/
|
||||||
|
declare private _: never;
|
||||||
|
override is<K extends keyof MoveClassMap>(moveKind: K): this is MoveClassMap[K] {
|
||||||
|
return moveKind === "AttackMove";
|
||||||
|
}
|
||||||
constructor(id: MoveId, type: PokemonType, category: MoveCategory, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
constructor(id: MoveId, type: PokemonType, category: MoveCategory, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
||||||
super(id, type, category, MoveTarget.NEAR_OTHER, power, accuracy, pp, chance, priority, generation);
|
super(id, type, category, MoveTarget.NEAR_OTHER, power, accuracy, pp, chance, priority, generation);
|
||||||
|
|
||||||
@ -985,7 +1015,7 @@ export class AttackMove extends Move {
|
|||||||
const [ thisStat, offStat ]: EffectiveStat[] = this.category === MoveCategory.PHYSICAL ? [ Stat.ATK, Stat.SPATK ] : [ Stat.SPATK, Stat.ATK ];
|
const [ thisStat, offStat ]: EffectiveStat[] = this.category === MoveCategory.PHYSICAL ? [ Stat.ATK, Stat.SPATK ] : [ Stat.SPATK, Stat.ATK ];
|
||||||
const statHolder = new NumberHolder(user.getEffectiveStat(thisStat, target));
|
const statHolder = new NumberHolder(user.getEffectiveStat(thisStat, target));
|
||||||
const offStatValue = user.getEffectiveStat(offStat, target);
|
const offStatValue = user.getEffectiveStat(offStat, target);
|
||||||
applyMoveAttrs(VariableAtkAttr, user, target, move, statHolder);
|
applyMoveAttrs("VariableAtkAttr", user, target, move, statHolder);
|
||||||
const statRatio = offStatValue / statHolder.value;
|
const statRatio = offStatValue / statHolder.value;
|
||||||
if (statRatio <= 0.75) {
|
if (statRatio <= 0.75) {
|
||||||
attackScore *= 2;
|
attackScore *= 2;
|
||||||
@ -994,7 +1024,7 @@ export class AttackMove extends Move {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const power = new NumberHolder(this.calculateEffectivePower());
|
const power = new NumberHolder(this.calculateEffectivePower());
|
||||||
applyMoveAttrs(VariablePowerAttr, user, target, move, power);
|
applyMoveAttrs("VariablePowerAttr", user, target, move, power);
|
||||||
|
|
||||||
attackScore += Math.floor(power.value / 5);
|
attackScore += Math.floor(power.value / 5);
|
||||||
|
|
||||||
@ -1003,20 +1033,42 @@ export class AttackMove extends Move {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class StatusMove extends Move {
|
export class StatusMove extends Move {
|
||||||
|
/** This field does not exist at runtime and must not be used.
|
||||||
|
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||||
|
*/
|
||||||
|
declare private _: never;
|
||||||
constructor(id: MoveId, type: PokemonType, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
constructor(id: MoveId, type: PokemonType, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
||||||
super(id, type, MoveCategory.STATUS, MoveTarget.NEAR_OTHER, -1, accuracy, pp, chance, priority, generation);
|
super(id, type, MoveCategory.STATUS, MoveTarget.NEAR_OTHER, -1, accuracy, pp, chance, priority, generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override is<K extends MoveKindString>(moveKind: K): this is MoveClassMap[K] {
|
||||||
|
return moveKind === "StatusMove";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SelfStatusMove extends Move {
|
export class SelfStatusMove extends Move {
|
||||||
|
/** This field does not exist at runtime and must not be used.
|
||||||
|
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||||
|
*/
|
||||||
|
declare private _: never;
|
||||||
constructor(id: MoveId, type: PokemonType, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
constructor(id: MoveId, type: PokemonType, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
||||||
super(id, type, MoveCategory.STATUS, MoveTarget.USER, -1, accuracy, pp, chance, priority, generation);
|
super(id, type, MoveCategory.STATUS, MoveTarget.USER, -1, accuracy, pp, chance, priority, generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override is<K extends MoveKindString>(moveKind: K): this is MoveClassMap[K] {
|
||||||
|
return moveKind === "SelfStatusMove";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubMove = new (...args: any[]) => Move;
|
// TODO: Figure out how to improve the signature of this so that
|
||||||
|
// the `ChargeMove` function knows that the argument `Base` is a specific subclass of move that cannot
|
||||||
|
// be abstract.
|
||||||
|
// Right now, I only know how to do this by using the type conjunction (the & operators)
|
||||||
|
type SubMove = new (...args: any[]) => Move & {
|
||||||
|
is<K extends keyof MoveClassMap>(moveKind: K): this is MoveClassMap[K];
|
||||||
|
};
|
||||||
|
|
||||||
function ChargeMove<TBase extends SubMove>(Base: TBase) {
|
function ChargeMove<TBase extends SubMove>(Base: TBase, nameAppend: string) {
|
||||||
return class extends Base {
|
return class extends Base {
|
||||||
/** The animation to play during the move's charging phase */
|
/** The animation to play during the move's charging phase */
|
||||||
public readonly chargeAnim: ChargeAnim = ChargeAnim[`${MoveId[this.id]}_CHARGING`];
|
public readonly chargeAnim: ChargeAnim = ChargeAnim[`${MoveId[this.id]}_CHARGING`];
|
||||||
@ -1060,8 +1112,12 @@ function ChargeMove<TBase extends SubMove>(Base: TBase) {
|
|||||||
* @returns Array of attributes that match `attrType`, or an empty array if
|
* @returns Array of attributes that match `attrType`, or an empty array if
|
||||||
* no matches are found.
|
* no matches are found.
|
||||||
*/
|
*/
|
||||||
getChargeAttrs<T extends MoveAttr>(attrType: Constructor<T>): T[] {
|
getChargeAttrs<T extends MoveAttrString>(attrType: T): (MoveAttrMap[T])[] {
|
||||||
return this.chargeAttrs.filter((attr): attr is T => attr instanceof attrType);
|
const targetAttr = MoveAttrs[attrType];
|
||||||
|
if (!targetAttr) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.chargeAttrs.filter((attr): attr is MoveAttrMap[T] => attr instanceof targetAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1069,8 +1125,12 @@ function ChargeMove<TBase extends SubMove>(Base: TBase) {
|
|||||||
* @param attrType any attribute that extends {@linkcode MoveAttr}
|
* @param attrType any attribute that extends {@linkcode MoveAttr}
|
||||||
* @returns `true` if a matching attribute is found; `false` otherwise
|
* @returns `true` if a matching attribute is found; `false` otherwise
|
||||||
*/
|
*/
|
||||||
hasChargeAttr<T extends MoveAttr>(attrType: Constructor<T>): boolean {
|
hasChargeAttr<T extends MoveAttrString>(attrType: T): boolean {
|
||||||
return this.chargeAttrs.some((attr) => attr instanceof attrType);
|
const targetAttr = MoveAttrs[attrType];
|
||||||
|
if (!targetAttr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.chargeAttrs.some((attr) => attr instanceof targetAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1088,10 +1148,8 @@ function ChargeMove<TBase extends SubMove>(Base: TBase) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChargingAttackMove extends ChargeMove(AttackMove) {}
|
export class ChargingAttackMove extends ChargeMove(AttackMove, "ChargingAttackMove") {}
|
||||||
export class ChargingSelfStatusMove extends ChargeMove(SelfStatusMove) {}
|
export class ChargingSelfStatusMove extends ChargeMove(SelfStatusMove, "ChargingSelfStatusMove") {}
|
||||||
|
|
||||||
export type ChargingMove = ChargingAttackMove | ChargingSelfStatusMove;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class defining all {@linkcode Move} Attributes
|
* Base class defining all {@linkcode Move} Attributes
|
||||||
@ -1102,6 +1160,22 @@ export abstract class MoveAttr {
|
|||||||
/** Should this {@linkcode Move} target the user? */
|
/** Should this {@linkcode Move} target the user? */
|
||||||
public selfTarget: boolean;
|
public selfTarget: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this attribute is of the given type.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Used to avoid requring the caller to have imported the specific attribute type, avoiding circular dependencies.
|
||||||
|
* @param attr - The attribute to check against
|
||||||
|
* @returns Whether the attribute is an instance of the given type.
|
||||||
|
*/
|
||||||
|
public is<T extends MoveAttrString>(attr: T): this is MoveAttrMap[T] {
|
||||||
|
const targetAttr = MoveAttrs[attr];
|
||||||
|
if (!targetAttr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this instanceof targetAttr;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(selfTarget: boolean = false) {
|
constructor(selfTarget: boolean = false) {
|
||||||
this.selfTarget = selfTarget;
|
this.selfTarget = selfTarget;
|
||||||
}
|
}
|
||||||
@ -1268,7 +1342,7 @@ export class MoveEffectAttr extends MoveAttr {
|
|||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, !showAbility, moveChance, move);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, !showAbility, moveChance, move);
|
||||||
|
|
||||||
if ((!move.hasAttr(FlinchAttr) || moveChance.value <= move.chance) && !move.hasAttr(SecretPowerAttr)) {
|
if ((!move.hasAttr("FlinchAttr") || moveChance.value <= move.chance) && !move.hasAttr("SecretPowerAttr")) {
|
||||||
const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
globalScene.arena.applyTagsForSide(ArenaTagType.WATER_FIRE_PLEDGE, userSide, false, moveChance);
|
globalScene.arena.applyTagsForSide(ArenaTagType.WATER_FIRE_PLEDGE, userSide, false, moveChance);
|
||||||
}
|
}
|
||||||
@ -2331,7 +2405,7 @@ export class MultiHitAttr extends MoveAttr {
|
|||||||
*/
|
*/
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const hitType = new NumberHolder(this.intrinsicMultiHitType);
|
const hitType = new NumberHolder(this.intrinsicMultiHitType);
|
||||||
applyMoveAttrs(ChangeMultiHitTypeAttr, user, target, move, hitType);
|
applyMoveAttrs("ChangeMultiHitTypeAttr", user, target, move, hitType);
|
||||||
this.multiHitType = hitType.value;
|
this.multiHitType = hitType.value;
|
||||||
|
|
||||||
(args[0] as NumberHolder).value = this.getHitCount(user, target);
|
(args[0] as NumberHolder).value = this.getHitCount(user, target);
|
||||||
@ -3032,7 +3106,11 @@ export class WeatherInstantChargeAttr extends InstantChargeAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class OverrideMoveEffectAttr extends MoveAttr {
|
export class OverrideMoveEffectAttr extends MoveAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
/** This field does not exist at runtime and must not be used.
|
||||||
|
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||||
|
*/
|
||||||
|
declare private _: never;
|
||||||
|
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3114,7 +3192,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
|
|||||||
const allyMovePhase = globalScene.phaseManager.findPhase<MovePhase>((phase) => phase.is("MovePhase") && phase.pokemon.isPlayer() === user.isPlayer());
|
const allyMovePhase = globalScene.phaseManager.findPhase<MovePhase>((phase) => phase.is("MovePhase") && phase.pokemon.isPlayer() === user.isPlayer());
|
||||||
if (allyMovePhase) {
|
if (allyMovePhase) {
|
||||||
const allyMove = allyMovePhase.move.getMove();
|
const allyMove = allyMovePhase.move.getMove();
|
||||||
if (allyMove !== move && allyMove.hasAttr(AwaitCombinedPledgeAttr)) {
|
if (allyMove !== move && allyMove.hasAttr("AwaitCombinedPledgeAttr")) {
|
||||||
[ user, allyMovePhase.pokemon ].forEach((p) => p.turnData.combiningPledge = move.id);
|
[ user, allyMovePhase.pokemon ].forEach((p) => p.turnData.combiningPledge = move.id);
|
||||||
|
|
||||||
// "{userPokemonName} is waiting for {allyPokemonName}'s move..."
|
// "{userPokemonName} is waiting for {allyPokemonName}'s move..."
|
||||||
@ -3235,22 +3313,22 @@ export class StatStageChangeAttr extends MoveEffectAttr {
|
|||||||
switch (stat) {
|
switch (stat) {
|
||||||
case Stat.ATK:
|
case Stat.ATK:
|
||||||
if (this.selfTarget) {
|
if (this.selfTarget) {
|
||||||
noEffect = !user.getMoveset().find(m => m instanceof AttackMove && m.category === MoveCategory.PHYSICAL);
|
noEffect = !user.getMoveset().find(m => {const mv = m.getMove(); return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL;} );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Stat.DEF:
|
case Stat.DEF:
|
||||||
if (!this.selfTarget) {
|
if (!this.selfTarget) {
|
||||||
noEffect = !user.getMoveset().find(m => m instanceof AttackMove && m.category === MoveCategory.PHYSICAL);
|
noEffect = !user.getMoveset().find(m => {const mv = m.getMove(); return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL;} );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Stat.SPATK:
|
case Stat.SPATK:
|
||||||
if (this.selfTarget) {
|
if (this.selfTarget) {
|
||||||
noEffect = !user.getMoveset().find(m => m instanceof AttackMove && m.category === MoveCategory.SPECIAL);
|
noEffect = !user.getMoveset().find(m => {const mv = m.getMove(); return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL;} );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Stat.SPDEF:
|
case Stat.SPDEF:
|
||||||
if (!this.selfTarget) {
|
if (!this.selfTarget) {
|
||||||
noEffect = !user.getMoveset().find(m => m instanceof AttackMove && m.category === MoveCategory.SPECIAL);
|
noEffect = !user.getMoveset().find(m => {const mv = m.getMove(); return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL;} );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -5410,7 +5488,7 @@ export class FrenzyAttr extends MoveEffectAttr {
|
|||||||
new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true }));
|
new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true }));
|
||||||
user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id);
|
user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id);
|
||||||
} else {
|
} else {
|
||||||
applyMoveAttrs(AddBattlerTagAttr, user, target, move, args);
|
applyMoveAttrs("AddBattlerTagAttr", user, target, move, args);
|
||||||
user.lapseTag(BattlerTagType.FRENZY); // if FRENZY is already in effect (moveQueue.length > 0), lapse the tag
|
user.lapseTag(BattlerTagType.FRENZY); // if FRENZY is already in effect (moveQueue.length > 0), lapse the tag
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5418,15 +5496,6 @@ export class FrenzyAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute that grants {@link https://bulbapedia.bulbagarden.net/wiki/Semi-invulnerable_turn | semi-invulnerability} to the user during
|
* Attribute that grants {@link https://bulbapedia.bulbagarden.net/wiki/Semi-invulnerable_turn | semi-invulnerability} to the user during
|
||||||
* the associated move's charging phase. Should only be used for {@linkcode ChargingMove | ChargingMoves} as a `chargeAttr`.
|
* the associated move's charging phase. Should only be used for {@linkcode ChargingMove | ChargingMoves} as a `chargeAttr`.
|
||||||
@ -5781,7 +5850,7 @@ export class ProtectAttr extends AddBattlerTagAttr {
|
|||||||
|
|
||||||
while (moveHistory.length) {
|
while (moveHistory.length) {
|
||||||
turnMove = moveHistory.shift();
|
turnMove = moveHistory.shift();
|
||||||
if (!allMoves[turnMove?.move ?? MoveId.NONE].hasAttr(ProtectAttr) || turnMove?.result !== MoveResult.SUCCESS) {
|
if (!allMoves[turnMove?.move ?? MoveId.NONE].hasAttr("ProtectAttr") || turnMove?.result !== MoveResult.SUCCESS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
timesUsed++;
|
timesUsed++;
|
||||||
@ -5912,7 +5981,7 @@ export class AddArenaTagAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((move.chance < 0 || move.chance === 100 || user.randBattleSeedInt(100) < move.chance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) {
|
if ((move.chance < 0 || move.chance === 100 || user.randBattleSeedInt(100) < move.chance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) {
|
||||||
const side = ((this.selfSideTarget ? user : target).isPlayer() !== (move.hasAttr(AddArenaTrapTagAttr) && target === user)) ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
const side = ((this.selfSideTarget ? user : target).isPlayer() !== (move.hasAttr("AddArenaTrapTagAttr") && target === user)) ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
globalScene.arena.addTag(this.tagType, this.turnCount, move.id, user.id, side);
|
globalScene.arena.addTag(this.tagType, this.turnCount, move.id, user.id, side);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -6376,7 +6445,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
return (user, target, move) => {
|
return (user, target, move) => {
|
||||||
const switchOutTarget = (this.selfSwitch ? user : target);
|
const switchOutTarget = (this.selfSwitch ? user : target);
|
||||||
const player = switchOutTarget.isPlayer();
|
const player = switchOutTarget.isPlayer();
|
||||||
const forceSwitchAttr = move.getAttrs(ForceSwitchOutAttr).find(attr => attr.switchType === SwitchType.FORCE_SWITCH);
|
const forceSwitchAttr = move.getAttrs("ForceSwitchOutAttr").find(attr => attr.switchType === SwitchType.FORCE_SWITCH);
|
||||||
|
|
||||||
if (!this.selfSwitch) {
|
if (!this.selfSwitch) {
|
||||||
if (move.hitsSubstitute(user, target)) {
|
if (move.hitsSubstitute(user, target)) {
|
||||||
@ -7945,58 +8014,6 @@ const attackedByItemMessageFunc = (user: Pokemon, target: Pokemon, move: Move) =
|
|||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MoveAttrFilter = (attr: MoveAttr) => boolean;
|
|
||||||
|
|
||||||
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: Constructor<MoveAttr>,
|
|
||||||
user: Pokemon | null,
|
|
||||||
target: Pokemon | null,
|
|
||||||
move: Move,
|
|
||||||
...args: any[]
|
|
||||||
): void {
|
|
||||||
applyMoveAttrsInternal((attr: MoveAttr) => attr instanceof 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: Constructor<MoveAttr>,
|
|
||||||
user: Pokemon | null,
|
|
||||||
target: Pokemon | null,
|
|
||||||
move: ChargingMove,
|
|
||||||
...args: any[]
|
|
||||||
): void {
|
|
||||||
applyMoveChargeAttrsInternal((attr: MoveAttr) => attr instanceof attrType, user, target, move, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MoveCondition {
|
export class MoveCondition {
|
||||||
protected func: MoveConditionFunc;
|
protected func: MoveConditionFunc;
|
||||||
|
|
||||||
@ -8175,73 +8192,232 @@ export type MoveTargetSet = {
|
|||||||
multiple: boolean;
|
multiple: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getMoveTargets(user: Pokemon, move: MoveId, replaceTarget?: MoveTarget): MoveTargetSet {
|
/**
|
||||||
const variableTarget = new NumberHolder(0);
|
* Map of Move attributes to their respective classes. Used for instanceof checks.
|
||||||
user.getOpponents(false).forEach(p => applyMoveAttrs(VariableTargetAttr, user, p, allMoves[move], variableTarget));
|
*/
|
||||||
|
const MoveAttrs = Object.freeze({
|
||||||
|
MoveEffectAttr,
|
||||||
|
MoveHeaderAttr,
|
||||||
|
MessageHeaderAttr,
|
||||||
|
AddBattlerTagAttr,
|
||||||
|
AddBattlerTagHeaderAttr,
|
||||||
|
BeakBlastHeaderAttr,
|
||||||
|
PreMoveMessageAttr,
|
||||||
|
PreUseInterruptAttr,
|
||||||
|
RespectAttackTypeImmunityAttr,
|
||||||
|
IgnoreOpponentStatStagesAttr,
|
||||||
|
HighCritAttr,
|
||||||
|
CritOnlyAttr,
|
||||||
|
FixedDamageAttr,
|
||||||
|
UserHpDamageAttr,
|
||||||
|
TargetHalfHpDamageAttr,
|
||||||
|
MatchHpAttr,
|
||||||
|
CounterDamageAttr,
|
||||||
|
LevelDamageAttr,
|
||||||
|
RandomLevelDamageAttr,
|
||||||
|
ModifiedDamageAttr,
|
||||||
|
SurviveDamageAttr,
|
||||||
|
SplashAttr,
|
||||||
|
CelebrateAttr,
|
||||||
|
RecoilAttr,
|
||||||
|
SacrificialAttr,
|
||||||
|
SacrificialAttrOnHit,
|
||||||
|
HalfSacrificialAttr,
|
||||||
|
AddSubstituteAttr,
|
||||||
|
HealAttr,
|
||||||
|
PartyStatusCureAttr,
|
||||||
|
FlameBurstAttr,
|
||||||
|
SacrificialFullRestoreAttr,
|
||||||
|
IgnoreWeatherTypeDebuffAttr,
|
||||||
|
WeatherHealAttr,
|
||||||
|
PlantHealAttr,
|
||||||
|
SandHealAttr,
|
||||||
|
BoostHealAttr,
|
||||||
|
HealOnAllyAttr,
|
||||||
|
HitHealAttr,
|
||||||
|
IncrementMovePriorityAttr,
|
||||||
|
MultiHitAttr,
|
||||||
|
ChangeMultiHitTypeAttr,
|
||||||
|
WaterShurikenMultiHitTypeAttr,
|
||||||
|
StatusEffectAttr,
|
||||||
|
MultiStatusEffectAttr,
|
||||||
|
PsychoShiftEffectAttr,
|
||||||
|
StealHeldItemChanceAttr,
|
||||||
|
RemoveHeldItemAttr,
|
||||||
|
EatBerryAttr,
|
||||||
|
StealEatBerryAttr,
|
||||||
|
HealStatusEffectAttr,
|
||||||
|
BypassSleepAttr,
|
||||||
|
BypassBurnDamageReductionAttr,
|
||||||
|
WeatherChangeAttr,
|
||||||
|
ClearWeatherAttr,
|
||||||
|
TerrainChangeAttr,
|
||||||
|
ClearTerrainAttr,
|
||||||
|
OneHitKOAttr,
|
||||||
|
InstantChargeAttr,
|
||||||
|
WeatherInstantChargeAttr,
|
||||||
|
OverrideMoveEffectAttr,
|
||||||
|
DelayedAttackAttr,
|
||||||
|
AwaitCombinedPledgeAttr,
|
||||||
|
StatStageChangeAttr,
|
||||||
|
SecretPowerAttr,
|
||||||
|
PostVictoryStatStageChangeAttr,
|
||||||
|
AcupressureStatStageChangeAttr,
|
||||||
|
GrowthStatStageChangeAttr,
|
||||||
|
CutHpStatStageBoostAttr,
|
||||||
|
OrderUpStatBoostAttr,
|
||||||
|
CopyStatsAttr,
|
||||||
|
InvertStatsAttr,
|
||||||
|
ResetStatsAttr,
|
||||||
|
SwapStatStagesAttr,
|
||||||
|
HpSplitAttr,
|
||||||
|
VariablePowerAttr,
|
||||||
|
LessPPMorePowerAttr,
|
||||||
|
MovePowerMultiplierAttr,
|
||||||
|
BeatUpAttr,
|
||||||
|
DoublePowerChanceAttr,
|
||||||
|
ConsecutiveUsePowerMultiplierAttr,
|
||||||
|
ConsecutiveUseDoublePowerAttr,
|
||||||
|
ConsecutiveUseMultiBasePowerAttr,
|
||||||
|
WeightPowerAttr,
|
||||||
|
ElectroBallPowerAttr,
|
||||||
|
GyroBallPowerAttr,
|
||||||
|
LowHpPowerAttr,
|
||||||
|
CompareWeightPowerAttr,
|
||||||
|
HpPowerAttr,
|
||||||
|
OpponentHighHpPowerAttr,
|
||||||
|
FirstAttackDoublePowerAttr,
|
||||||
|
TurnDamagedDoublePowerAttr,
|
||||||
|
MagnitudePowerAttr,
|
||||||
|
AntiSunlightPowerDecreaseAttr,
|
||||||
|
FriendshipPowerAttr,
|
||||||
|
RageFistPowerAttr,
|
||||||
|
PositiveStatStagePowerAttr,
|
||||||
|
PunishmentPowerAttr,
|
||||||
|
PresentPowerAttr,
|
||||||
|
WaterShurikenPowerAttr,
|
||||||
|
SpitUpPowerAttr,
|
||||||
|
SwallowHealAttr,
|
||||||
|
MultiHitPowerIncrementAttr,
|
||||||
|
LastMoveDoublePowerAttr,
|
||||||
|
CombinedPledgePowerAttr,
|
||||||
|
CombinedPledgeStabBoostAttr,
|
||||||
|
RoundPowerAttr,
|
||||||
|
CueNextRoundAttr,
|
||||||
|
StatChangeBeforeDmgCalcAttr,
|
||||||
|
SpectralThiefAttr,
|
||||||
|
VariableAtkAttr,
|
||||||
|
TargetAtkUserAtkAttr,
|
||||||
|
DefAtkAttr,
|
||||||
|
VariableDefAttr,
|
||||||
|
DefDefAttr,
|
||||||
|
VariableAccuracyAttr,
|
||||||
|
ThunderAccuracyAttr,
|
||||||
|
StormAccuracyAttr,
|
||||||
|
AlwaysHitMinimizeAttr,
|
||||||
|
ToxicAccuracyAttr,
|
||||||
|
BlizzardAccuracyAttr,
|
||||||
|
VariableMoveCategoryAttr,
|
||||||
|
PhotonGeyserCategoryAttr,
|
||||||
|
TeraMoveCategoryAttr,
|
||||||
|
TeraBlastPowerAttr,
|
||||||
|
StatusCategoryOnAllyAttr,
|
||||||
|
ShellSideArmCategoryAttr,
|
||||||
|
VariableMoveTypeAttr,
|
||||||
|
FormChangeItemTypeAttr,
|
||||||
|
TechnoBlastTypeAttr,
|
||||||
|
AuraWheelTypeAttr,
|
||||||
|
RagingBullTypeAttr,
|
||||||
|
IvyCudgelTypeAttr,
|
||||||
|
WeatherBallTypeAttr,
|
||||||
|
TerrainPulseTypeAttr,
|
||||||
|
HiddenPowerTypeAttr,
|
||||||
|
TeraBlastTypeAttr,
|
||||||
|
TeraStarstormTypeAttr,
|
||||||
|
MatchUserTypeAttr,
|
||||||
|
CombinedPledgeTypeAttr,
|
||||||
|
VariableMoveTypeMultiplierAttr,
|
||||||
|
NeutralDamageAgainstFlyingTypeMultiplierAttr,
|
||||||
|
IceNoEffectTypeAttr,
|
||||||
|
FlyingTypeMultiplierAttr,
|
||||||
|
VariableMoveTypeChartAttr,
|
||||||
|
FreezeDryAttr,
|
||||||
|
OneHitKOAccuracyAttr,
|
||||||
|
SheerColdAccuracyAttr,
|
||||||
|
MissEffectAttr,
|
||||||
|
NoEffectAttr,
|
||||||
|
TypelessAttr,
|
||||||
|
BypassRedirectAttr,
|
||||||
|
FrenzyAttr,
|
||||||
|
SemiInvulnerableAttr,
|
||||||
|
LeechSeedAttr,
|
||||||
|
FallDownAttr,
|
||||||
|
GulpMissileTagAttr,
|
||||||
|
JawLockAttr,
|
||||||
|
CurseAttr,
|
||||||
|
LapseBattlerTagAttr,
|
||||||
|
RemoveBattlerTagAttr,
|
||||||
|
FlinchAttr,
|
||||||
|
ConfuseAttr,
|
||||||
|
RechargeAttr,
|
||||||
|
TrapAttr,
|
||||||
|
ProtectAttr,
|
||||||
|
IgnoreAccuracyAttr,
|
||||||
|
FaintCountdownAttr,
|
||||||
|
RemoveAllSubstitutesAttr,
|
||||||
|
HitsTagAttr,
|
||||||
|
HitsTagForDoubleDamageAttr,
|
||||||
|
AddArenaTagAttr,
|
||||||
|
RemoveArenaTagsAttr,
|
||||||
|
AddArenaTrapTagAttr,
|
||||||
|
AddArenaTrapTagHitAttr,
|
||||||
|
RemoveArenaTrapAttr,
|
||||||
|
RemoveScreensAttr,
|
||||||
|
SwapArenaTagsAttr,
|
||||||
|
AddPledgeEffectAttr,
|
||||||
|
RevivalBlessingAttr,
|
||||||
|
ForceSwitchOutAttr,
|
||||||
|
ChillyReceptionAttr,
|
||||||
|
RemoveTypeAttr,
|
||||||
|
CopyTypeAttr,
|
||||||
|
CopyBiomeTypeAttr,
|
||||||
|
ChangeTypeAttr,
|
||||||
|
AddTypeAttr,
|
||||||
|
FirstMoveTypeAttr,
|
||||||
|
CallMoveAttr,
|
||||||
|
RandomMoveAttr,
|
||||||
|
RandomMovesetMoveAttr,
|
||||||
|
NaturePowerAttr,
|
||||||
|
CopyMoveAttr,
|
||||||
|
RepeatMoveAttr,
|
||||||
|
ReducePpMoveAttr,
|
||||||
|
AttackReducePpMoveAttr,
|
||||||
|
MovesetCopyMoveAttr,
|
||||||
|
SketchAttr,
|
||||||
|
AbilityChangeAttr,
|
||||||
|
AbilityCopyAttr,
|
||||||
|
AbilityGiveAttr,
|
||||||
|
SwitchAbilitiesAttr,
|
||||||
|
SuppressAbilitiesAttr,
|
||||||
|
TransformAttr,
|
||||||
|
SwapStatAttr,
|
||||||
|
ShiftStatAttr,
|
||||||
|
AverageStatsAttr,
|
||||||
|
MoneyAttr,
|
||||||
|
DestinyBondAttr,
|
||||||
|
AddBattlerTagIfBoostedAttr,
|
||||||
|
StatusIfBoostedAttr,
|
||||||
|
LastResortAttr,
|
||||||
|
VariableTargetAttr,
|
||||||
|
AfterYouAttr,
|
||||||
|
ForceLastAttr,
|
||||||
|
HitsSameTypeAttr,
|
||||||
|
ResistLastMoveTypeAttr,
|
||||||
|
ExposedMoveAttr,
|
||||||
|
});
|
||||||
|
|
||||||
let moveTarget: MoveTarget | undefined;
|
/** Map of of move attribute names to their constructors */
|
||||||
if (allMoves[move].hasAttr(VariableTargetAttr)) {
|
export type MoveAttrConstructorMap = typeof MoveAttrs;
|
||||||
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 selfStatLowerMoves: MoveId[] = [];
|
export const selfStatLowerMoves: MoveId[] = [];
|
||||||
|
|
||||||
@ -11255,7 +11431,7 @@ export function initMoves() {
|
|||||||
.attr(StatusEffectAttr, StatusEffect.TOXIC)
|
.attr(StatusEffectAttr, StatusEffect.TOXIC)
|
||||||
);
|
);
|
||||||
allMoves.map(m => {
|
allMoves.map(m => {
|
||||||
if (m.getAttrs(StatStageChangeAttr).some(a => a.selfTarget && a.stages < 0)) {
|
if (m.getAttrs("StatStageChangeAttr").some(a => a.selfTarget && a.stages < 0)) {
|
||||||
selfStatLowerMoves.push(m.id);
|
selfStatLowerMoves.push(m.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
93
src/data/moves/pokemon-move.ts
Normal file
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);
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,8 @@ import {
|
|||||||
transitionMysteryEncounterIntroVisuals,
|
transitionMysteryEncounterIntroVisuals,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
@ -25,7 +26,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { randInt } from "#app/utils/common";
|
import { randInt } from "#app/utils/common";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import {
|
import {
|
||||||
applyModifierTypeToPlayerPokemon,
|
applyModifierTypeToPlayerPokemon,
|
||||||
catchPokemon,
|
catchPokemon,
|
||||||
|
@ -24,7 +24,7 @@ import { TrainerType } from "#enums/trainer-type";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
@ -37,11 +37,11 @@ import { UiMode } from "#enums/ui-mode";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { Ability } from "#app/data/abilities/ability-class";
|
import { Ability } from "#app/data/abilities/ability-class";
|
||||||
import { BerryModifier } from "#app/modifier/modifier";
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -23,7 +23,8 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
|||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
@ -7,7 +7,8 @@ import {
|
|||||||
setEncounterExp,
|
setEncounterExp,
|
||||||
setEncounterRewards,
|
setEncounterRewards,
|
||||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
@ -24,9 +24,9 @@ import { SpeciesId } from "#enums/species-id";
|
|||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
|
@ -13,7 +13,7 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst
|
|||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { FieldPosition } from "#app/field/pokemon";
|
import { FieldPosition } from "#enums/field-position";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
@ -25,7 +25,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
|
|||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
||||||
import { addPokeballOpenParticles } from "#app/field/anims";
|
import { addPokeballOpenParticles } from "#app/field/anims";
|
||||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
@ -33,7 +33,8 @@ import {
|
|||||||
} from "#app/utils/common";
|
} from "#app/utils/common";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { EnemyPokemon, PokemonMove } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import {
|
import {
|
||||||
HiddenAbilityRateBoosterModifier,
|
HiddenAbilityRateBoosterModifier,
|
||||||
|
@ -11,7 +11,7 @@ import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encount
|
|||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
|
||||||
const OPTION_1_REQUIRED_MOVE = MoveId.SURF;
|
const OPTION_1_REQUIRED_MOVE = MoveId.SURF;
|
||||||
const OPTION_2_REQUIRED_MOVE = MoveId.FLY;
|
const OPTION_2_REQUIRED_MOVE = MoveId.FLY;
|
||||||
|
@ -21,8 +21,9 @@ import {
|
|||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { AiType, PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
import { AiType } from "#enums/ai-type";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
@ -17,11 +17,11 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
@ -23,7 +23,7 @@ import { Nature } from "#enums/nature";
|
|||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/abilities/ability";
|
import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/abilities/ability";
|
||||||
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
|
@ -12,7 +12,7 @@ import { speciesStarterCosts } from "#app/data/balance/starters";
|
|||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { AbilityAttr } from "#app/system/game-data";
|
import { AbilityAttr } from "#enums/ability-attr";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { isNullOrUndefined, randSeedShuffle } from "#app/utils/common";
|
import { isNullOrUndefined, randSeedShuffle } from "#app/utils/common";
|
||||||
|
@ -24,8 +24,8 @@ import i18next from "#app/plugins/i18n";
|
|||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
@ -29,8 +29,7 @@ import {
|
|||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { SelfStatusMove } from "#app/data/moves/move";
|
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
@ -175,7 +174,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
|
|||||||
// Check what type of move the egg move is to determine target
|
// Check what type of move the egg move is to determine target
|
||||||
const pokemonMove = new PokemonMove(eggMove);
|
const pokemonMove = new PokemonMove(eggMove);
|
||||||
const move = pokemonMove.getMove();
|
const move = pokemonMove.getMove();
|
||||||
const target = move instanceof SelfStatusMove ? BattlerIndex.ENEMY : BattlerIndex.PLAYER;
|
const target = move.is("SelfStatusMove") ? BattlerIndex.ENEMY : BattlerIndex.PLAYER;
|
||||||
|
|
||||||
encounter.startOfBattleEffects.push({
|
encounter.startOfBattleEffects.push({
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
|
@ -16,7 +16,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common";
|
import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
@ -2,7 +2,9 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import { allAbilities } from "../data-lists";
|
import { allAbilities } from "../data-lists";
|
||||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||||
|
import { SpeciesFormChangeItemTrigger } from "../pokemon-forms/form-change-triggers";
|
||||||
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import type { PokemonMove } from "../moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils/common";
|
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils/common";
|
||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
@ -20,11 +21,11 @@ import {
|
|||||||
StatusEffectRequirement,
|
StatusEffectRequirement,
|
||||||
WaveRangeRequirement,
|
WaveRangeRequirement,
|
||||||
} from "./mystery-encounter-requirements";
|
} from "./mystery-encounter-requirements";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import type { GameModes } from "#app/game-mode";
|
import type { GameModes } from "#enums/game-modes";
|
||||||
import type { EncounterAnim } from "#enums/encounter-anims";
|
import type { EncounterAnim } from "#enums/encounter-anims";
|
||||||
import type { Challenges } from "#enums/challenges";
|
import type { Challenges } from "#enums/challenges";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type Battle from "#app/battle";
|
import type Battle from "#app/battle";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
|
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
|
||||||
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
@ -8,9 +8,12 @@ import {
|
|||||||
WEIGHT_INCREMENT_ON_SPAWN_MISS,
|
WEIGHT_INCREMENT_ON_SPAWN_MISS,
|
||||||
} from "#app/data/mystery-encounters/mystery-encounters";
|
} from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import type { AiType, PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import type { AiType } from "#enums/ai-type";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { EnemyPokemon, FieldPosition, PokemonMove } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
import { FieldPosition } from "#enums/field-position";
|
||||||
import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type";
|
import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type";
|
||||||
import {
|
import {
|
||||||
getPartyLuckValue,
|
getPartyLuckValue,
|
||||||
@ -30,7 +33,8 @@ import type { BattlerTagType } from "#enums/battler-tag-type";
|
|||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
import type { TrainerType } from "#enums/trainer-type";
|
import type { TrainerType } from "#enums/trainer-type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Trainer, { TrainerVariant } from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import type { Gender } from "#app/data/gender";
|
import type { Gender } from "#app/data/gender";
|
||||||
import type { Nature } from "#enums/nature";
|
import type { Nature } from "#enums/nature";
|
||||||
import type { MoveId } from "#enums/move-id";
|
import type { MoveId } from "#enums/move-id";
|
||||||
|
@ -1,139 +1,30 @@
|
|||||||
import { PokemonFormChangeItemModifier } from "../modifier/modifier";
|
|
||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
|
||||||
import { allMoves } from "./data-lists";
|
import { allMoves } from "./data-lists";
|
||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import type { Constructor, nil } from "#app/utils/common";
|
import type { Constructor, nil } from "#app/utils/common";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import type { TimeOfDay } from "#enums/time-of-day";
|
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
|
||||||
import i18next from "i18next";
|
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { Challenges } from "#app/enums/challenges";
|
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
export enum FormChangeItem {
|
import {
|
||||||
NONE,
|
MeloettaFormChangePostMoveTrigger,
|
||||||
|
SpeciesDefaultFormMatchTrigger,
|
||||||
ABOMASITE,
|
SpeciesFormChangeAbilityTrigger,
|
||||||
ABSOLITE,
|
SpeciesFormChangeActiveTrigger,
|
||||||
AERODACTYLITE,
|
SpeciesFormChangeCompoundTrigger,
|
||||||
AGGRONITE,
|
SpeciesFormChangeItemTrigger,
|
||||||
ALAKAZITE,
|
SpeciesFormChangeLapseTeraTrigger,
|
||||||
ALTARIANITE,
|
SpeciesFormChangeManualTrigger,
|
||||||
AMPHAROSITE,
|
SpeciesFormChangeMoveLearnedTrigger,
|
||||||
AUDINITE,
|
SpeciesFormChangePreMoveTrigger,
|
||||||
BANETTITE,
|
SpeciesFormChangeRevertWeatherFormTrigger,
|
||||||
BEEDRILLITE,
|
SpeciesFormChangeTeraTrigger,
|
||||||
BLASTOISINITE,
|
type SpeciesFormChangeTrigger,
|
||||||
BLAZIKENITE,
|
SpeciesFormChangeWeatherTrigger,
|
||||||
CAMERUPTITE,
|
} from "./pokemon-forms/form-change-triggers";
|
||||||
CHARIZARDITE_X,
|
|
||||||
CHARIZARDITE_Y,
|
|
||||||
DIANCITE,
|
|
||||||
GALLADITE,
|
|
||||||
GARCHOMPITE,
|
|
||||||
GARDEVOIRITE,
|
|
||||||
GENGARITE,
|
|
||||||
GLALITITE,
|
|
||||||
GYARADOSITE,
|
|
||||||
HERACRONITE,
|
|
||||||
HOUNDOOMINITE,
|
|
||||||
KANGASKHANITE,
|
|
||||||
LATIASITE,
|
|
||||||
LATIOSITE,
|
|
||||||
LOPUNNITE,
|
|
||||||
LUCARIONITE,
|
|
||||||
MANECTITE,
|
|
||||||
MAWILITE,
|
|
||||||
MEDICHAMITE,
|
|
||||||
METAGROSSITE,
|
|
||||||
MEWTWONITE_X,
|
|
||||||
MEWTWONITE_Y,
|
|
||||||
PIDGEOTITE,
|
|
||||||
PINSIRITE,
|
|
||||||
RAYQUAZITE,
|
|
||||||
SABLENITE,
|
|
||||||
SALAMENCITE,
|
|
||||||
SCEPTILITE,
|
|
||||||
SCIZORITE,
|
|
||||||
SHARPEDONITE,
|
|
||||||
SLOWBRONITE,
|
|
||||||
STEELIXITE,
|
|
||||||
SWAMPERTITE,
|
|
||||||
TYRANITARITE,
|
|
||||||
VENUSAURITE,
|
|
||||||
|
|
||||||
BLUE_ORB = 50,
|
|
||||||
RED_ORB,
|
|
||||||
ADAMANT_CRYSTAL,
|
|
||||||
LUSTROUS_GLOBE,
|
|
||||||
GRISEOUS_CORE,
|
|
||||||
REVEAL_GLASS,
|
|
||||||
MAX_MUSHROOMS,
|
|
||||||
DARK_STONE,
|
|
||||||
LIGHT_STONE,
|
|
||||||
PRISON_BOTTLE,
|
|
||||||
RUSTED_SWORD,
|
|
||||||
RUSTED_SHIELD,
|
|
||||||
ICY_REINS_OF_UNITY,
|
|
||||||
SHADOW_REINS_OF_UNITY,
|
|
||||||
ULTRANECROZIUM_Z,
|
|
||||||
|
|
||||||
SHARP_METEORITE = 100,
|
|
||||||
HARD_METEORITE,
|
|
||||||
SMOOTH_METEORITE,
|
|
||||||
GRACIDEA,
|
|
||||||
SHOCK_DRIVE,
|
|
||||||
BURN_DRIVE,
|
|
||||||
CHILL_DRIVE,
|
|
||||||
DOUSE_DRIVE,
|
|
||||||
N_SOLARIZER,
|
|
||||||
N_LUNARIZER,
|
|
||||||
WELLSPRING_MASK,
|
|
||||||
HEARTHFLAME_MASK,
|
|
||||||
CORNERSTONE_MASK,
|
|
||||||
FIST_PLATE,
|
|
||||||
SKY_PLATE,
|
|
||||||
TOXIC_PLATE,
|
|
||||||
EARTH_PLATE,
|
|
||||||
STONE_PLATE,
|
|
||||||
INSECT_PLATE,
|
|
||||||
SPOOKY_PLATE,
|
|
||||||
IRON_PLATE,
|
|
||||||
FLAME_PLATE,
|
|
||||||
SPLASH_PLATE,
|
|
||||||
MEADOW_PLATE,
|
|
||||||
ZAP_PLATE,
|
|
||||||
MIND_PLATE,
|
|
||||||
ICICLE_PLATE,
|
|
||||||
DRACO_PLATE,
|
|
||||||
DREAD_PLATE,
|
|
||||||
PIXIE_PLATE,
|
|
||||||
BLANK_PLATE, // TODO: Find a potential use for this
|
|
||||||
LEGEND_PLATE, // TODO: Find a potential use for this
|
|
||||||
FIGHTING_MEMORY,
|
|
||||||
FLYING_MEMORY,
|
|
||||||
POISON_MEMORY,
|
|
||||||
GROUND_MEMORY,
|
|
||||||
ROCK_MEMORY,
|
|
||||||
BUG_MEMORY,
|
|
||||||
GHOST_MEMORY,
|
|
||||||
STEEL_MEMORY,
|
|
||||||
FIRE_MEMORY,
|
|
||||||
WATER_MEMORY,
|
|
||||||
GRASS_MEMORY,
|
|
||||||
ELECTRIC_MEMORY,
|
|
||||||
PSYCHIC_MEMORY,
|
|
||||||
ICE_MEMORY,
|
|
||||||
DRAGON_MEMORY,
|
|
||||||
DARK_MEMORY,
|
|
||||||
FAIRY_MEMORY,
|
|
||||||
NORMAL_MEMORY, // TODO: Find a potential use for this
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
|
export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
|
||||||
export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void;
|
export type SpeciesFormChangeConditionEnforceFunc = (p: Pokemon) => void;
|
||||||
@ -214,347 +105,6 @@ export class SpeciesFormChangeCondition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class SpeciesFormChangeTrigger {
|
|
||||||
public description = "";
|
|
||||||
|
|
||||||
canChange(_pokemon: Pokemon): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasTriggerType(triggerType: Constructor<SpeciesFormChangeTrigger>): boolean {
|
|
||||||
return this instanceof triggerType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeManualTrigger extends SpeciesFormChangeTrigger {}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeAbilityTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public description: string = i18next.t("pokemonEvolutions:Forms.ability");
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeCompoundTrigger {
|
|
||||||
public description = "";
|
|
||||||
public triggers: SpeciesFormChangeTrigger[];
|
|
||||||
|
|
||||||
constructor(...triggers: SpeciesFormChangeTrigger[]) {
|
|
||||||
this.triggers = triggers;
|
|
||||||
this.description = this.triggers
|
|
||||||
.filter(trigger => trigger?.description?.length > 0)
|
|
||||||
.map(trigger => trigger.description)
|
|
||||||
.join(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
for (const trigger of this.triggers) {
|
|
||||||
if (!trigger.canChange(pokemon)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasTriggerType(triggerType: Constructor<SpeciesFormChangeTrigger>): boolean {
|
|
||||||
return !!this.triggers.find(t => t.hasTriggerType(triggerType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeItemTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public item: FormChangeItem;
|
|
||||||
public active: boolean;
|
|
||||||
|
|
||||||
constructor(item: FormChangeItem, active = true) {
|
|
||||||
super();
|
|
||||||
this.item = item;
|
|
||||||
this.active = active;
|
|
||||||
this.description = this.active
|
|
||||||
? i18next.t("pokemonEvolutions:Forms.item", {
|
|
||||||
item: i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.item]}`),
|
|
||||||
})
|
|
||||||
: i18next.t("pokemonEvolutions:Forms.deactivateItem", {
|
|
||||||
item: i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.item]}`),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return !!globalScene.findModifier(
|
|
||||||
m =>
|
|
||||||
m instanceof PokemonFormChangeItemModifier &&
|
|
||||||
m.pokemonId === pokemon.id &&
|
|
||||||
m.formChangeItem === this.item &&
|
|
||||||
m.active === this.active,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeTimeOfDayTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public timesOfDay: TimeOfDay[];
|
|
||||||
|
|
||||||
constructor(...timesOfDay: TimeOfDay[]) {
|
|
||||||
super();
|
|
||||||
this.timesOfDay = timesOfDay;
|
|
||||||
this.description = i18next.t("pokemonEvolutions:Forms.timeOfDay");
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(_pokemon: Pokemon): boolean {
|
|
||||||
return this.timesOfDay.indexOf(globalScene.arena.getTimeOfDay()) > -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeActiveTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public active: boolean;
|
|
||||||
|
|
||||||
constructor(active = false) {
|
|
||||||
super();
|
|
||||||
this.active = active;
|
|
||||||
this.description = this.active
|
|
||||||
? i18next.t("pokemonEvolutions:Forms.enter")
|
|
||||||
: i18next.t("pokemonEvolutions:Forms.leave");
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return pokemon.isActive(true) === this.active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public statusEffects: StatusEffect[];
|
|
||||||
public invert: boolean;
|
|
||||||
|
|
||||||
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
|
|
||||||
super();
|
|
||||||
if (!Array.isArray(statusEffects)) {
|
|
||||||
statusEffects = [statusEffects];
|
|
||||||
}
|
|
||||||
this.statusEffects = statusEffects;
|
|
||||||
this.invert = invert;
|
|
||||||
this.description = i18next.t("pokemonEvolutions:Forms.statusEffect");
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return this.statusEffects.indexOf(pokemon.status?.effect || StatusEffect.NONE) > -1 !== this.invert;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public move: MoveId;
|
|
||||||
public known: boolean;
|
|
||||||
|
|
||||||
constructor(move: MoveId, known = true) {
|
|
||||||
super();
|
|
||||||
this.move = move;
|
|
||||||
this.known = known;
|
|
||||||
const moveKey = MoveId[this.move]
|
|
||||||
.split("_")
|
|
||||||
.filter(f => f)
|
|
||||||
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
|
||||||
.join("") as unknown as string;
|
|
||||||
this.description = known
|
|
||||||
? i18next.t("pokemonEvolutions:Forms.moveLearned", {
|
|
||||||
move: i18next.t(`move:${moveKey}.name`),
|
|
||||||
})
|
|
||||||
: i18next.t("pokemonEvolutions:Forms.moveForgotten", {
|
|
||||||
move: i18next.t(`move:${moveKey}.name`),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return !!pokemon.moveset.filter(m => m.moveId === this.move).length === this.known;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class SpeciesFormChangeMoveTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
public movePredicate: (m: MoveId) => boolean;
|
|
||||||
public used: boolean;
|
|
||||||
|
|
||||||
constructor(move: MoveId | ((m: MoveId) => boolean), used = true) {
|
|
||||||
super();
|
|
||||||
this.movePredicate = typeof move === "function" ? move : (m: MoveId) => m === move;
|
|
||||||
this.used = used;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger {
|
|
||||||
description = i18next.t("pokemonEvolutions:Forms.preMove");
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
const command = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
|
|
||||||
return !!command?.move && this.movePredicate(command.move.move) === this.used;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesFormChangePostMoveTrigger extends SpeciesFormChangeMoveTrigger {
|
|
||||||
description = i18next.t("pokemonEvolutions:Forms.postMove");
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return (
|
|
||||||
pokemon.summonData && !!pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length === this.used
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MeloettaFormChangePostMoveTrigger extends SpeciesFormChangePostMoveTrigger {
|
|
||||||
override canChange(pokemon: Pokemon): boolean {
|
|
||||||
if (globalScene.gameMode.hasChallenge(Challenges.SINGLE_TYPE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Meloetta will not transform if it has the ability Sheer Force when using Relic Song
|
|
||||||
if (pokemon.hasAbility(AbilityId.SHEER_FORCE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return super.canChange(pokemon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
private formKey: string;
|
|
||||||
|
|
||||||
constructor(formKey: string) {
|
|
||||||
super();
|
|
||||||
this.formKey = formKey;
|
|
||||||
this.description = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
return (
|
|
||||||
this.formKey ===
|
|
||||||
pokemon.species.forms[globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)]
|
|
||||||
.formKey
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used for triggering form changes based on the user's Tera type.
|
|
||||||
* Used by Ogerpon and Terapagos.
|
|
||||||
* @extends SpeciesFormChangeTrigger
|
|
||||||
*/
|
|
||||||
export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
description = i18next.t("pokemonEvolutions:Forms.tera");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used for triggering form changes based on the user's lapsed Tera type.
|
|
||||||
* Used by Ogerpon and Terapagos.
|
|
||||||
* @extends SpeciesFormChangeTrigger
|
|
||||||
*/
|
|
||||||
export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
description = i18next.t("pokemonEvolutions:Forms.teraLapse");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used for triggering form changes based on weather.
|
|
||||||
* Used by Castform and Cherrim.
|
|
||||||
* @extends SpeciesFormChangeTrigger
|
|
||||||
*/
|
|
||||||
export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
/** The ability that triggers the form change */
|
|
||||||
public ability: AbilityId;
|
|
||||||
/** The list of weathers that trigger the form change */
|
|
||||||
public weathers: WeatherType[];
|
|
||||||
|
|
||||||
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
|
||||||
super();
|
|
||||||
this.ability = ability;
|
|
||||||
this.weathers = weathers;
|
|
||||||
this.description = i18next.t("pokemonEvolutions:Forms.weather");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the Pokemon has the required ability and is in the correct weather while
|
|
||||||
* the weather or ability is also not suppressed.
|
|
||||||
* @param {Pokemon} pokemon the pokemon that is trying to do the form change
|
|
||||||
* @returns `true` if the Pokemon can change forms, `false` otherwise
|
|
||||||
*/
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
|
|
||||||
const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
|
|
||||||
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
|
||||||
|
|
||||||
return (
|
|
||||||
!isAbilitySuppressed &&
|
|
||||||
!isWeatherSuppressed &&
|
|
||||||
pokemon.hasAbility(this.ability) &&
|
|
||||||
this.weathers.includes(currentWeather)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used for reverting to the original form when the weather runs out
|
|
||||||
* or when the user loses the ability/is suppressed.
|
|
||||||
* Used by Castform and Cherrim.
|
|
||||||
* @extends SpeciesFormChangeTrigger
|
|
||||||
*/
|
|
||||||
export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChangeTrigger {
|
|
||||||
/** The ability that triggers the form change*/
|
|
||||||
public ability: AbilityId;
|
|
||||||
/** The list of weathers that will also trigger a form change to original form */
|
|
||||||
public weathers: WeatherType[];
|
|
||||||
|
|
||||||
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
|
||||||
super();
|
|
||||||
this.ability = ability;
|
|
||||||
this.weathers = weathers;
|
|
||||||
this.description = i18next.t("pokemonEvolutions:Forms.weatherRevert");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the Pokemon has the required ability and the weather is one that will revert
|
|
||||||
* the Pokemon to its original form or the weather or ability is suppressed
|
|
||||||
* @param {Pokemon} pokemon the pokemon that is trying to do the form change
|
|
||||||
* @returns `true` if the Pokemon will revert to its original form, `false` otherwise
|
|
||||||
*/
|
|
||||||
canChange(pokemon: Pokemon): boolean {
|
|
||||||
if (pokemon.hasAbility(this.ability, false, true)) {
|
|
||||||
const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
|
|
||||||
const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
|
|
||||||
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
|
||||||
const summonDataAbility = pokemon.summonData.ability;
|
|
||||||
const isAbilityChanged = summonDataAbility !== this.ability && summonDataAbility !== AbilityId.NONE;
|
|
||||||
|
|
||||||
if (this.weathers.includes(currentWeather) || isWeatherSuppressed || isAbilitySuppressed || isAbilityChanged) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: SpeciesFormChange, preName: string): string {
|
|
||||||
const isMega = formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1;
|
|
||||||
const isGmax = formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1;
|
|
||||||
const isEmax = formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1;
|
|
||||||
const isRevert = !isMega && formChange.formKey === pokemon.species.forms[0].formKey;
|
|
||||||
if (isMega) {
|
|
||||||
return i18next.t("battlePokemonForm:megaChange", {
|
|
||||||
preName,
|
|
||||||
pokemonName: pokemon.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isGmax) {
|
|
||||||
return i18next.t("battlePokemonForm:gigantamaxChange", {
|
|
||||||
preName,
|
|
||||||
pokemonName: pokemon.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isEmax) {
|
|
||||||
return i18next.t("battlePokemonForm:eternamaxChange", {
|
|
||||||
preName,
|
|
||||||
pokemonName: pokemon.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isRevert) {
|
|
||||||
return i18next.t("battlePokemonForm:revertChange", {
|
|
||||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (pokemon.getAbility().id === AbilityId.DISGUISE) {
|
|
||||||
return i18next.t("battlePokemonForm:disguiseChange");
|
|
||||||
}
|
|
||||||
return i18next.t("battlePokemonForm:formChange", { preName });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives a condition for form changing checking if a species is registered as caught in the player's dex data.
|
* Gives a condition for form changing checking if a species is registered as caught in the player's dex data.
|
||||||
* Used for fusion forms such as Kyurem and Necrozma.
|
* Used for fusion forms such as Kyurem and Necrozma.
|
||||||
|
348
src/data/pokemon-forms/form-change-triggers.ts
Normal file
348
src/data/pokemon-forms/form-change-triggers.ts
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
import i18next from "i18next";
|
||||||
|
import type { Constructor } from "#app/utils/common";
|
||||||
|
import type { TimeOfDay } from "#enums/time-of-day";
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||||
|
import type { PokemonFormChangeItemModifier } from "#app/modifier/modifier";
|
||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
|
import { WeatherType } from "#enums/weather-type";
|
||||||
|
|
||||||
|
export abstract class SpeciesFormChangeTrigger {
|
||||||
|
public description = "";
|
||||||
|
|
||||||
|
canChange(_pokemon: Pokemon): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTriggerType(triggerType: Constructor<SpeciesFormChangeTrigger>): boolean {
|
||||||
|
return this instanceof triggerType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeManualTrigger extends SpeciesFormChangeTrigger {}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeAbilityTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public description: string = i18next.t("pokemonEvolutions:Forms.ability");
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeCompoundTrigger {
|
||||||
|
public description = "";
|
||||||
|
public triggers: SpeciesFormChangeTrigger[];
|
||||||
|
|
||||||
|
constructor(...triggers: SpeciesFormChangeTrigger[]) {
|
||||||
|
this.triggers = triggers;
|
||||||
|
this.description = this.triggers
|
||||||
|
.filter(trigger => trigger?.description?.length > 0)
|
||||||
|
.map(trigger => trigger.description)
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
for (const trigger of this.triggers) {
|
||||||
|
if (!trigger.canChange(pokemon)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTriggerType(triggerType: Constructor<SpeciesFormChangeTrigger>): boolean {
|
||||||
|
return !!this.triggers.find(t => t.hasTriggerType(triggerType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeItemTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public item: FormChangeItem;
|
||||||
|
public active: boolean;
|
||||||
|
|
||||||
|
constructor(item: FormChangeItem, active = true) {
|
||||||
|
super();
|
||||||
|
this.item = item;
|
||||||
|
this.active = active;
|
||||||
|
this.description = this.active
|
||||||
|
? i18next.t("pokemonEvolutions:Forms.item", {
|
||||||
|
item: i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.item]}`),
|
||||||
|
})
|
||||||
|
: i18next.t("pokemonEvolutions:Forms.deactivateItem", {
|
||||||
|
item: i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.item]}`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return !!globalScene.findModifier(r => {
|
||||||
|
// Assume that if m has the `formChangeItem` property, then it is a PokemonFormChangeItemModifier
|
||||||
|
const m = r as PokemonFormChangeItemModifier;
|
||||||
|
return (
|
||||||
|
"formChangeItem" in m &&
|
||||||
|
m.pokemonId === pokemon.id &&
|
||||||
|
m.formChangeItem === this.item &&
|
||||||
|
m.active === this.active
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeTimeOfDayTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public timesOfDay: TimeOfDay[];
|
||||||
|
|
||||||
|
constructor(...timesOfDay: TimeOfDay[]) {
|
||||||
|
super();
|
||||||
|
this.timesOfDay = timesOfDay;
|
||||||
|
this.description = i18next.t("pokemonEvolutions:Forms.timeOfDay");
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(_pokemon: Pokemon): boolean {
|
||||||
|
return this.timesOfDay.indexOf(globalScene.arena.getTimeOfDay()) > -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class SpeciesFormChangeActiveTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public active: boolean;
|
||||||
|
|
||||||
|
constructor(active = false) {
|
||||||
|
super();
|
||||||
|
this.active = active;
|
||||||
|
this.description = this.active
|
||||||
|
? i18next.t("pokemonEvolutions:Forms.enter")
|
||||||
|
: i18next.t("pokemonEvolutions:Forms.leave");
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return pokemon.isActive(true) === this.active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeStatusEffectTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public statusEffects: StatusEffect[];
|
||||||
|
public invert: boolean;
|
||||||
|
|
||||||
|
constructor(statusEffects: StatusEffect | StatusEffect[], invert = false) {
|
||||||
|
super();
|
||||||
|
if (!Array.isArray(statusEffects)) {
|
||||||
|
statusEffects = [statusEffects];
|
||||||
|
}
|
||||||
|
this.statusEffects = statusEffects;
|
||||||
|
this.invert = invert;
|
||||||
|
// this.description = i18next.t("pokemonEvolutions:Forms.statusEffect");
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return this.statusEffects.indexOf(pokemon.status?.effect || StatusEffect.NONE) > -1 !== this.invert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public move: MoveId;
|
||||||
|
public known: boolean;
|
||||||
|
|
||||||
|
constructor(move: MoveId, known = true) {
|
||||||
|
super();
|
||||||
|
this.move = move;
|
||||||
|
this.known = known;
|
||||||
|
const moveKey = MoveId[this.move]
|
||||||
|
.split("_")
|
||||||
|
.filter(f => f)
|
||||||
|
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
||||||
|
.join("") as unknown as string;
|
||||||
|
this.description = known
|
||||||
|
? i18next.t("pokemonEvolutions:Forms.moveLearned", {
|
||||||
|
move: i18next.t(`move:${moveKey}.name`),
|
||||||
|
})
|
||||||
|
: i18next.t("pokemonEvolutions:Forms.moveForgotten", {
|
||||||
|
move: i18next.t(`move:${moveKey}.name`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return !!pokemon.moveset.filter(m => m.moveId === this.move).length === this.known;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class SpeciesFormChangeMoveTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
public movePredicate: (m: MoveId) => boolean;
|
||||||
|
public used: boolean;
|
||||||
|
|
||||||
|
constructor(move: MoveId | ((m: MoveId) => boolean), used = true) {
|
||||||
|
super();
|
||||||
|
this.movePredicate = typeof move === "function" ? move : (m: MoveId) => m === move;
|
||||||
|
this.used = used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger {
|
||||||
|
description = i18next.t("pokemonEvolutions:Forms.preMove");
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
const command = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
|
||||||
|
return !!command?.move && this.movePredicate(command.move.move) === this.used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesFormChangePostMoveTrigger extends SpeciesFormChangeMoveTrigger {
|
||||||
|
description = i18next.t("pokemonEvolutions:Forms.postMove");
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return (
|
||||||
|
pokemon.summonData && !!pokemon.getLastXMoves(1).filter(m => this.movePredicate(m.move)).length === this.used
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MeloettaFormChangePostMoveTrigger extends SpeciesFormChangePostMoveTrigger {
|
||||||
|
override canChange(pokemon: Pokemon): boolean {
|
||||||
|
if (globalScene.gameMode.hasChallenge(Challenges.SINGLE_TYPE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Meloetta will not transform if it has the ability Sheer Force when using Relic Song
|
||||||
|
if (pokemon.hasAbility(AbilityId.SHEER_FORCE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.canChange(pokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpeciesDefaultFormMatchTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
private formKey: string;
|
||||||
|
|
||||||
|
constructor(formKey: string) {
|
||||||
|
super();
|
||||||
|
this.formKey = formKey;
|
||||||
|
this.description = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
return (
|
||||||
|
this.formKey ===
|
||||||
|
pokemon.species.forms[globalScene.getSpeciesFormIndex(pokemon.species, pokemon.gender, pokemon.getNature(), true)]
|
||||||
|
.formKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for triggering form changes based on the user's Tera type.
|
||||||
|
* Used by Ogerpon and Terapagos.
|
||||||
|
*/
|
||||||
|
export class SpeciesFormChangeTeraTrigger extends SpeciesFormChangeTrigger {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for triggering form changes based on the user's lapsed Tera type.
|
||||||
|
* Used by Ogerpon and Terapagos.
|
||||||
|
*/
|
||||||
|
export class SpeciesFormChangeLapseTeraTrigger extends SpeciesFormChangeTrigger {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for triggering form changes based on weather.
|
||||||
|
* Used by Castform and Cherrim.
|
||||||
|
*/
|
||||||
|
export class SpeciesFormChangeWeatherTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
/** The ability that triggers the form change */
|
||||||
|
public ability: AbilityId;
|
||||||
|
/** The list of weathers that trigger the form change */
|
||||||
|
public weathers: WeatherType[];
|
||||||
|
|
||||||
|
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
||||||
|
super();
|
||||||
|
this.ability = ability;
|
||||||
|
this.weathers = weathers;
|
||||||
|
this.description = i18next.t("pokemonEvolutions:Forms.weather");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the Pokemon has the required ability and is in the correct weather while
|
||||||
|
* the weather or ability is also not suppressed.
|
||||||
|
* @param pokemon - The pokemon that is trying to do the form change
|
||||||
|
* @returns `true` if the Pokemon can change forms, `false` otherwise
|
||||||
|
*/
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
|
||||||
|
const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
|
||||||
|
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
||||||
|
|
||||||
|
return (
|
||||||
|
!isAbilitySuppressed &&
|
||||||
|
!isWeatherSuppressed &&
|
||||||
|
pokemon.hasAbility(this.ability) &&
|
||||||
|
this.weathers.includes(currentWeather)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for reverting to the original form when the weather runs out
|
||||||
|
* or when the user loses the ability/is suppressed.
|
||||||
|
* Used by Castform and Cherrim.
|
||||||
|
*/
|
||||||
|
export class SpeciesFormChangeRevertWeatherFormTrigger extends SpeciesFormChangeTrigger {
|
||||||
|
/** The ability that triggers the form change*/
|
||||||
|
public ability: AbilityId;
|
||||||
|
/** The list of weathers that will also trigger a form change to original form */
|
||||||
|
public weathers: WeatherType[];
|
||||||
|
|
||||||
|
constructor(ability: AbilityId, weathers: WeatherType[]) {
|
||||||
|
super();
|
||||||
|
this.ability = ability;
|
||||||
|
this.weathers = weathers;
|
||||||
|
this.description = i18next.t("pokemonEvolutions:Forms.weatherRevert");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the Pokemon has the required ability and the weather is one that will revert
|
||||||
|
* the Pokemon to its original form or the weather or ability is suppressed
|
||||||
|
* @param {Pokemon} pokemon the pokemon that is trying to do the form change
|
||||||
|
* @returns `true` if the Pokemon will revert to its original form, `false` otherwise
|
||||||
|
*/
|
||||||
|
canChange(pokemon: Pokemon): boolean {
|
||||||
|
if (pokemon.hasAbility(this.ability, false, true)) {
|
||||||
|
const currentWeather = globalScene.arena.weather?.weatherType ?? WeatherType.NONE;
|
||||||
|
const isWeatherSuppressed = globalScene.arena.weather?.isEffectSuppressed();
|
||||||
|
const isAbilitySuppressed = pokemon.summonData.abilitySuppressed;
|
||||||
|
const summonDataAbility = pokemon.summonData.ability;
|
||||||
|
const isAbilityChanged = summonDataAbility !== this.ability && summonDataAbility !== AbilityId.NONE;
|
||||||
|
|
||||||
|
if (this.weathers.includes(currentWeather) || isWeatherSuppressed || isAbilitySuppressed || isAbilityChanged) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: SpeciesFormChange, preName: string): string {
|
||||||
|
const isMega = formChange.formKey.indexOf(SpeciesFormKey.MEGA) > -1;
|
||||||
|
const isGmax = formChange.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1;
|
||||||
|
const isEmax = formChange.formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1;
|
||||||
|
const isRevert = !isMega && formChange.formKey === pokemon.species.forms[0].formKey;
|
||||||
|
if (isMega) {
|
||||||
|
return i18next.t("battlePokemonForm:megaChange", {
|
||||||
|
preName,
|
||||||
|
pokemonName: pokemon.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isGmax) {
|
||||||
|
return i18next.t("battlePokemonForm:gigantamaxChange", {
|
||||||
|
preName,
|
||||||
|
pokemonName: pokemon.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isEmax) {
|
||||||
|
return i18next.t("battlePokemonForm:eternamaxChange", {
|
||||||
|
preName,
|
||||||
|
pokemonName: pokemon.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isRevert) {
|
||||||
|
return i18next.t("battlePokemonForm:revertChange", {
|
||||||
|
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (pokemon.getAbility().id === AbilityId.DISGUISE) {
|
||||||
|
return i18next.t("battlePokemonForm:disguiseChange");
|
||||||
|
}
|
||||||
|
return i18next.t("battlePokemonForm:formChange", { preName });
|
||||||
|
}
|
@ -7,7 +7,8 @@ import i18next from "i18next";
|
|||||||
import type { AnySound } from "#app/battle-scene";
|
import type { AnySound } from "#app/battle-scene";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { GameMode } from "#app/game-mode";
|
import type { GameMode } from "#app/game-mode";
|
||||||
import { DexAttr, type StarterMoveset } from "#app/system/game-data";
|
import type { StarterMoveset } from "#app/system/game-data";
|
||||||
|
import { DexAttr } from "#enums/dex-attr";
|
||||||
import {
|
import {
|
||||||
isNullOrUndefined,
|
isNullOrUndefined,
|
||||||
capitalizeString,
|
capitalizeString,
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import type Move from "./moves/move";
|
import type Move from "./moves/move";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { ProtectAttr } from "./moves/move";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export enum TerrainType {
|
export enum TerrainType {
|
||||||
@ -55,7 +54,7 @@ export class Terrain {
|
|||||||
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
|
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
|
||||||
switch (this.terrainType) {
|
switch (this.terrainType) {
|
||||||
case TerrainType.PSYCHIC:
|
case TerrainType.PSYCHIC:
|
||||||
if (!move.hasAttr(ProtectAttr)) {
|
if (!move.hasAttr("ProtectAttr")) {
|
||||||
// Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain
|
// Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain
|
||||||
return (
|
return (
|
||||||
move.getPriority(user) > 0 &&
|
move.getPriority(user) > 0 &&
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { startingWave } from "#app/starting-wave";
|
import { startingWave } from "#app/starting-wave";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import { GameModes } from "#app/game-mode";
|
import { GameModes } from "#enums/game-modes";
|
||||||
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
|
||||||
|
|
||||||
export class TrainerPartyTemplate {
|
export class TrainerPartyTemplate {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "../moves/pokemon-move";
|
||||||
import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt, randSeedIntRange } from "#app/utils/common";
|
import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt, randSeedIntRange } from "#app/utils/common";
|
||||||
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { tmSpecies } from "#app/data/balance/tms";
|
import { tmSpecies } from "#app/data/balance/tms";
|
||||||
import { doubleBattleDialogue } from "#app/data/dialogue";
|
import { doubleBattleDialogue } from "#app/data/dialogue";
|
||||||
import { TrainerVariant } from "#app/field/trainer";
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
import { getIsInitialized, initI18n } from "#app/plugins/i18n";
|
import { getIsInitialized, initI18n } from "#app/plugins/i18n";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
|
@ -4,7 +4,6 @@ import { getPokemonNameWithAffix } from "../messages";
|
|||||||
import type Pokemon from "../field/pokemon";
|
import type Pokemon from "../field/pokemon";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import type Move from "./moves/move";
|
import type Move from "./moves/move";
|
||||||
import { AttackMove } from "./moves/move";
|
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
import { SuppressWeatherEffectAbAttr } from "./abilities/ability";
|
import { SuppressWeatherEffectAbAttr } from "./abilities/ability";
|
||||||
import { TerrainType, getTerrainName } from "./terrain";
|
import { TerrainType, getTerrainName } from "./terrain";
|
||||||
@ -95,9 +94,9 @@ export class Weather {
|
|||||||
|
|
||||||
switch (this.weatherType) {
|
switch (this.weatherType) {
|
||||||
case WeatherType.HARSH_SUN:
|
case WeatherType.HARSH_SUN:
|
||||||
return move instanceof AttackMove && moveType === PokemonType.WATER;
|
return move.is("AttackMove") && moveType === PokemonType.WATER;
|
||||||
case WeatherType.HEAVY_RAIN:
|
case WeatherType.HEAVY_RAIN:
|
||||||
return move instanceof AttackMove && moveType === PokemonType.FIRE;
|
return move.is("AttackMove") && moveType === PokemonType.FIRE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
11
src/enums/ability-attr.ts
Normal file
11
src/enums/ability-attr.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Not to be confused with an Ability Attribute.
|
||||||
|
* This is an object literal storing the slot that an ability can occupy.
|
||||||
|
*/
|
||||||
|
export const AbilityAttr = Object.freeze({
|
||||||
|
ABILITY_1: 1,
|
||||||
|
ABILITY_2: 2,
|
||||||
|
ABILITY_HIDDEN: 4,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AbilityAttr = typeof AbilityAttr[keyof typeof AbilityAttr];
|
5
src/enums/ai-type.ts
Normal file
5
src/enums/ai-type.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum AiType {
|
||||||
|
RANDOM,
|
||||||
|
SMART_RANDOM,
|
||||||
|
SMART
|
||||||
|
}
|
5
src/enums/arena-tag-side.ts
Normal file
5
src/enums/arena-tag-side.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum ArenaTagSide {
|
||||||
|
BOTH,
|
||||||
|
PLAYER,
|
||||||
|
ENEMY
|
||||||
|
}
|
7
src/enums/battler-index.ts
Normal file
7
src/enums/battler-index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export enum BattlerIndex {
|
||||||
|
ATTACKER = -1,
|
||||||
|
PLAYER,
|
||||||
|
PLAYER_2,
|
||||||
|
ENEMY,
|
||||||
|
ENEMY_2
|
||||||
|
}
|
12
src/enums/battler-tag-lapse-type.ts
Normal file
12
src/enums/battler-tag-lapse-type.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export enum BattlerTagLapseType {
|
||||||
|
FAINT,
|
||||||
|
MOVE,
|
||||||
|
PRE_MOVE,
|
||||||
|
AFTER_MOVE,
|
||||||
|
MOVE_EFFECT,
|
||||||
|
TURN_END,
|
||||||
|
HIT,
|
||||||
|
/** Tag lapses AFTER_HIT, applying its effects even if the user faints */
|
||||||
|
AFTER_HIT,
|
||||||
|
CUSTOM
|
||||||
|
}
|
69
src/enums/challenge-type.ts
Normal file
69
src/enums/challenge-type.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
7
src/enums/command.ts
Normal file
7
src/enums/command.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export enum Command {
|
||||||
|
FIGHT = 0,
|
||||||
|
BALL,
|
||||||
|
POKEMON,
|
||||||
|
RUN,
|
||||||
|
TERA
|
||||||
|
}
|
11
src/enums/dex-attr.ts
Normal file
11
src/enums/dex-attr.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const DexAttr = Object.freeze({
|
||||||
|
NON_SHINY: 1n,
|
||||||
|
SHINY: 2n,
|
||||||
|
MALE: 4n,
|
||||||
|
FEMALE: 8n,
|
||||||
|
DEFAULT_VARIANT: 16n,
|
||||||
|
VARIANT_2: 32n,
|
||||||
|
VARIANT_3: 64n,
|
||||||
|
DEFAULT_FORM: 128n,
|
||||||
|
});
|
||||||
|
export type DexAttr = typeof DexAttr[keyof typeof DexAttr];
|
5
src/enums/field-position.ts
Normal file
5
src/enums/field-position.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum FieldPosition {
|
||||||
|
CENTER,
|
||||||
|
LEFT,
|
||||||
|
RIGHT
|
||||||
|
}
|
119
src/enums/form-change-item.ts
Normal file
119
src/enums/form-change-item.ts
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
export enum FormChangeItem {
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
ABOMASITE,
|
||||||
|
ABSOLITE,
|
||||||
|
AERODACTYLITE,
|
||||||
|
AGGRONITE,
|
||||||
|
ALAKAZITE,
|
||||||
|
ALTARIANITE,
|
||||||
|
AMPHAROSITE,
|
||||||
|
AUDINITE,
|
||||||
|
BANETTITE,
|
||||||
|
BEEDRILLITE,
|
||||||
|
BLASTOISINITE,
|
||||||
|
BLAZIKENITE,
|
||||||
|
CAMERUPTITE,
|
||||||
|
CHARIZARDITE_X,
|
||||||
|
CHARIZARDITE_Y,
|
||||||
|
DIANCITE,
|
||||||
|
GALLADITE,
|
||||||
|
GARCHOMPITE,
|
||||||
|
GARDEVOIRITE,
|
||||||
|
GENGARITE,
|
||||||
|
GLALITITE,
|
||||||
|
GYARADOSITE,
|
||||||
|
HERACRONITE,
|
||||||
|
HOUNDOOMINITE,
|
||||||
|
KANGASKHANITE,
|
||||||
|
LATIASITE,
|
||||||
|
LATIOSITE,
|
||||||
|
LOPUNNITE,
|
||||||
|
LUCARIONITE,
|
||||||
|
MANECTITE,
|
||||||
|
MAWILITE,
|
||||||
|
MEDICHAMITE,
|
||||||
|
METAGROSSITE,
|
||||||
|
MEWTWONITE_X,
|
||||||
|
MEWTWONITE_Y,
|
||||||
|
PIDGEOTITE,
|
||||||
|
PINSIRITE,
|
||||||
|
RAYQUAZITE,
|
||||||
|
SABLENITE,
|
||||||
|
SALAMENCITE,
|
||||||
|
SCEPTILITE,
|
||||||
|
SCIZORITE,
|
||||||
|
SHARPEDONITE,
|
||||||
|
SLOWBRONITE,
|
||||||
|
STEELIXITE,
|
||||||
|
SWAMPERTITE,
|
||||||
|
TYRANITARITE,
|
||||||
|
VENUSAURITE,
|
||||||
|
|
||||||
|
BLUE_ORB = 50,
|
||||||
|
RED_ORB,
|
||||||
|
ADAMANT_CRYSTAL,
|
||||||
|
LUSTROUS_GLOBE,
|
||||||
|
GRISEOUS_CORE,
|
||||||
|
REVEAL_GLASS,
|
||||||
|
MAX_MUSHROOMS,
|
||||||
|
DARK_STONE,
|
||||||
|
LIGHT_STONE,
|
||||||
|
PRISON_BOTTLE,
|
||||||
|
RUSTED_SWORD,
|
||||||
|
RUSTED_SHIELD,
|
||||||
|
ICY_REINS_OF_UNITY,
|
||||||
|
SHADOW_REINS_OF_UNITY,
|
||||||
|
ULTRANECROZIUM_Z,
|
||||||
|
|
||||||
|
SHARP_METEORITE = 100,
|
||||||
|
HARD_METEORITE,
|
||||||
|
SMOOTH_METEORITE,
|
||||||
|
GRACIDEA,
|
||||||
|
SHOCK_DRIVE,
|
||||||
|
BURN_DRIVE,
|
||||||
|
CHILL_DRIVE,
|
||||||
|
DOUSE_DRIVE,
|
||||||
|
N_SOLARIZER,
|
||||||
|
N_LUNARIZER,
|
||||||
|
WELLSPRING_MASK,
|
||||||
|
HEARTHFLAME_MASK,
|
||||||
|
CORNERSTONE_MASK,
|
||||||
|
FIST_PLATE,
|
||||||
|
SKY_PLATE,
|
||||||
|
TOXIC_PLATE,
|
||||||
|
EARTH_PLATE,
|
||||||
|
STONE_PLATE,
|
||||||
|
INSECT_PLATE,
|
||||||
|
SPOOKY_PLATE,
|
||||||
|
IRON_PLATE,
|
||||||
|
FLAME_PLATE,
|
||||||
|
SPLASH_PLATE,
|
||||||
|
MEADOW_PLATE,
|
||||||
|
ZAP_PLATE,
|
||||||
|
MIND_PLATE,
|
||||||
|
ICICLE_PLATE,
|
||||||
|
DRACO_PLATE,
|
||||||
|
DREAD_PLATE,
|
||||||
|
PIXIE_PLATE,
|
||||||
|
BLANK_PLATE,// TODO: Find a potential use for this
|
||||||
|
LEGEND_PLATE,// TODO: Find a potential use for this
|
||||||
|
FIGHTING_MEMORY,
|
||||||
|
FLYING_MEMORY,
|
||||||
|
POISON_MEMORY,
|
||||||
|
GROUND_MEMORY,
|
||||||
|
ROCK_MEMORY,
|
||||||
|
BUG_MEMORY,
|
||||||
|
GHOST_MEMORY,
|
||||||
|
STEEL_MEMORY,
|
||||||
|
FIRE_MEMORY,
|
||||||
|
WATER_MEMORY,
|
||||||
|
GRASS_MEMORY,
|
||||||
|
ELECTRIC_MEMORY,
|
||||||
|
PSYCHIC_MEMORY,
|
||||||
|
ICE_MEMORY,
|
||||||
|
DRAGON_MEMORY,
|
||||||
|
DARK_MEMORY,
|
||||||
|
FAIRY_MEMORY,
|
||||||
|
NORMAL_MEMORY
|
||||||
|
}
|
7
src/enums/game-modes.ts
Normal file
7
src/enums/game-modes.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export enum GameModes {
|
||||||
|
CLASSIC,
|
||||||
|
ENDLESS,
|
||||||
|
SPLICED_ENDLESS,
|
||||||
|
DAILY,
|
||||||
|
CHALLENGE
|
||||||
|
}
|
15
src/enums/hit-result.ts
Normal file
15
src/enums/hit-result.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export enum HitResult {
|
||||||
|
EFFECTIVE = 1,
|
||||||
|
SUPER_EFFECTIVE,
|
||||||
|
NOT_VERY_EFFECTIVE,
|
||||||
|
ONE_HIT_KO,
|
||||||
|
NO_EFFECT,
|
||||||
|
STATUS,
|
||||||
|
HEAL,
|
||||||
|
FAIL,
|
||||||
|
MISS,
|
||||||
|
INDIRECT,
|
||||||
|
IMMUNE,
|
||||||
|
CONFUSION,
|
||||||
|
INDIRECT_KO
|
||||||
|
}
|
8
src/enums/learn-move-situation.ts
Normal file
8
src/enums/learn-move-situation.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export enum LearnMoveSituation {
|
||||||
|
MISC,
|
||||||
|
LEVEL_UP,
|
||||||
|
RELEARN,
|
||||||
|
EVOLUTION,
|
||||||
|
EVOLUTION_FUSED,// If fusionSpecies has Evolved
|
||||||
|
EVOLUTION_FUSED_BASE
|
||||||
|
}
|
8
src/enums/learn-move-type.ts
Normal file
8
src/enums/learn-move-type.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export enum LearnMoveType {
|
||||||
|
/** For learning a move via level-up, evolution, or other non-item-based event */
|
||||||
|
LEARN_MOVE,
|
||||||
|
/** For learning a move via Memory Mushroom */
|
||||||
|
MEMORY,
|
||||||
|
/** For learning a move via TM */
|
||||||
|
TM
|
||||||
|
}
|
95
src/enums/move-anims-common.ts
Normal file
95
src/enums/move-anims-common.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
export enum AnimFrameTarget {
|
||||||
|
USER,
|
||||||
|
TARGET,
|
||||||
|
GRAPHIC
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AnimFocus {
|
||||||
|
TARGET = 1,
|
||||||
|
USER,
|
||||||
|
USER_TARGET,
|
||||||
|
SCREEN
|
||||||
|
}
|
||||||
|
|
||||||
|
export 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
|
||||||
|
}
|
7
src/enums/move-result.ts
Normal file
7
src/enums/move-result.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export enum MoveResult {
|
||||||
|
PENDING,
|
||||||
|
SUCCESS,
|
||||||
|
FAIL,
|
||||||
|
MISS,
|
||||||
|
OTHER
|
||||||
|
}
|
12
src/enums/move-source-type.ts
Normal file
12
src/enums/move-source-type.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
5
src/enums/trainer-variant.ts
Normal file
5
src/enums/trainer-variant.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum TrainerVariant {
|
||||||
|
DEFAULT,
|
||||||
|
FEMALE,
|
||||||
|
DOUBLE
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import type { ArenaTagSide } from "#app/data/arena-tag";
|
import type { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import type { ArenaTagType } from "#enums/arena-tag-type";
|
import type { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import type { TerrainType } from "#app/data/terrain";
|
import type { TerrainType } from "#app/data/terrain";
|
||||||
import type { WeatherType } from "#enums/weather-type";
|
import type { WeatherType } from "#enums/weather-type";
|
||||||
|
@ -12,12 +12,13 @@ import {
|
|||||||
getLegendaryWeatherContinuesMessage,
|
getLegendaryWeatherContinuesMessage,
|
||||||
Weather,
|
Weather,
|
||||||
} from "#app/data/weather";
|
} from "#app/data/weather";
|
||||||
import { CommonAnim } from "#app/data/battle-anims";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import type { PokemonType } from "#enums/pokemon-type";
|
import type { PokemonType } from "#enums/pokemon-type";
|
||||||
import type Move from "#app/data/moves/move";
|
import type Move from "#app/data/moves/move";
|
||||||
import type { ArenaTag } from "#app/data/arena-tag";
|
import type { ArenaTag } from "#app/data/arena-tag";
|
||||||
import { ArenaTagSide, ArenaTrapTag, getArenaTag } from "#app/data/arena-tag";
|
import { ArenaTrapTag, getArenaTag } from "#app/data/arena-tag";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { Terrain, TerrainType } from "#app/data/terrain";
|
import { Terrain, TerrainType } from "#app/data/terrain";
|
||||||
import {
|
import {
|
||||||
applyAbAttrs,
|
applyAbAttrs,
|
||||||
@ -37,7 +38,10 @@ import { SpeciesId } from "#enums/species-id";
|
|||||||
import { TimeOfDay } from "#enums/time-of-day";
|
import { TimeOfDay } from "#enums/time-of-day";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms";
|
import {
|
||||||
|
SpeciesFormChangeRevertWeatherFormTrigger,
|
||||||
|
SpeciesFormChangeWeatherTrigger,
|
||||||
|
} from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { FieldEffectModifier } from "#app/modifier/modifier";
|
import { FieldEffectModifier } from "#app/modifier/modifier";
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { TextStyle, addTextObject } from "../ui/text";
|
import { TextStyle, addTextObject } from "../ui/text";
|
||||||
import type { DamageResult } from "./pokemon";
|
import type { DamageResult } from "./pokemon";
|
||||||
import type Pokemon from "./pokemon";
|
import type Pokemon from "./pokemon";
|
||||||
import { HitResult } from "./pokemon";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { formatStat, fixedInt } from "#app/utils/common";
|
import { formatStat, fixedInt } from "#app/utils/common";
|
||||||
import type { BattlerIndex } from "../battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
|
||||||
type TextAndShadowArr = [string | null, string | null];
|
type TextAndShadowArr = [string | null, string | null];
|
||||||
|
@ -9,36 +9,8 @@ import BattleInfo from "#app/ui/battle-info/battle-info";
|
|||||||
import { EnemyBattleInfo } from "#app/ui/battle-info/enemy-battle-info";
|
import { EnemyBattleInfo } from "#app/ui/battle-info/enemy-battle-info";
|
||||||
import { PlayerBattleInfo } from "#app/ui/battle-info/player-battle-info";
|
import { PlayerBattleInfo } from "#app/ui/battle-info/player-battle-info";
|
||||||
import type Move from "#app/data/moves/move";
|
import type Move from "#app/data/moves/move";
|
||||||
import {
|
import { getMoveTargets } from "#app/data/moves/move-utils";
|
||||||
HighCritAttr,
|
import { applyMoveAttrs } from "#app/data/moves/apply-attrs";
|
||||||
HitsTagAttr,
|
|
||||||
applyMoveAttrs,
|
|
||||||
FixedDamageAttr,
|
|
||||||
VariableAtkAttr,
|
|
||||||
TypelessAttr,
|
|
||||||
CritOnlyAttr,
|
|
||||||
getMoveTargets,
|
|
||||||
OneHitKOAttr,
|
|
||||||
VariableMoveTypeAttr,
|
|
||||||
VariableDefAttr,
|
|
||||||
AttackMove,
|
|
||||||
ModifiedDamageAttr,
|
|
||||||
VariableMoveTypeMultiplierAttr,
|
|
||||||
IgnoreOpponentStatStagesAttr,
|
|
||||||
SacrificialAttr,
|
|
||||||
VariableMoveCategoryAttr,
|
|
||||||
CounterDamageAttr,
|
|
||||||
StatStageChangeAttr,
|
|
||||||
RechargeAttr,
|
|
||||||
IgnoreWeatherTypeDebuffAttr,
|
|
||||||
BypassBurnDamageReductionAttr,
|
|
||||||
SacrificialAttrOnHit,
|
|
||||||
OneHitKOAccuracyAttr,
|
|
||||||
RespectAttackTypeImmunityAttr,
|
|
||||||
CombinedPledgeStabBoostAttr,
|
|
||||||
VariableMoveTypeChartAttr,
|
|
||||||
HpSplitAttr,
|
|
||||||
} from "#app/data/moves/move";
|
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { MoveTarget } from "#enums/MoveTarget";
|
import { MoveTarget } from "#enums/MoveTarget";
|
||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
@ -116,7 +88,6 @@ import {
|
|||||||
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/tms";
|
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/tms";
|
||||||
import {
|
import {
|
||||||
BattlerTag,
|
BattlerTag,
|
||||||
BattlerTagLapseType,
|
|
||||||
EncoreTag,
|
EncoreTag,
|
||||||
GroundedTag,
|
GroundedTag,
|
||||||
HighestStatBoostTag,
|
HighestStatBoostTag,
|
||||||
@ -135,8 +106,10 @@ import {
|
|||||||
loadBattlerTag,
|
loadBattlerTag,
|
||||||
type GrudgeTag,
|
type GrudgeTag,
|
||||||
} from "../data/battler-tags";
|
} from "../data/battler-tags";
|
||||||
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
import { NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import type { SuppressAbilitiesTag } from "#app/data/arena-tag";
|
import type { SuppressAbilitiesTag } from "#app/data/arena-tag";
|
||||||
import type { Ability } from "#app/data/abilities/ability-class";
|
import type { Ability } from "#app/data/abilities/ability-class";
|
||||||
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr";
|
||||||
@ -195,7 +168,7 @@ import {
|
|||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
import { allAbilities } from "#app/data/data-lists";
|
import { allAbilities } from "#app/data/data-lists";
|
||||||
import type PokemonData from "#app/system/pokemon-data";
|
import type PokemonData from "#app/system/pokemon-data";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import type { PartyOption } from "#app/ui/party-ui-handler";
|
import type { PartyOption } from "#app/ui/party-ui-handler";
|
||||||
import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler";
|
import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
@ -204,7 +177,7 @@ import type { LevelMoves } from "#app/data/balance/pokemon-level-moves";
|
|||||||
import { EVOLVE_MOVE, RELEARN_MOVE } from "#app/data/balance/pokemon-level-moves";
|
import { EVOLVE_MOVE, RELEARN_MOVE } from "#app/data/balance/pokemon-level-moves";
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
import type { StarterDataEntry, StarterMoveset } from "#app/system/game-data";
|
import type { StarterDataEntry, StarterMoveset } from "#app/system/game-data";
|
||||||
import { DexAttr } from "#app/system/game-data";
|
import { DexAttr } from "#enums/dex-attr";
|
||||||
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities";
|
import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities";
|
||||||
import { getNatureStatMultiplier } from "#app/data/nature";
|
import { getNatureStatMultiplier } from "#app/data/nature";
|
||||||
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||||
@ -213,14 +186,15 @@ import {
|
|||||||
SpeciesFormChangeLapseTeraTrigger,
|
SpeciesFormChangeLapseTeraTrigger,
|
||||||
SpeciesFormChangeMoveLearnedTrigger,
|
SpeciesFormChangeMoveLearnedTrigger,
|
||||||
SpeciesFormChangePostMoveTrigger,
|
SpeciesFormChangePostMoveTrigger,
|
||||||
} from "#app/data/pokemon-forms";
|
} from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { TerrainType } from "#app/data/terrain";
|
import { TerrainType } from "#app/data/terrain";
|
||||||
import type { TrainerSlot } from "#enums/trainer-slot";
|
import type { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
import { speciesEggMoves } from "#app/data/balance/egg-moves";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import { applyChallenges, ChallengeType } from "#app/data/challenge";
|
import { applyChallenges } from "#app/data/challenge";
|
||||||
|
import { ChallengeType } from "#enums/challenge-type";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { BattleSpec } from "#enums/battle-spec";
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
@ -249,21 +223,12 @@ import { doShinySparkleAnim } from "#app/field/anims";
|
|||||||
import { MoveFlags } from "#enums/MoveFlags";
|
import { MoveFlags } from "#enums/MoveFlags";
|
||||||
import { timedEventManager } from "#app/global-event-manager";
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader";
|
import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader";
|
||||||
|
import { FieldPosition } from "#enums/field-position";
|
||||||
export enum LearnMoveSituation {
|
import { LearnMoveSituation } from "#enums/learn-move-situation";
|
||||||
MISC,
|
import { HitResult } from "#enums/hit-result";
|
||||||
LEVEL_UP,
|
import { AiType } from "#enums/ai-type";
|
||||||
RELEARN,
|
import type { MoveResult } from "#enums/move-result";
|
||||||
EVOLUTION,
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
EVOLUTION_FUSED, // If fusionSpecies has Evolved
|
|
||||||
EVOLUTION_FUSED_BASE, // If fusion's base species has Evolved
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum FieldPosition {
|
|
||||||
CENTER,
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Base typeclass for damage parameter methods, used for DRY */
|
/** Base typeclass for damage parameter methods, used for DRY */
|
||||||
type damageParams = {
|
type damageParams = {
|
||||||
@ -1435,7 +1400,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
*/
|
*/
|
||||||
getCritStage(source: Pokemon, move: Move): number {
|
getCritStage(source: Pokemon, move: Move): number {
|
||||||
const critStage = new NumberHolder(0);
|
const critStage = new NumberHolder(0);
|
||||||
applyMoveAttrs(HighCritAttr, source, this, move, critStage);
|
applyMoveAttrs("HighCritAttr", source, this, move, critStage);
|
||||||
globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
|
globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
|
||||||
globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
|
globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
|
||||||
applyAbAttrs(BonusCritAbAttr, source, null, false, critStage);
|
applyAbAttrs(BonusCritAbAttr, source, null, false, critStage);
|
||||||
@ -1461,7 +1426,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
*/
|
*/
|
||||||
getMoveCategory(target: Pokemon, move: Move): MoveCategory {
|
getMoveCategory(target: Pokemon, move: Move): MoveCategory {
|
||||||
const moveCategory = new NumberHolder(move.category);
|
const moveCategory = new NumberHolder(move.category);
|
||||||
applyMoveAttrs(VariableMoveCategoryAttr, this, target, move, moveCategory);
|
applyMoveAttrs("VariableMoveCategoryAttr", this, target, move, moveCategory);
|
||||||
return moveCategory.value;
|
return moveCategory.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2356,7 +2321,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
public getMoveType(move: Move, simulated = true): PokemonType {
|
public getMoveType(move: Move, simulated = true): PokemonType {
|
||||||
const moveTypeHolder = new NumberHolder(move.type);
|
const moveTypeHolder = new NumberHolder(move.type);
|
||||||
|
|
||||||
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
|
applyMoveAttrs("VariableMoveTypeAttr", this, null, move, moveTypeHolder);
|
||||||
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder);
|
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder);
|
||||||
|
|
||||||
// If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type,
|
// If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type,
|
||||||
@ -2400,18 +2365,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return this.turnData?.moveEffectiveness;
|
return this.turnData?.moveEffectiveness;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (move.hasAttr(TypelessAttr)) {
|
if (move.hasAttr("TypelessAttr")) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
const moveType = source.getMoveType(move);
|
const moveType = source.getMoveType(move);
|
||||||
|
|
||||||
const typeMultiplier = new NumberHolder(
|
const typeMultiplier = new NumberHolder(
|
||||||
move.category !== MoveCategory.STATUS || move.hasAttr(RespectAttackTypeImmunityAttr)
|
move.category !== MoveCategory.STATUS || move.hasAttr("RespectAttackTypeImmunityAttr")
|
||||||
? this.getAttackTypeEffectiveness(moveType, source, false, simulated, move, useIllusion)
|
? this.getAttackTypeEffectiveness(moveType, source, false, simulated, move, useIllusion)
|
||||||
: 1,
|
: 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
|
applyMoveAttrs("VariableMoveTypeMultiplierAttr", source, this, move, typeMultiplier);
|
||||||
if (this.getTypes(true, true).find(t => move.isTypeImmune(source, this, t))) {
|
if (this.getTypes(true, true).find(t => move.isTypeImmune(source, this, t))) {
|
||||||
typeMultiplier.value = 0;
|
typeMultiplier.value = 0;
|
||||||
}
|
}
|
||||||
@ -2438,7 +2403,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
const immuneTags = this.findTags(tag => tag instanceof TypeImmuneTag && tag.immuneType === moveType);
|
const immuneTags = this.findTags(tag => tag instanceof TypeImmuneTag && tag.immuneType === moveType);
|
||||||
for (const tag of immuneTags) {
|
for (const tag of immuneTags) {
|
||||||
if (move && !move.getAttrs(HitsTagAttr).some(attr => attr.tagType === tag.tagType)) {
|
if (move && !move.getAttrs("HitsTagAttr").some(attr => attr.tagType === tag.tagType)) {
|
||||||
typeMultiplier.value = 0;
|
typeMultiplier.value = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2494,7 +2459,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defType));
|
const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defType));
|
||||||
applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
||||||
if (move) {
|
if (move) {
|
||||||
applyMoveAttrs(VariableMoveTypeChartAttr, null, this, move, multiplier, defType);
|
applyMoveAttrs("VariableMoveTypeChartAttr", null, this, move, multiplier, defType);
|
||||||
}
|
}
|
||||||
if (source) {
|
if (source) {
|
||||||
const ignoreImmunity = new BooleanHolder(false);
|
const ignoreImmunity = new BooleanHolder(false);
|
||||||
@ -3130,24 +3095,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
// Bosses never get self ko moves or Pain Split
|
// Bosses never get self ko moves or Pain Split
|
||||||
if (this.isBoss()) {
|
if (this.isBoss()) {
|
||||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr) && !allMoves[m[0]].hasAttr(HpSplitAttr));
|
movePool = movePool.filter(
|
||||||
|
m => !allMoves[m[0]].hasAttr("SacrificialAttr") && !allMoves[m[0]].hasAttr("HpSplitAttr"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// No one gets Memento or Final Gambit
|
// No one gets Memento or Final Gambit
|
||||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit));
|
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr("SacrificialAttrOnHit"));
|
||||||
if (this.hasTrainer()) {
|
if (this.hasTrainer()) {
|
||||||
// Trainers never get OHKO moves
|
// Trainers never get OHKO moves
|
||||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(OneHitKOAttr));
|
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr("OneHitKOAttr"));
|
||||||
// Half the weight of self KO moves
|
// Half the weight of self KO moves
|
||||||
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].hasAttr(SacrificialAttr) ? 0.5 : 1)]);
|
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].hasAttr("SacrificialAttr") ? 0.5 : 1)]);
|
||||||
// Trainers get a weight bump to stat buffing moves
|
// Trainers get a weight bump to stat buffing moves
|
||||||
movePool = movePool.map(m => [
|
movePool = movePool.map(m => [
|
||||||
m[0],
|
m[0],
|
||||||
m[1] * (allMoves[m[0]].getAttrs(StatStageChangeAttr).some(a => a.stages > 1 && a.selfTarget) ? 1.25 : 1),
|
m[1] * (allMoves[m[0]].getAttrs("StatStageChangeAttr").some(a => a.stages > 1 && a.selfTarget) ? 1.25 : 1),
|
||||||
]);
|
]);
|
||||||
// Trainers get a weight decrease to multiturn moves
|
// Trainers get a weight decrease to multiturn moves
|
||||||
movePool = movePool.map(m => [
|
movePool = movePool.map(m => [
|
||||||
m[0],
|
m[0],
|
||||||
m[1] * (!!allMoves[m[0]].isChargingMove() || !!allMoves[m[0]].hasAttr(RechargeAttr) ? 0.7 : 1),
|
m[1] * (!!allMoves[m[0]].isChargingMove() || !!allMoves[m[0]].hasAttr("RechargeAttr") ? 0.7 : 1),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3214,7 +3181,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
!this.moveset.some(
|
!this.moveset.some(
|
||||||
mo =>
|
mo =>
|
||||||
m[0] === mo.moveId ||
|
m[0] === mo.moveId ||
|
||||||
(allMoves[m[0]].hasAttr(SacrificialAttr) && mo.getMove().hasAttr(SacrificialAttr)), // Only one self-KO move allowed
|
(allMoves[m[0]].hasAttr("SacrificialAttr") && mo.getMove().hasAttr("SacrificialAttr")), // Only one self-KO move allowed
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.map(m => {
|
.map(m => {
|
||||||
@ -3243,7 +3210,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
!this.moveset.some(
|
!this.moveset.some(
|
||||||
mo =>
|
mo =>
|
||||||
m[0] === mo.moveId ||
|
m[0] === mo.moveId ||
|
||||||
(allMoves[m[0]].hasAttr(SacrificialAttr) && mo.getMove().hasAttr(SacrificialAttr)), // Only one self-KO move allowed
|
(allMoves[m[0]].hasAttr("SacrificialAttr") && mo.getMove().hasAttr("SacrificialAttr")), // Only one self-KO move allowed
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -3451,7 +3418,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, simulated, stat, ignoreStatStage);
|
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, simulated, stat, ignoreStatStage);
|
||||||
}
|
}
|
||||||
if (move) {
|
if (move) {
|
||||||
applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, opponent, move, ignoreStatStage);
|
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, opponent, move, ignoreStatStage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3476,7 +3443,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @returns The calculated accuracy multiplier.
|
* @returns The calculated accuracy multiplier.
|
||||||
*/
|
*/
|
||||||
getAccuracyMultiplier(target: Pokemon, sourceMove: Move): number {
|
getAccuracyMultiplier(target: Pokemon, sourceMove: Move): number {
|
||||||
const isOhko = sourceMove.hasAttr(OneHitKOAccuracyAttr);
|
const isOhko = sourceMove.hasAttr("OneHitKOAccuracyAttr");
|
||||||
if (isOhko) {
|
if (isOhko) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -3489,7 +3456,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, target, null, false, Stat.ACC, ignoreAccStatStage);
|
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, target, null, false, Stat.ACC, ignoreAccStatStage);
|
||||||
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, this, null, false, Stat.EVA, ignoreEvaStatStage);
|
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, this, null, false, Stat.EVA, ignoreEvaStatStage);
|
||||||
applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, target, sourceMove, ignoreEvaStatStage);
|
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, target, sourceMove, ignoreEvaStatStage);
|
||||||
|
|
||||||
globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage);
|
globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage);
|
||||||
|
|
||||||
@ -3572,7 +3539,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
simulated,
|
simulated,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
applyMoveAttrs(VariableAtkAttr, source, this, move, sourceAtk);
|
applyMoveAttrs("VariableAtkAttr", source, this, move, sourceAtk);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Pokemon's defensive stat for the given move's category.
|
* This Pokemon's defensive stat for the given move's category.
|
||||||
@ -3590,7 +3557,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
simulated,
|
simulated,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
applyMoveAttrs(VariableDefAttr, source, this, move, targetDef);
|
applyMoveAttrs("VariableDefAttr", source, this, move, targetDef);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attack's base damage, as determined by the source's level, move power
|
* The attack's base damage, as determined by the source's level, move power
|
||||||
@ -3617,7 +3584,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
*/
|
*/
|
||||||
calculateStabMultiplier(source: Pokemon, move: Move, ignoreSourceAbility: boolean, simulated: boolean): number {
|
calculateStabMultiplier(source: Pokemon, move: Move, ignoreSourceAbility: boolean, simulated: boolean): number {
|
||||||
// If the move has the Typeless attribute, it doesn't get STAB (e.g. struggle)
|
// If the move has the Typeless attribute, it doesn't get STAB (e.g. struggle)
|
||||||
if (move.hasAttr(TypelessAttr)) {
|
if (move.hasAttr("TypelessAttr")) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
const sourceTypes = source.getTypes();
|
const sourceTypes = source.getTypes();
|
||||||
@ -3629,7 +3596,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
stabMultiplier.value += 0.5;
|
stabMultiplier.value += 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyMoveAttrs(CombinedPledgeStabBoostAttr, source, this, move, stabMultiplier);
|
applyMoveAttrs("CombinedPledgeStabBoostAttr", source, this, move, stabMultiplier);
|
||||||
|
|
||||||
if (!ignoreSourceAbility) {
|
if (!ignoreSourceAbility) {
|
||||||
applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier);
|
applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier);
|
||||||
@ -3678,7 +3645,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
|
|
||||||
const variableCategory = new NumberHolder(move.category);
|
const variableCategory = new NumberHolder(move.category);
|
||||||
applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, variableCategory);
|
applyMoveAttrs("VariableMoveCategoryAttr", source, this, move, variableCategory);
|
||||||
const moveCategory = variableCategory.value as MoveCategory;
|
const moveCategory = variableCategory.value as MoveCategory;
|
||||||
|
|
||||||
/** The move's type after type-changing effects are applied */
|
/** The move's type after type-changing effects are applied */
|
||||||
@ -3703,7 +3670,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const arenaAttackTypeMultiplier = new NumberHolder(
|
const arenaAttackTypeMultiplier = new NumberHolder(
|
||||||
globalScene.arena.getAttackTypeMultiplier(moveType, source.isGrounded()),
|
globalScene.arena.getAttackTypeMultiplier(moveType, source.isGrounded()),
|
||||||
);
|
);
|
||||||
applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier);
|
applyMoveAttrs("IgnoreWeatherTypeDebuffAttr", source, this, move, arenaAttackTypeMultiplier);
|
||||||
|
|
||||||
const isTypeImmune = typeMultiplier * arenaAttackTypeMultiplier.value === 0;
|
const isTypeImmune = typeMultiplier * arenaAttackTypeMultiplier.value === 0;
|
||||||
|
|
||||||
@ -3717,7 +3684,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
// If the attack deals fixed damage, return a result with that much damage
|
// If the attack deals fixed damage, return a result with that much damage
|
||||||
const fixedDamage = new NumberHolder(0);
|
const fixedDamage = new NumberHolder(0);
|
||||||
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
|
applyMoveAttrs("FixedDamageAttr", source, this, move, fixedDamage);
|
||||||
if (fixedDamage.value) {
|
if (fixedDamage.value) {
|
||||||
const multiLensMultiplier = new NumberHolder(1);
|
const multiLensMultiplier = new NumberHolder(1);
|
||||||
globalScene.applyModifiers(
|
globalScene.applyModifiers(
|
||||||
@ -3739,7 +3706,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
// If the attack is a one-hit KO move, return a result with damage equal to this Pokemon's HP
|
// If the attack is a one-hit KO move, return a result with damage equal to this Pokemon's HP
|
||||||
const isOneHitKo = new BooleanHolder(false);
|
const isOneHitKo = new BooleanHolder(false);
|
||||||
applyMoveAttrs(OneHitKOAttr, source, this, move, isOneHitKo);
|
applyMoveAttrs("OneHitKOAttr", source, this, move, isOneHitKo);
|
||||||
if (isOneHitKo.value) {
|
if (isOneHitKo.value) {
|
||||||
return {
|
return {
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
@ -3816,7 +3783,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
isPhysical &&
|
isPhysical &&
|
||||||
source.status &&
|
source.status &&
|
||||||
source.status.effect === StatusEffect.BURN &&
|
source.status.effect === StatusEffect.BURN &&
|
||||||
!move.hasAttr(BypassBurnDamageReductionAttr)
|
!move.hasAttr("BypassBurnDamageReductionAttr")
|
||||||
) {
|
) {
|
||||||
const burnDamageReductionCancelled = new BooleanHolder(false);
|
const burnDamageReductionCancelled = new BooleanHolder(false);
|
||||||
if (!ignoreSourceAbility) {
|
if (!ignoreSourceAbility) {
|
||||||
@ -3850,7 +3817,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
*/
|
*/
|
||||||
const hitsTagMultiplier = new NumberHolder(1);
|
const hitsTagMultiplier = new NumberHolder(1);
|
||||||
move
|
move
|
||||||
.getAttrs(HitsTagAttr)
|
.getAttrs("HitsTagAttr")
|
||||||
.filter(hta => hta.doubleDamage)
|
.filter(hta => hta.doubleDamage)
|
||||||
.forEach(hta => {
|
.forEach(hta => {
|
||||||
if (this.getTag(hta.tagType)) {
|
if (this.getTag(hta.tagType)) {
|
||||||
@ -3907,7 +3874,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
|
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
|
||||||
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
|
applyMoveAttrs("ModifiedDamageAttr", source, this, move, damage);
|
||||||
|
|
||||||
if (this.isFullHp() && !ignoreAbility) {
|
if (this.isFullHp() && !ignoreAbility) {
|
||||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
|
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
|
||||||
@ -3943,7 +3910,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
getCriticalHitResult(source: Pokemon, move: Move, simulated = true): boolean {
|
getCriticalHitResult(source: Pokemon, move: Move, simulated = true): boolean {
|
||||||
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
const noCritTag = globalScene.arena.getTagOnSide(NoCritTag, defendingSide);
|
const noCritTag = globalScene.arena.getTagOnSide(NoCritTag, defendingSide);
|
||||||
if (noCritTag || Overrides.NEVER_CRIT_OVERRIDE || move.hasAttr(FixedDamageAttr)) {
|
if (noCritTag || Overrides.NEVER_CRIT_OVERRIDE || move.hasAttr("FixedDamageAttr")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const isCritical = new BooleanHolder(false);
|
const isCritical = new BooleanHolder(false);
|
||||||
@ -3951,7 +3918,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (source.getTag(BattlerTagType.ALWAYS_CRIT)) {
|
if (source.getTag(BattlerTagType.ALWAYS_CRIT)) {
|
||||||
isCritical.value = true;
|
isCritical.value = true;
|
||||||
}
|
}
|
||||||
applyMoveAttrs(CritOnlyAttr, source, this, move, isCritical);
|
applyMoveAttrs("CritOnlyAttr", source, this, move, isCritical);
|
||||||
applyAbAttrs(ConditionalCritAbAttr, source, null, simulated, isCritical, this, move);
|
applyAbAttrs(ConditionalCritAbAttr, source, null, simulated, isCritical, this, move);
|
||||||
if (!isCritical.value) {
|
if (!isCritical.value) {
|
||||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
||||||
@ -6272,7 +6239,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
.targets.map(ind => fieldPokemon[ind])
|
.targets.map(ind => fieldPokemon[ind])
|
||||||
.filter(p => this.isPlayer() !== p.isPlayer());
|
.filter(p => this.isPlayer() !== p.isPlayer());
|
||||||
// Only considers critical hits for crit-only moves or when this Pokemon is under the effect of Laser Focus
|
// Only considers critical hits for crit-only moves or when this Pokemon is under the effect of Laser Focus
|
||||||
const isCritical = move.hasAttr(CritOnlyAttr) || !!this.getTag(BattlerTagType.ALWAYS_CRIT);
|
const isCritical = move.hasAttr("CritOnlyAttr") || !!this.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
move.category !== MoveCategory.STATUS &&
|
move.category !== MoveCategory.STATUS &&
|
||||||
@ -6341,7 +6308,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
![MoveId.SUCKER_PUNCH, MoveId.UPPER_HAND, MoveId.THUNDERCLAP].includes(move.id)
|
![MoveId.SUCKER_PUNCH, MoveId.UPPER_HAND, MoveId.THUNDERCLAP].includes(move.id)
|
||||||
) {
|
) {
|
||||||
targetScore = -20;
|
targetScore = -20;
|
||||||
} else if (move instanceof AttackMove) {
|
} else if (move.is("AttackMove")) {
|
||||||
/**
|
/**
|
||||||
* Attack moves are given extra multipliers to their base benefit score based on
|
* Attack moves are given extra multipliers to their base benefit score based on
|
||||||
* the move's type effectiveness against the target and whether the move is a STAB move.
|
* the move's type effectiveness against the target and whether the move is a STAB move.
|
||||||
@ -6462,7 +6429,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
if (!sortedBenefitScores.length) {
|
if (!sortedBenefitScores.length) {
|
||||||
// Set target to BattlerIndex.ATTACKER when using a counter move
|
// Set target to BattlerIndex.ATTACKER when using a counter move
|
||||||
// This is the same as when the player does so
|
// This is the same as when the player does so
|
||||||
if (move.hasAttr(CounterDamageAttr)) {
|
if (move.hasAttr("CounterDamageAttr")) {
|
||||||
return [BattlerIndex.ATTACKER];
|
return [BattlerIndex.ATTACKER];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6945,36 +6912,6 @@ export class PokemonTurnData {
|
|||||||
public berriesEaten: BerryType[] = [];
|
public berriesEaten: BerryType[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AiType {
|
|
||||||
RANDOM,
|
|
||||||
SMART_RANDOM,
|
|
||||||
SMART,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum MoveResult {
|
|
||||||
PENDING,
|
|
||||||
SUCCESS,
|
|
||||||
FAIL,
|
|
||||||
MISS,
|
|
||||||
OTHER,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum HitResult {
|
|
||||||
EFFECTIVE = 1,
|
|
||||||
SUPER_EFFECTIVE,
|
|
||||||
NOT_VERY_EFFECTIVE,
|
|
||||||
ONE_HIT_KO,
|
|
||||||
NO_EFFECT,
|
|
||||||
STATUS,
|
|
||||||
HEAL,
|
|
||||||
FAIL,
|
|
||||||
MISS,
|
|
||||||
INDIRECT,
|
|
||||||
IMMUNE,
|
|
||||||
CONFUSION,
|
|
||||||
INDIRECT_KO,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DamageResult =
|
export type DamageResult =
|
||||||
| HitResult.EFFECTIVE
|
| HitResult.EFFECTIVE
|
||||||
| HitResult.SUPER_EFFECTIVE
|
| HitResult.SUPER_EFFECTIVE
|
||||||
@ -6993,91 +6930,3 @@ export interface DamageCalculationResult {
|
|||||||
/** The damage dealt by the move */
|
/** The damage dealt by the move */
|
||||||
damage: number;
|
damage: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -13,19 +13,15 @@ import { TeraAIMode } from "#enums/tera-ai-mode";
|
|||||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import { randSeedWeightedItem, randSeedItem, randSeedInt } from "#app/utils/common";
|
import { randSeedWeightedItem, randSeedItem, randSeedInt } from "#app/utils/common";
|
||||||
import type { PersistentModifier } from "#app/modifier/modifier";
|
import type { PersistentModifier } from "#app/modifier/modifier";
|
||||||
import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag";
|
import { ArenaTrapTag } from "#app/data/arena-tag";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { getIsInitialized, initI18n } from "#app/plugins/i18n";
|
import { getIsInitialized, initI18n } from "#app/plugins/i18n";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import { signatureSpecies } from "#app/data/balance/signature-species";
|
import { signatureSpecies } from "#app/data/balance/signature-species";
|
||||||
|
import { TrainerVariant } from "#enums/trainer-variant";
|
||||||
export enum TrainerVariant {
|
|
||||||
DEFAULT,
|
|
||||||
FEMALE,
|
|
||||||
DOUBLE,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Trainer extends Phaser.GameObjects.Container {
|
export default class Trainer extends Phaser.GameObjects.Container {
|
||||||
public config: TrainerConfig;
|
public config: TrainerConfig;
|
||||||
|
@ -2,7 +2,8 @@ import i18next from "i18next";
|
|||||||
import type { FixedBattleConfigs } from "./battle";
|
import type { FixedBattleConfigs } from "./battle";
|
||||||
import { classicFixedBattles, FixedBattleConfig } from "./battle";
|
import { classicFixedBattles, FixedBattleConfig } from "./battle";
|
||||||
import type { Challenge } from "./data/challenge";
|
import type { Challenge } from "./data/challenge";
|
||||||
import { allChallenges, applyChallenges, ChallengeType, copyChallenge } from "./data/challenge";
|
import { allChallenges, applyChallenges, copyChallenge } from "./data/challenge";
|
||||||
|
import { ChallengeType } from "#enums/challenge-type";
|
||||||
import type PokemonSpecies from "./data/pokemon-species";
|
import type PokemonSpecies from "./data/pokemon-species";
|
||||||
import { allSpecies } from "./data/pokemon-species";
|
import { allSpecies } from "./data/pokemon-species";
|
||||||
import type { Arena } from "./field/arena";
|
import type { Arena } from "./field/arena";
|
||||||
@ -14,14 +15,7 @@ import { Challenges } from "./enums/challenges";
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { getDailyStartingBiome } from "./data/daily-run";
|
import { getDailyStartingBiome } from "./data/daily-run";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES, CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES } from "./constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES, CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES } from "./constants";
|
||||||
|
import { GameModes } from "#enums/game-modes";
|
||||||
export enum GameModes {
|
|
||||||
CLASSIC,
|
|
||||||
ENDLESS,
|
|
||||||
SPLICED_ENDLESS,
|
|
||||||
DAILY,
|
|
||||||
CHALLENGE,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GameModeConfig {
|
interface GameModeConfig {
|
||||||
isClassic?: boolean;
|
isClassic?: boolean;
|
||||||
|
@ -2,19 +2,16 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms";
|
import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms";
|
||||||
import { getBerryEffectDescription, getBerryName } from "#app/data/berry";
|
import { getBerryEffectDescription, getBerryName } from "#app/data/berry";
|
||||||
import { AttackMove } from "#app/data/moves/move";
|
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { getNatureName, getNatureStatMultiplier } from "#app/data/nature";
|
import { getNatureName, getNatureStatMultiplier } from "#app/data/nature";
|
||||||
import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
||||||
import {
|
import { pokemonFormChanges, SpeciesFormChangeCondition } from "#app/data/pokemon-forms";
|
||||||
FormChangeItem,
|
import { SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
pokemonFormChanges,
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
SpeciesFormChangeCondition,
|
|
||||||
SpeciesFormChangeItemTrigger,
|
|
||||||
} from "#app/data/pokemon-forms";
|
|
||||||
import { getStatusEffectDescriptor } from "#app/data/status-effect";
|
import { getStatusEffectDescriptor } from "#app/data/status-effect";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import type { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import {
|
import {
|
||||||
@ -1339,7 +1336,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
|||||||
p
|
p
|
||||||
.getMoveset()
|
.getMoveset()
|
||||||
.map(m => m.getMove())
|
.map(m => m.getMove())
|
||||||
.filter(m => m instanceof AttackMove)
|
.filter(m => m.is("AttackMove"))
|
||||||
.map(m => m.type),
|
.map(m => m.type),
|
||||||
);
|
);
|
||||||
if (!attackMoveTypes.length) {
|
if (!attackMoveTypes.length) {
|
||||||
|
@ -3,14 +3,15 @@ import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
|
|||||||
import { getLevelTotalExp } from "#app/data/exp";
|
import { getLevelTotalExp } from "#app/data/exp";
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
||||||
import { type FormChangeItem, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
|
import type { FormChangeItem } from "#enums/form-change-item";
|
||||||
import { getStatusEffectHealText } from "#app/data/status-effect";
|
import { getStatusEffectHealText } from "#app/data/status-effect";
|
||||||
import Pokemon, { type PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { type PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { LearnMoveType } from "#app/phases/learn-move-phase";
|
import { LearnMoveType } from "#enums/learn-move-type";
|
||||||
import type { VoucherType } from "#app/system/voucher";
|
import type { VoucherType } from "#app/system/voucher";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#enums/command";
|
||||||
import { addTextObject, TextStyle } from "#app/ui/text";
|
import { addTextObject, TextStyle } from "#app/ui/text";
|
||||||
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#app/utils/common";
|
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#app/utils/common";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { type PokeballCounts } from "#app/battle-scene";
|
import { type PokeballCounts } from "#app/battle-scene";
|
||||||
import { EvolutionItem } from "#app/data/balance/pokemon-evolutions";
|
import { EvolutionItem } from "#app/data/balance/pokemon-evolutions";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import { FormChangeItem } from "#app/data/pokemon-forms";
|
import { FormChangeItem } from "#enums/form-change-item";
|
||||||
import { type ModifierOverride } from "#app/modifier/modifier-type";
|
import { type ModifierOverride } from "#app/modifier/modifier-type";
|
||||||
import { Variant } from "#app/sprites/variant";
|
import { Variant } from "#app/sprites/variant";
|
||||||
import { Unlockables } from "#app/system/unlockables";
|
import { Unlockables } from "#app/system/unlockables";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||||
import { SubstituteTag } from "#app/data/battler-tags";
|
import { SubstituteTag } from "#app/data/battler-tags";
|
||||||
import {
|
import {
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
HealFromBerryUseAbAttr,
|
HealFromBerryUseAbAttr,
|
||||||
RepeatBerryNextTurnAbAttr,
|
RepeatBerryNextTurnAbAttr,
|
||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
import { CommonAnim } from "#app/data/battle-anims";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { BerryUsedEvent } from "#app/events/battle-scene";
|
import { BerryUsedEvent } from "#app/events/battle-scene";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { BerryModifier } from "#app/modifier/modifier";
|
import { BerryModifier } from "#app/modifier/modifier";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
|
||||||
export class CheckStatusEffectPhase extends Phase {
|
export class CheckStatusEffectPhase extends Phase {
|
||||||
|
@ -4,7 +4,7 @@ import { BattleType } from "#enums/battle-type";
|
|||||||
import type { EncoreTag } from "#app/data/battler-tags";
|
import type { EncoreTag } from "#app/data/battler-tags";
|
||||||
import { TrappedTag } from "#app/data/battler-tags";
|
import { TrappedTag } from "#app/data/battler-tags";
|
||||||
import type { MoveTargetSet } from "#app/data/moves/move";
|
import type { MoveTargetSet } from "#app/data/moves/move";
|
||||||
import { getMoveTargets } from "#app/data/moves/move";
|
import { getMoveTargets } from "#app/data/moves/move-utils";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
@ -12,15 +12,15 @@ import { BiomeId } from "#enums/biome-id";
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import type { PlayerPokemon, TurnMove } from "#app/field/pokemon";
|
import type { PlayerPokemon, TurnMove } from "#app/field/pokemon";
|
||||||
import { FieldPosition } from "#app/field/pokemon";
|
import { FieldPosition } from "#enums/field-position";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#enums/command";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { FieldPhase } from "./field-phase";
|
import { FieldPhase } from "./field-phase";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
import { ArenaTagSide } from "#app/data/arena-tag";
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||||
|
|
||||||
export class CommandPhase extends FieldPhase {
|
export class CommandPhase extends FieldPhase {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { CommonAnim } from "#app/data/battle-anims";
|
import type { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { CommonBattleAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim } from "#app/data/battle-anims";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattleSpec } from "#enums/battle-spec";
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
import { type DamageResult, HitResult } from "#app/field/pokemon";
|
import type { DamageResult } from "#app/field/pokemon";
|
||||||
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { fixedInt } from "#app/utils/common";
|
import { fixedInt } from "#app/utils/common";
|
||||||
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||||
@ -17,7 +17,7 @@ import { TrainerSlot } from "#enums/trainer-slot";
|
|||||||
import { getRandomWeatherType } from "#app/data/weather";
|
import { getRandomWeatherType } from "#app/data/weather";
|
||||||
import { EncounterPhaseEvent } from "#app/events/battle-scene";
|
import { EncounterPhaseEvent } from "#app/events/battle-scene";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { FieldPosition } from "#app/field/pokemon";
|
import { FieldPosition } from "#enums/field-position";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
|
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
|
||||||
import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#enums/command";
|
||||||
import { FieldPhase } from "./field-phase";
|
import { FieldPhase } from "./field-phase";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
@ -10,7 +10,7 @@ import { UiMode } from "#enums/ui-mode";
|
|||||||
import { cos, sin } from "#app/field/anims";
|
import { cos, sin } from "#app/field/anims";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { LearnMoveSituation } from "#app/field/pokemon";
|
import { LearnMoveSituation } from "#enums/learn-move-situation";
|
||||||
import { getTypeRgb } from "#app/data/type";
|
import { getTypeRgb } from "#app/data/type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattleType } from "#enums/battle-type";
|
import { BattleType } from "#enums/battle-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import {
|
import {
|
||||||
@ -9,16 +9,16 @@ import {
|
|||||||
PostKnockOutAbAttr,
|
PostKnockOutAbAttr,
|
||||||
PostVictoryAbAttr,
|
PostVictoryAbAttr,
|
||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
import { BattlerTagLapseType } from "#app/data/battler-tags";
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import { battleSpecDialogue } from "#app/data/dialogue";
|
import { battleSpecDialogue } from "#app/data/dialogue";
|
||||||
import { PostVictoryStatStageChangeAttr } from "#app/data/moves/move";
|
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { BattleSpec } from "#app/enums/battle-spec";
|
import { BattleSpec } from "#app/enums/battle-spec";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
import type { EnemyPokemon } from "#app/field/pokemon";
|
import type { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { HitResult, PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
|
import { HitResult } from "#enums/hit-result";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
import { PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
||||||
@ -144,7 +144,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
if (defeatSource?.isOnField()) {
|
if (defeatSource?.isOnField()) {
|
||||||
applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource);
|
applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource);
|
||||||
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
|
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
|
||||||
const pvattrs = pvmove.getAttrs(PostVictoryStatStageChangeAttr);
|
const pvattrs = pvmove.getAttrs("PostVictoryStatStageChangeAttr");
|
||||||
if (pvattrs.length) {
|
if (pvattrs.length) {
|
||||||
for (const pvattr of pvattrs) {
|
for (const pvattr of pvattrs) {
|
||||||
pvattr.applyPostVictory(defeatSource, defeatSource, pvmove);
|
pvattr.applyPostVictory(defeatSource, defeatSource, pvmove);
|
||||||
|
@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import { fixedInt } from "#app/utils/common";
|
import { fixedInt } from "#app/utils/common";
|
||||||
import { achvs } from "../system/achv";
|
import { achvs } from "../system/achv";
|
||||||
import type { SpeciesFormChange } from "../data/pokemon-forms";
|
import type { SpeciesFormChange } from "../data/pokemon-forms";
|
||||||
import { getSpeciesFormChangeMessage } from "../data/pokemon-forms";
|
import { getSpeciesFormChangeMessage } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import type { PlayerPokemon } from "../field/pokemon";
|
import type { PlayerPokemon } from "../field/pokemon";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import type PartyUiHandler from "../ui/party-ui-handler";
|
import type PartyUiHandler from "../ui/party-ui-handler";
|
||||||
|
@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
|
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
|
||||||
import type Move from "#app/data/moves/move";
|
import type Move from "#app/data/moves/move";
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
@ -12,15 +12,7 @@ import { UiMode } from "#enums/ui-mode";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase";
|
import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import { LearnMoveType } from "#enums/learn-move-type";
|
||||||
export enum LearnMoveType {
|
|
||||||
/** For learning a move via level-up, evolution, or other non-item-based event */
|
|
||||||
LEARN_MOVE,
|
|
||||||
/** For learning a move via Memory Mushroom */
|
|
||||||
MEMORY,
|
|
||||||
/** For learning a move via TM */
|
|
||||||
TM,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
|
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
|
||||||
public readonly phaseName = "LearnMovePhase";
|
public readonly phaseName = "LearnMovePhase";
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { MoveChargeAnim } from "#app/data/battle-anims";
|
import { MoveChargeAnim } from "#app/data/battle-anims";
|
||||||
import { applyMoveChargeAttrs, MoveEffectAttr, InstantChargeAttr } from "#app/data/moves/move";
|
import { applyMoveChargeAttrs } from "#app/data/moves/apply-attrs";
|
||||||
import type { PokemonMove } from "#app/field/pokemon";
|
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { MoveResult } from "#app/field/pokemon";
|
import { MoveResult } from "#enums/move-result";
|
||||||
import { BooleanHolder } from "#app/utils/common";
|
import { BooleanHolder } from "#app/utils/common";
|
||||||
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
@ -43,7 +43,7 @@ export class MoveChargePhase extends PokemonPhase {
|
|||||||
new MoveChargeAnim(move.chargeAnim, move.id, user).play(false, () => {
|
new MoveChargeAnim(move.chargeAnim, move.id, user).play(false, () => {
|
||||||
move.showChargeText(user, target);
|
move.showChargeText(user, target);
|
||||||
|
|
||||||
applyMoveChargeAttrs(MoveEffectAttr, user, target, move);
|
applyMoveChargeAttrs("MoveEffectAttr", user, target, move);
|
||||||
user.addTag(BattlerTagType.CHARGING, 1, move.id, user.id);
|
user.addTag(BattlerTagType.CHARGING, 1, move.id, user.id);
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
@ -57,7 +57,7 @@ export class MoveChargePhase extends PokemonPhase {
|
|||||||
if (move.isChargingMove()) {
|
if (move.isChargingMove()) {
|
||||||
const instantCharge = new BooleanHolder(false);
|
const instantCharge = new BooleanHolder(false);
|
||||||
|
|
||||||
applyMoveChargeAttrs(InstantChargeAttr, user, null, move, instantCharge);
|
applyMoveChargeAttrs("InstantChargeAttr", user, null, move, instantCharge);
|
||||||
|
|
||||||
if (instantCharge.value) {
|
if (instantCharge.value) {
|
||||||
// this MoveEndPhase will be duplicated by the queued MovePhase if not removed
|
// this MoveEndPhase will be duplicated by the queued MovePhase if not removed
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import {
|
import {
|
||||||
AddSecondStrikeAbAttr,
|
AddSecondStrikeAbAttr,
|
||||||
@ -16,43 +16,31 @@ import {
|
|||||||
PostDefendAbAttr,
|
PostDefendAbAttr,
|
||||||
ReflectStatusMoveAbAttr,
|
ReflectStatusMoveAbAttr,
|
||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag";
|
import { ConditionalProtectTag } from "#app/data/arena-tag";
|
||||||
|
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||||
import { MoveAnim } from "#app/data/battle-anims";
|
import { MoveAnim } from "#app/data/battle-anims";
|
||||||
import {
|
import {
|
||||||
BattlerTagLapseType,
|
|
||||||
DamageProtectedTag,
|
DamageProtectedTag,
|
||||||
ProtectedTag,
|
ProtectedTag,
|
||||||
SemiInvulnerableTag,
|
SemiInvulnerableTag,
|
||||||
SubstituteTag,
|
SubstituteTag,
|
||||||
TypeBoostTag,
|
TypeBoostTag,
|
||||||
} from "#app/data/battler-tags";
|
} from "#app/data/battler-tags";
|
||||||
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import type { MoveAttr } from "#app/data/moves/move";
|
import type { MoveAttr } from "#app/data/moves/move";
|
||||||
import {
|
import { getMoveTargets } from "#app/data/moves/move-utils";
|
||||||
applyFilteredMoveAttrs,
|
import { applyFilteredMoveAttrs, applyMoveAttrs } from "#app/data/moves/apply-attrs";
|
||||||
applyMoveAttrs,
|
|
||||||
AttackMove,
|
|
||||||
DelayedAttackAttr,
|
|
||||||
FlinchAttr,
|
|
||||||
getMoveTargets,
|
|
||||||
HitsTagAttr,
|
|
||||||
MissEffectAttr,
|
|
||||||
MoveEffectAttr,
|
|
||||||
MultiHitAttr,
|
|
||||||
NoEffectAttr,
|
|
||||||
OneHitKOAttr,
|
|
||||||
OverrideMoveEffectAttr,
|
|
||||||
StatChangeBeforeDmgCalcAttr,
|
|
||||||
ToxicAccuracyAttr,
|
|
||||||
} from "#app/data/moves/move";
|
|
||||||
import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
|
import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
|
||||||
import { MoveFlags } from "#enums/MoveFlags";
|
import { MoveFlags } from "#enums/MoveFlags";
|
||||||
import { MoveTarget } from "#enums/MoveTarget";
|
import { MoveTarget } from "#enums/MoveTarget";
|
||||||
import { MoveCategory } from "#enums/MoveCategory";
|
import { MoveCategory } from "#enums/MoveCategory";
|
||||||
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { type DamageResult, PokemonMove, type TurnMove } from "#app/field/pokemon";
|
import type { DamageResult, TurnMove } from "#app/field/pokemon";
|
||||||
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { HitResult, MoveResult } from "#app/field/pokemon";
|
import { MoveResult } from "#enums/move-result";
|
||||||
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import {
|
import {
|
||||||
ContactHeldItemTransferChanceModifier,
|
ContactHeldItemTransferChanceModifier,
|
||||||
@ -238,13 +226,13 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
case HitCheckResult.NO_EFFECT_NO_MESSAGE:
|
case HitCheckResult.NO_EFFECT_NO_MESSAGE:
|
||||||
case HitCheckResult.PROTECTED:
|
case HitCheckResult.PROTECTED:
|
||||||
case HitCheckResult.TARGET_NOT_ON_FIELD:
|
case HitCheckResult.TARGET_NOT_ON_FIELD:
|
||||||
applyMoveAttrs(NoEffectAttr, user, target, this.move);
|
applyMoveAttrs("NoEffectAttr", user, target, this.move);
|
||||||
break;
|
break;
|
||||||
case HitCheckResult.MISS:
|
case HitCheckResult.MISS:
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }),
|
i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }),
|
||||||
);
|
);
|
||||||
applyMoveAttrs(MissEffectAttr, user, target, this.move);
|
applyMoveAttrs("MissEffectAttr", user, target, this.move);
|
||||||
break;
|
break;
|
||||||
case HitCheckResult.REFLECTED:
|
case HitCheckResult.REFLECTED:
|
||||||
this.queueReflectedMove(user, target);
|
this.queueReflectedMove(user, target);
|
||||||
@ -274,7 +262,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
globalScene.currentBattle.lastPlayerInvolved = this.fieldIndex;
|
globalScene.currentBattle.lastPlayerInvolved = this.fieldIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDelayedAttack = this.move.hasAttr(DelayedAttackAttr);
|
const isDelayedAttack = this.move.hasAttr("DelayedAttackAttr");
|
||||||
/** If the user was somehow removed from the field and it's not a delayed attack, end this phase */
|
/** If the user was somehow removed from the field and it's not a delayed attack, end this phase */
|
||||||
if (!user.isOnField()) {
|
if (!user.isOnField()) {
|
||||||
if (!isDelayedAttack) {
|
if (!isDelayedAttack) {
|
||||||
@ -300,7 +288,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
const move = this.move;
|
const move = this.move;
|
||||||
|
|
||||||
// Assume single target for override
|
// Assume single target for override
|
||||||
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getFirstTarget() ?? null, move, overridden, this.virtual);
|
applyMoveAttrs("OverrideMoveEffectAttr", user, this.getFirstTarget() ?? null, move, overridden, this.virtual);
|
||||||
|
|
||||||
// If other effects were overriden, stop this phase before they can be applied
|
// If other effects were overriden, stop this phase before they can be applied
|
||||||
if (overridden.value) {
|
if (overridden.value) {
|
||||||
@ -327,7 +315,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
if (user.turnData.hitsLeft === -1) {
|
if (user.turnData.hitsLeft === -1) {
|
||||||
const hitCount = new NumberHolder(1);
|
const hitCount = new NumberHolder(1);
|
||||||
// Assume single target for multi hit
|
// Assume single target for multi hit
|
||||||
applyMoveAttrs(MultiHitAttr, user, this.getFirstTarget() ?? null, move, hitCount);
|
applyMoveAttrs("MultiHitAttr", user, this.getFirstTarget() ?? null, move, hitCount);
|
||||||
// If Parental Bond is applicable, add another hit
|
// If Parental Bond is applicable, add another hit
|
||||||
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, hitCount, null);
|
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, hitCount, null);
|
||||||
// If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses
|
// If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses
|
||||||
@ -359,7 +347,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
// Play the animation if the move was successful against any of its targets or it has a POST_TARGET effect (like self destruct)
|
// Play the animation if the move was successful against any of its targets or it has a POST_TARGET effect (like self destruct)
|
||||||
if (
|
if (
|
||||||
this.moveHistoryEntry.result === MoveResult.SUCCESS ||
|
this.moveHistoryEntry.result === MoveResult.SUCCESS ||
|
||||||
move.getAttrs(MoveEffectAttr).some(attr => attr.trigger === MoveEffectTrigger.POST_TARGET)
|
move.getAttrs("MoveEffectAttr").some(attr => attr.trigger === MoveEffectTrigger.POST_TARGET)
|
||||||
) {
|
) {
|
||||||
const firstTarget = this.getFirstTarget();
|
const firstTarget = this.getFirstTarget();
|
||||||
new MoveAnim(
|
new MoveAnim(
|
||||||
@ -458,7 +446,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @returns a function intended to be passed into a `then()` call.
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
protected applyHeldItemFlinchCheck(user: Pokemon, target: Pokemon, dealsDamage: boolean): void {
|
protected applyHeldItemFlinchCheck(user: Pokemon, target: Pokemon, dealsDamage: boolean): void {
|
||||||
if (this.move.hasAttr(FlinchAttr)) {
|
if (this.move.hasAttr("FlinchAttr")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +588,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
const bypassAccuracy =
|
const bypassAccuracy =
|
||||||
bypassAccAndInvuln ||
|
bypassAccAndInvuln ||
|
||||||
target.getTag(BattlerTagType.ALWAYS_GET_HIT) ||
|
target.getTag(BattlerTagType.ALWAYS_GET_HIT) ||
|
||||||
(target.getTag(BattlerTagType.TELEKINESIS) && !this.move.hasAttr(OneHitKOAttr));
|
(target.getTag(BattlerTagType.TELEKINESIS) && !this.move.hasAttr("OneHitKOAttr"));
|
||||||
|
|
||||||
if (moveAccuracy === -1 || bypassAccuracy) {
|
if (moveAccuracy === -1 || bypassAccuracy) {
|
||||||
return [HitCheckResult.HIT, effectiveness];
|
return [HitCheckResult.HIT, effectiveness];
|
||||||
@ -641,7 +629,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) {
|
if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.move.hasAttr(ToxicAccuracyAttr) && user.isOfType(PokemonType.POISON)) {
|
if (this.move.hasAttr("ToxicAccuracyAttr") && user.isOfType(PokemonType.POISON)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// TODO: Fix lock on / mind reader check.
|
// TODO: Fix lock on / mind reader check.
|
||||||
@ -666,7 +654,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const move = this.move;
|
const move = this.move;
|
||||||
return move.getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType);
|
return move.getAttrs("HitsTagAttr").some(hta => hta.tagType === semiInvulnerableTag.tagType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns The {@linkcode Pokemon} using this phase's invoked move */
|
/** @returns The {@linkcode Pokemon} using this phase's invoked move */
|
||||||
@ -757,7 +745,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
): void {
|
): void {
|
||||||
applyFilteredMoveAttrs(
|
applyFilteredMoveAttrs(
|
||||||
(attr: MoveAttr) =>
|
(attr: MoveAttr) =>
|
||||||
attr instanceof MoveEffectAttr &&
|
attr.is("MoveEffectAttr") &&
|
||||||
attr.trigger === triggerType &&
|
attr.trigger === triggerType &&
|
||||||
(isNullOrUndefined(selfTarget) || attr.selfTarget === selfTarget) &&
|
(isNullOrUndefined(selfTarget) || attr.selfTarget === selfTarget) &&
|
||||||
(!attr.firstHitOnly || this.firstHit) &&
|
(!attr.firstHitOnly || this.firstHit) &&
|
||||||
@ -820,7 +808,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* Apply stat changes from {@linkcode move} and gives it to {@linkcode source}
|
* Apply stat changes from {@linkcode move} and gives it to {@linkcode source}
|
||||||
* before damage calculation
|
* before damage calculation
|
||||||
*/
|
*/
|
||||||
applyMoveAttrs(StatChangeBeforeDmgCalcAttr, user, target, this.move);
|
applyMoveAttrs("StatChangeBeforeDmgCalcAttr", user, target, this.move);
|
||||||
|
|
||||||
const { result, damage: dmg } = target.getAttackDamage({
|
const { result, damage: dmg } = target.getAttackDamage({
|
||||||
source: user,
|
source: user,
|
||||||
@ -998,12 +986,12 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move, hitResult);
|
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move, hitResult);
|
||||||
|
|
||||||
// We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens
|
// We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens
|
||||||
if (!user.isPlayer() && this.move instanceof AttackMove) {
|
if (!user.isPlayer() && this.move.is("AttackMove")) {
|
||||||
globalScene.applyShuffledModifiers(EnemyAttackStatusEffectChanceModifier, false, target);
|
globalScene.applyShuffledModifiers(EnemyAttackStatusEffectChanceModifier, false, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply Grip Claw's chance to steal an item from the target
|
// Apply Grip Claw's chance to steal an item from the target
|
||||||
if (this.move instanceof AttackMove) {
|
if (this.move.is("AttackMove")) {
|
||||||
globalScene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
globalScene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { BattlerTagLapseType } from "#app/data/battler-tags";
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/abilities/ability";
|
import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/abilities/ability";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { applyMoveAttrs, MoveHeaderAttr } from "#app/data/moves/move";
|
import { applyMoveAttrs } from "#app/data/moves/apply-attrs";
|
||||||
import type { PokemonMove } from "#app/field/pokemon";
|
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export class MoveHeaderPhase extends BattlePhase {
|
|||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
if (this.canMove()) {
|
if (this.canMove()) {
|
||||||
applyMoveAttrs(MoveHeaderAttr, this.pokemon, null, this.move.getMove());
|
applyMoveAttrs("MoveHeaderAttr", this.pokemon, null, this.move.getMove());
|
||||||
}
|
}
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import {
|
import {
|
||||||
applyAbAttrs,
|
applyAbAttrs,
|
||||||
@ -12,30 +12,21 @@ import {
|
|||||||
ReduceStatusEffectDurationAbAttr,
|
ReduceStatusEffectDurationAbAttr,
|
||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
import type { DelayedAttackTag } from "#app/data/arena-tag";
|
import type { DelayedAttackTag } from "#app/data/arena-tag";
|
||||||
import { CommonAnim } from "#app/data/battle-anims";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags";
|
import { CenterOfAttentionTag } from "#app/data/battler-tags";
|
||||||
import {
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
AddArenaTrapTagAttr,
|
import type { HealStatusEffectAttr } from "#app/data/moves/move";
|
||||||
applyMoveAttrs,
|
import { applyMoveAttrs } from "#app/data/moves/apply-attrs";
|
||||||
BypassRedirectAttr,
|
|
||||||
BypassSleepAttr,
|
|
||||||
CopyMoveAttr,
|
|
||||||
DelayedAttackAttr,
|
|
||||||
frenzyMissFunc,
|
|
||||||
HealStatusEffectAttr,
|
|
||||||
PreMoveMessageAttr,
|
|
||||||
PreUseInterruptAttr,
|
|
||||||
} from "#app/data/moves/move";
|
|
||||||
import { allMoves } from "#app/data/data-lists";
|
import { allMoves } from "#app/data/data-lists";
|
||||||
import { MoveFlags } from "#enums/MoveFlags";
|
import { MoveFlags } from "#enums/MoveFlags";
|
||||||
import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect";
|
import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { getTerrainBlockMessage, getWeatherBlockMessage } from "#app/data/weather";
|
import { getTerrainBlockMessage, getWeatherBlockMessage } from "#app/data/weather";
|
||||||
import { MoveUsedEvent } from "#app/events/battle-scene";
|
import { MoveUsedEvent } from "#app/events/battle-scene";
|
||||||
import type { PokemonMove } from "#app/field/pokemon";
|
import type { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { MoveResult } from "#app/field/pokemon";
|
import { MoveResult } from "#enums/move-result";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { BattlePhase } from "#app/phases/battle-phase";
|
import { BattlePhase } from "#app/phases/battle-phase";
|
||||||
@ -46,6 +37,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import { frenzyMissFunc } from "#app/data/moves/move-utils";
|
||||||
|
|
||||||
export class MovePhase extends BattlePhase {
|
export class MovePhase extends BattlePhase {
|
||||||
public readonly phaseName = "MovePhase";
|
public readonly phaseName = "MovePhase";
|
||||||
@ -204,7 +196,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
const moveQueue = this.pokemon.getMoveQueue();
|
const moveQueue = this.pokemon.getMoveQueue();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(targets.length === 0 && !this.move.getMove().hasAttr(AddArenaTrapTagAttr)) ||
|
(targets.length === 0 && !this.move.getMove().hasAttr("AddArenaTrapTagAttr")) ||
|
||||||
(moveQueue.length && moveQueue[0].move === MoveId.NONE)
|
(moveQueue.length && moveQueue[0].move === MoveId.NONE)
|
||||||
) {
|
) {
|
||||||
this.showMoveText();
|
this.showMoveText();
|
||||||
@ -233,7 +225,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
Overrides.STATUS_ACTIVATION_OVERRIDE !== false;
|
Overrides.STATUS_ACTIVATION_OVERRIDE !== false;
|
||||||
break;
|
break;
|
||||||
case StatusEffect.SLEEP: {
|
case StatusEffect.SLEEP: {
|
||||||
applyMoveAttrs(BypassSleepAttr, this.pokemon, null, this.move.getMove());
|
applyMoveAttrs("BypassSleepAttr", this.pokemon, null, this.move.getMove());
|
||||||
const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0);
|
const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0);
|
||||||
applyAbAttrs(
|
applyAbAttrs(
|
||||||
ReduceStatusEffectDurationAbAttr,
|
ReduceStatusEffectDurationAbAttr,
|
||||||
@ -253,7 +245,10 @@ export class MovePhase extends BattlePhase {
|
|||||||
!!this.move
|
!!this.move
|
||||||
.getMove()
|
.getMove()
|
||||||
.findAttr(
|
.findAttr(
|
||||||
attr => attr instanceof HealStatusEffectAttr && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE),
|
attr =>
|
||||||
|
attr.is("HealStatusEffectAttr") &&
|
||||||
|
attr.selfTarget &&
|
||||||
|
(attr as unknown as HealStatusEffectAttr).isOfEffect(StatusEffect.FREEZE),
|
||||||
) ||
|
) ||
|
||||||
(!this.pokemon.randBattleSeedInt(5) && Overrides.STATUS_ACTIVATION_OVERRIDE !== true) ||
|
(!this.pokemon.randBattleSeedInt(5) && Overrides.STATUS_ACTIVATION_OVERRIDE !== true) ||
|
||||||
Overrides.STATUS_ACTIVATION_OVERRIDE === false;
|
Overrides.STATUS_ACTIVATION_OVERRIDE === false;
|
||||||
@ -303,7 +298,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
// form changes happen even before we know that the move wll execute.
|
// form changes happen even before we know that the move wll execute.
|
||||||
globalScene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger);
|
globalScene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger);
|
||||||
|
|
||||||
const isDelayedAttack = this.move.getMove().hasAttr(DelayedAttackAttr);
|
const isDelayedAttack = this.move.getMove().hasAttr("DelayedAttackAttr");
|
||||||
if (isDelayedAttack) {
|
if (isDelayedAttack) {
|
||||||
// Check the player side arena if future sight is active
|
// Check the player side arena if future sight is active
|
||||||
const futureSightTags = globalScene.arena.findTags(t => t.tagType === ArenaTagType.FUTURE_SIGHT);
|
const futureSightTags = globalScene.arena.findTags(t => t.tagType === ArenaTagType.FUTURE_SIGHT);
|
||||||
@ -331,7 +326,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
|
|
||||||
let success = true;
|
let success = true;
|
||||||
// Check if there are any attributes that can interrupt the move, overriding the fail message.
|
// Check if there are any attributes that can interrupt the move, overriding the fail message.
|
||||||
for (const move of this.move.getMove().getAttrs(PreUseInterruptAttr)) {
|
for (const move of this.move.getMove().getAttrs("PreUseInterruptAttr")) {
|
||||||
if (move.apply(this.pokemon, targets[0], this.move.getMove())) {
|
if (move.apply(this.pokemon, targets[0], this.move.getMove())) {
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
@ -386,7 +381,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the battle's "last move" pointer, unless we're currently mimicking a move.
|
// Update the battle's "last move" pointer, unless we're currently mimicking a move.
|
||||||
if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) {
|
if (!allMoves[this.move.moveId].hasAttr("CopyMoveAttr")) {
|
||||||
// The last move used is unaffected by moves that fail
|
// The last move used is unaffected by moves that fail
|
||||||
if (success) {
|
if (success) {
|
||||||
globalScene.currentBattle.lastMove = this.move.moveId;
|
globalScene.currentBattle.lastMove = this.move.moveId;
|
||||||
@ -543,7 +538,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (currentTarget !== redirectTarget.value) {
|
if (currentTarget !== redirectTarget.value) {
|
||||||
const bypassRedirectAttrs = this.move.getMove().getAttrs(BypassRedirectAttr);
|
const bypassRedirectAttrs = this.move.getMove().getAttrs("BypassRedirectAttr");
|
||||||
bypassRedirectAttrs.forEach(attr => {
|
bypassRedirectAttrs.forEach(attr => {
|
||||||
if (!attr.abilitiesOnly || redirectedByAbility) {
|
if (!attr.abilitiesOnly || redirectedByAbility) {
|
||||||
redirectTarget.value = currentTarget;
|
redirectTarget.value = currentTarget;
|
||||||
@ -664,7 +659,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
}),
|
}),
|
||||||
500,
|
500,
|
||||||
);
|
);
|
||||||
applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents(false)[0], this.move.getMove());
|
applyMoveAttrs("PreMoveMessageAttr", this.pokemon, this.pokemon.getOpponents(false)[0], this.move.getMove());
|
||||||
}
|
}
|
||||||
|
|
||||||
public showFailedText(failedText: string = i18next.t("battle:attackFailed")): void {
|
public showFailedText(failedText: string = i18next.t("battle:attackFailed")): void {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BattlerTagLapseType } from "#app/data/battler-tags";
|
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||||
import type { OptionPhaseCallback } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import type { OptionPhaseCallback } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { SeenEncounterData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
import { SeenEncounterData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim } from "#app/data/battle-anims";
|
||||||
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect";
|
import { getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/abilities/ability";
|
import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/abilities/ability";
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { CommonAnim } from "#app/data/battle-anims";
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { getStatusEffectHealText } from "#app/data/status-effect";
|
import { getStatusEffectHealText } from "#app/data/status-effect";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
import { HitResult } from "#app/field/pokemon";
|
import { HitResult } from "#enums/hit-result";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { HealingBoosterModifier } from "#app/modifier/modifier";
|
import { HealingBoosterModifier } from "#app/modifier/modifier";
|
||||||
import { HealAchv } from "#app/system/achv";
|
import { HealAchv } from "#app/system/achv";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#enums/battler-index";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { FieldPhase } from "./field-phase";
|
import { FieldPhase } from "./field-phase";
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { EFFECTIVE_STATS, BATTLE_STATS } from "#enums/stat";
|
import { EFFECTIVE_STATS, BATTLE_STATS } from "#enums/stat";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import {
|
import {
|
||||||
applyAbAttrs,
|
applyAbAttrs,
|
||||||
applyPostDamageAbAttrs,
|
applyPostDamageAbAttrs,
|
||||||
@ -8,7 +8,8 @@ import {
|
|||||||
PostDamageAbAttr,
|
PostDamageAbAttr,
|
||||||
ReduceBurnDamageAbAttr,
|
ReduceBurnDamageAbAttr,
|
||||||
} from "#app/data/abilities/ability";
|
} from "#app/data/abilities/ability";
|
||||||
import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim } from "#app/data/battle-anims";
|
||||||
|
import { CommonAnim } from "#enums/move-anims-common";
|
||||||
import { getStatusEffectActivationText } from "#app/data/status-effect";
|
import { getStatusEffectActivationText } from "#app/data/status-effect";
|
||||||
import { BattleSpec } from "#app/enums/battle-spec";
|
import { BattleSpec } from "#app/enums/battle-spec";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { SemiInvulnerableTag } from "#app/data/battler-tags";
|
import { SemiInvulnerableTag } from "#app/data/battler-tags";
|
||||||
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
import type { SpeciesFormChange } from "#app/data/pokemon-forms";
|
||||||
import { getSpeciesFormChangeMessage, SpeciesFormChangeTeraTrigger } from "#app/data/pokemon-forms";
|
import {
|
||||||
|
getSpeciesFormChangeMessage,
|
||||||
|
SpeciesFormChangeTeraTrigger,
|
||||||
|
} from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { getTypeRgb } from "#app/data/type";
|
import { getTypeRgb } from "#app/data/type";
|
||||||
import { BattleSpec } from "#app/enums/battle-spec";
|
import { BattleSpec } from "#app/enums/battle-spec";
|
||||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { SwitchType } from "#enums/switch-type";
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import { SwitchSummonPhase } from "./switch-summon-phase";
|
import { SwitchSummonPhase } from "./switch-summon-phase";
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { PERMANENT_STATS, Stat } from "#app/enums/stat";
|
import { PERMANENT_STATS, Stat } from "#app/enums/stat";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { getTextColor, TextStyle } from "#app/ui/text";
|
import { getTextColor, TextStyle } from "#app/ui/text";
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { applyChallenges, ChallengeType } from "#app/data/challenge";
|
import { applyChallenges } from "#app/data/challenge";
|
||||||
|
import { ChallengeType } from "#enums/challenge-type";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier";
|
import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#enums/command";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user